Serenity Operating System
at master 145 lines 5.0 kB view raw
1/* 2 * Copyright (c) 2022, MacDue <macdue@dueutil.tech> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7#define AK_DONT_REPLACE_STD 8 9#include <LibWebView/AccessibilityTreeModel.h> 10#include <LibWebView/DOMTreeModel.h> 11#include <LibWebView/StylePropertiesModel.h> 12 13#include "InspectorWidget.h" 14#include <QCloseEvent> 15#include <QHeaderView> 16#include <QSplitter> 17#include <QStringList> 18#include <QTabWidget> 19#include <QTableView> 20#include <QTreeView> 21#include <QVBoxLayout> 22 23namespace Ladybird { 24 25InspectorWidget::InspectorWidget() 26{ 27 setLayout(new QVBoxLayout); 28 auto splitter = new QSplitter(this); 29 layout()->addWidget(splitter); 30 splitter->setOrientation(Qt::Vertical); 31 32 auto add_tab = [&](auto* tab_widget, auto* widget, auto name) { 33 auto container = new QWidget; 34 container->setLayout(new QVBoxLayout); 35 container->layout()->addWidget(widget); 36 tab_widget->addTab(container, name); 37 }; 38 39 auto top_tap_widget = new QTabWidget; 40 splitter->addWidget(top_tap_widget); 41 42 auto dom_tree_view = new QTreeView; 43 dom_tree_view->setHeaderHidden(true); 44 dom_tree_view->setModel(&m_dom_model); 45 QObject::connect(dom_tree_view->selectionModel(), &QItemSelectionModel::selectionChanged, 46 [this](QItemSelection const& selected, QItemSelection const&) { 47 auto indexes = selected.indexes(); 48 if (indexes.size()) { 49 auto index = m_dom_model.to_gui(indexes.first()); 50 set_selection(index); 51 } 52 }); 53 add_tab(top_tap_widget, dom_tree_view, "DOM"); 54 55 auto accessibility_tree_view = new QTreeView; 56 accessibility_tree_view->setHeaderHidden(true); 57 accessibility_tree_view->setModel(&m_accessibility_model); 58 add_tab(top_tap_widget, accessibility_tree_view, "Accessibility"); 59 60 auto add_table_tab = [&](auto* tab_widget, auto& model, auto name) { 61 auto table_view = new QTableView; 62 table_view->setModel(&model); 63 table_view->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); 64 table_view->verticalHeader()->setVisible(false); 65 table_view->horizontalHeader()->setVisible(false); 66 add_tab(tab_widget, table_view, name); 67 }; 68 69 auto node_tabs = new QTabWidget; 70 add_table_tab(node_tabs, m_computed_style_model, "Computed"); 71 add_table_tab(node_tabs, m_resolved_style_model, "Resolved"); 72 add_table_tab(node_tabs, m_custom_properties_model, "Variables"); 73 splitter->addWidget(node_tabs); 74} 75 76void InspectorWidget::set_dom_json(StringView dom_json) 77{ 78 m_dom_model.set_underlying_model(WebView::DOMTreeModel::create(dom_json)); 79} 80 81void InspectorWidget::set_accessibility_json(StringView accessibility_json) 82{ 83 m_accessibility_model.set_underlying_model(WebView::AccessibilityTreeModel::create(accessibility_json)); 84} 85 86void InspectorWidget::clear_dom_json() 87{ 88 m_dom_model.set_underlying_model(nullptr); 89 // The accessibility tree is pretty much another form of the DOM tree, so should be cleared at the time time. 90 m_accessibility_model.set_underlying_model(nullptr); 91 clear_style_json(); 92} 93 94void InspectorWidget::load_style_json(StringView computed_style_json, StringView resolved_style_json, StringView custom_properties_json) 95{ 96 m_computed_style_model.set_underlying_model(WebView::StylePropertiesModel::create(computed_style_json)); 97 m_resolved_style_model.set_underlying_model(WebView::StylePropertiesModel::create(resolved_style_json)); 98 m_custom_properties_model.set_underlying_model(WebView::StylePropertiesModel::create(custom_properties_json)); 99} 100 101void InspectorWidget::clear_style_json() 102{ 103 m_computed_style_model.set_underlying_model(nullptr); 104 m_resolved_style_model.set_underlying_model(nullptr); 105 m_custom_properties_model.set_underlying_model(nullptr); 106} 107 108void InspectorWidget::closeEvent(QCloseEvent* event) 109{ 110 event->accept(); 111 if (on_close) 112 on_close(); 113} 114 115void InspectorWidget::set_selection(GUI::ModelIndex index) 116{ 117 if (!index.is_valid()) 118 return; 119 120 auto* json = static_cast<JsonObject const*>(index.internal_data()); 121 VERIFY(json); 122 123 Selection selection {}; 124 if (json->has_u32("pseudo-element"sv)) { 125 selection.dom_node_id = json->get_i32("parent-id"sv).value(); 126 selection.pseudo_element = static_cast<Web::CSS::Selector::PseudoElement>(json->get_u32("pseudo-element"sv).value()); 127 } else { 128 selection.dom_node_id = json->get_i32("id"sv).value(); 129 } 130 131 if (selection == m_selection) 132 return; 133 m_selection = selection; 134 135 VERIFY(on_dom_node_inspected); 136 auto maybe_inspected_node_properties = on_dom_node_inspected(m_selection.dom_node_id, m_selection.pseudo_element); 137 if (!maybe_inspected_node_properties.is_error()) { 138 auto properties = maybe_inspected_node_properties.release_value(); 139 load_style_json(properties.computed_style_json, properties.resolved_style_json, properties.custom_properties_json); 140 } else { 141 clear_style_json(); 142 } 143} 144 145}