InspectorWidget.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. /*
  2. * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021-2022, Sam Atkins <atkinssj@serenityos.org>
  4. * Copyright (c) 2022, the SerenityOS developers.
  5. *
  6. * SPDX-License-Identifier: BSD-2-Clause
  7. */
  8. #include "InspectorWidget.h"
  9. #include "ElementSizePreviewWidget.h"
  10. #include <LibGUI/BoxLayout.h>
  11. #include <LibGUI/Splitter.h>
  12. #include <LibGUI/TabWidget.h>
  13. #include <LibGUI/TableView.h>
  14. #include <LibGUI/TreeView.h>
  15. #include <LibWeb/DOM/Document.h>
  16. #include <LibWeb/DOM/Element.h>
  17. #include <LibWeb/DOMTreeModel.h>
  18. #include <LibWeb/StylePropertiesModel.h>
  19. #include <LibWebView/OutOfProcessWebView.h>
  20. namespace Browser {
  21. void InspectorWidget::set_selection(Selection selection)
  22. {
  23. if (!m_dom_json.has_value()) {
  24. // DOM Tree hasn't been loaded yet, so make a note to inspect it later.
  25. m_pending_selection = move(selection);
  26. return;
  27. }
  28. auto* model = verify_cast<Web::DOMTreeModel>(m_dom_tree_view->model());
  29. auto index = model->index_for_node(selection.dom_node_id, selection.pseudo_element);
  30. if (!index.is_valid()) {
  31. dbgln("InspectorWidget told to inspect non-existent node: {}", selection.to_string());
  32. return;
  33. }
  34. m_dom_tree_view->expand_all_parents_of(index);
  35. m_dom_tree_view->set_cursor(index, GUI::AbstractView::SelectionUpdate::Set);
  36. set_selection(index);
  37. }
  38. void InspectorWidget::set_selection(GUI::ModelIndex const index)
  39. {
  40. if (!index.is_valid())
  41. return;
  42. auto* json = static_cast<JsonObject const*>(index.internal_data());
  43. VERIFY(json);
  44. Selection selection {};
  45. if (json->has_u32("pseudo-element")) {
  46. selection.dom_node_id = json->get("parent-id").to_i32();
  47. selection.pseudo_element = static_cast<Web::CSS::Selector::PseudoElement>(json->get("pseudo-element").to_u32());
  48. } else {
  49. selection.dom_node_id = json->get("id").to_i32();
  50. }
  51. if (selection == m_selection)
  52. return;
  53. m_selection = move(selection);
  54. auto maybe_inspected_node_properties = m_web_view->inspect_dom_node(m_selection.dom_node_id, m_selection.pseudo_element);
  55. if (maybe_inspected_node_properties.has_value()) {
  56. auto inspected_node_properties = maybe_inspected_node_properties.value();
  57. load_style_json(inspected_node_properties.specified_values_json, inspected_node_properties.computed_values_json, inspected_node_properties.custom_properties_json);
  58. update_node_box_model(inspected_node_properties.node_box_sizing_json);
  59. } else {
  60. clear_style_json();
  61. }
  62. }
  63. InspectorWidget::InspectorWidget()
  64. {
  65. set_fill_with_background_color(true);
  66. auto& layout = set_layout<GUI::VerticalBoxLayout>();
  67. layout.set_margins({ 4, 4, 4, 4 });
  68. auto& splitter = add<GUI::VerticalSplitter>();
  69. auto& top_tab_widget = splitter.add<GUI::TabWidget>();
  70. auto& dom_tree_container = top_tab_widget.add_tab<GUI::Widget>("DOM");
  71. dom_tree_container.set_layout<GUI::VerticalBoxLayout>();
  72. dom_tree_container.layout()->set_margins({ 4, 4, 4, 4 });
  73. m_dom_tree_view = dom_tree_container.add<GUI::TreeView>();
  74. m_dom_tree_view->on_selection_change = [this] {
  75. const auto& index = m_dom_tree_view->selection().first();
  76. set_selection(index);
  77. };
  78. auto& bottom_tab_widget = splitter.add<GUI::TabWidget>();
  79. auto& computed_style_table_container = bottom_tab_widget.add_tab<GUI::Widget>("Computed");
  80. computed_style_table_container.set_layout<GUI::VerticalBoxLayout>();
  81. computed_style_table_container.layout()->set_margins({ 4, 4, 4, 4 });
  82. m_computed_style_table_view = computed_style_table_container.add<GUI::TableView>();
  83. auto& resolved_style_table_container = bottom_tab_widget.add_tab<GUI::Widget>("Resolved");
  84. resolved_style_table_container.set_layout<GUI::VerticalBoxLayout>();
  85. resolved_style_table_container.layout()->set_margins({ 4, 4, 4, 4 });
  86. m_resolved_style_table_view = resolved_style_table_container.add<GUI::TableView>();
  87. auto& custom_properties_table_container = bottom_tab_widget.add_tab<GUI::Widget>("Variables");
  88. custom_properties_table_container.set_layout<GUI::VerticalBoxLayout>();
  89. custom_properties_table_container.layout()->set_margins({ 4, 4, 4, 4 });
  90. m_custom_properties_table_view = custom_properties_table_container.add<GUI::TableView>();
  91. auto& box_model_widget = bottom_tab_widget.add_tab<GUI::Widget>("Box Model");
  92. box_model_widget.set_layout<GUI::VerticalBoxLayout>();
  93. box_model_widget.layout()->set_margins({ 4, 4, 4, 4 });
  94. m_element_size_view = box_model_widget.add<ElementSizePreviewWidget>();
  95. m_element_size_view->set_should_hide_unnecessary_scrollbars(true);
  96. m_dom_tree_view->set_focus(true);
  97. }
  98. void InspectorWidget::select_default_node()
  99. {
  100. clear_style_json();
  101. // FIXME: Select the <body> element, or else the root node.
  102. m_dom_tree_view->collapse_tree();
  103. m_dom_tree_view->set_cursor({}, GUI::AbstractView::SelectionUpdate::ClearIfNotSelected);
  104. }
  105. void InspectorWidget::set_dom_json(String json)
  106. {
  107. if (m_dom_json.has_value() && m_dom_json.value() == json)
  108. return;
  109. m_dom_json = json;
  110. m_dom_tree_view->set_model(Web::DOMTreeModel::create(m_dom_json->view(), *m_dom_tree_view));
  111. if (m_pending_selection.has_value()) {
  112. set_selection(m_pending_selection.release_value());
  113. } else {
  114. select_default_node();
  115. }
  116. }
  117. void InspectorWidget::clear_dom_json()
  118. {
  119. m_dom_json.clear();
  120. m_dom_tree_view->set_model(nullptr);
  121. clear_style_json();
  122. }
  123. void InspectorWidget::set_dom_node_properties_json(Selection selection, String specified_values_json, String computed_values_json, String custom_properties_json, String node_box_sizing_json)
  124. {
  125. if (selection != m_selection) {
  126. dbgln("Got data for the wrong node id! Wanted ({}), got ({})", m_selection.to_string(), selection.to_string());
  127. return;
  128. }
  129. load_style_json(specified_values_json, computed_values_json, custom_properties_json);
  130. update_node_box_model(node_box_sizing_json);
  131. }
  132. void InspectorWidget::load_style_json(String specified_values_json, String computed_values_json, String custom_properties_json)
  133. {
  134. m_selection_specified_values_json = specified_values_json;
  135. m_computed_style_table_view->set_model(Web::StylePropertiesModel::create(m_selection_specified_values_json.value().view()));
  136. m_computed_style_table_view->set_searchable(true);
  137. m_selection_computed_values_json = computed_values_json;
  138. m_resolved_style_table_view->set_model(Web::StylePropertiesModel::create(m_selection_computed_values_json.value().view()));
  139. m_resolved_style_table_view->set_searchable(true);
  140. m_selection_custom_properties_json = custom_properties_json;
  141. m_custom_properties_table_view->set_model(Web::StylePropertiesModel::create(m_selection_custom_properties_json.value().view()));
  142. m_custom_properties_table_view->set_searchable(true);
  143. }
  144. void InspectorWidget::update_node_box_model(Optional<String> node_box_sizing_json)
  145. {
  146. if (!node_box_sizing_json.has_value()) {
  147. return;
  148. }
  149. auto json_or_error = JsonValue::from_string(node_box_sizing_json.value());
  150. if (json_or_error.is_error() || !json_or_error.value().is_object()) {
  151. return;
  152. }
  153. auto json_value = json_or_error.release_value();
  154. auto const& json_object = json_value.as_object();
  155. m_node_box_sizing.margin.top = json_object.get("margin_top").to_float();
  156. m_node_box_sizing.margin.right = json_object.get("margin_right").to_float();
  157. m_node_box_sizing.margin.bottom = json_object.get("margin_bottom").to_float();
  158. m_node_box_sizing.margin.left = json_object.get("margin_left").to_float();
  159. m_node_box_sizing.padding.top = json_object.get("padding_top").to_float();
  160. m_node_box_sizing.padding.right = json_object.get("padding_right").to_float();
  161. m_node_box_sizing.padding.bottom = json_object.get("padding_bottom").to_float();
  162. m_node_box_sizing.padding.left = json_object.get("padding_left").to_float();
  163. m_node_box_sizing.border.top = json_object.get("border_top").to_float();
  164. m_node_box_sizing.border.right = json_object.get("border_right").to_float();
  165. m_node_box_sizing.border.bottom = json_object.get("border_bottom").to_float();
  166. m_node_box_sizing.border.left = json_object.get("border_left").to_float();
  167. m_element_size_view->set_node_content_width(json_object.get("content_width").to_float());
  168. m_element_size_view->set_node_content_height(json_object.get("content_height").to_float());
  169. m_element_size_view->set_box_model(m_node_box_sizing);
  170. }
  171. void InspectorWidget::clear_style_json()
  172. {
  173. m_selection_specified_values_json.clear();
  174. m_computed_style_table_view->set_model(nullptr);
  175. m_selection_computed_values_json.clear();
  176. m_resolved_style_table_view->set_model(nullptr);
  177. m_selection_custom_properties_json.clear();
  178. m_custom_properties_table_view->set_model(nullptr);
  179. m_element_size_view->set_box_model({});
  180. m_element_size_view->set_node_content_width(0);
  181. m_element_size_view->set_node_content_height(0);
  182. }
  183. }