Browser: Port the Inspector to the WebView property tables

This commit is contained in:
Timothy Flynn 2023-11-27 16:18:32 -05:00 committed by Andreas Kling
parent ad9dfffda7
commit 0037fdaf11
7 changed files with 2 additions and 427 deletions

View file

@ -17,7 +17,6 @@ set(SOURCES
ConsoleWidget.cpp
CookiesModel.cpp
DownloadWidget.cpp
ElementSizePreviewWidget.cpp
History/HistoryModel.cpp
History/HistoryWidget.cpp
IconBag.cpp

View file

@ -1,112 +0,0 @@
/*
* Copyright (c) 2022, Michiel Vrins
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include "ElementSizePreviewWidget.h"
#include <LibGUI/Painter.h>
namespace Browser {
void ElementSizePreviewWidget::paint_event(GUI::PaintEvent& event)
{
GUI::Frame::paint_event(event);
GUI::Painter painter(*this);
painter.fill_rect(frame_inner_rect(), Color::White);
int outer_margin = 10;
int text_width_padding = 4;
int text_height_padding = 4;
int content_width_padding = 8;
int content_height_padding = 8;
auto content_size_text = DeprecatedString::formatted("{}x{}", m_node_content_width, m_node_content_height);
int inner_content_width = max(100, font().width(content_size_text) + 2 * content_width_padding);
int inner_content_height = max(15, font().pixel_size_rounded_up() + 2 * content_height_padding);
auto format_size_text = [&](Web::CSSPixels size) {
return DeprecatedString::formatted("{:.4f}", size);
};
auto compute_text_string_width = [&](Web::CSSPixels size) {
return font().width(format_size_text(size)) + 2 * text_width_padding;
};
int margin_left_width = max(25, compute_text_string_width(m_node_box_sizing.margin.left));
int margin_right_width = max(25, compute_text_string_width(m_node_box_sizing.margin.right));
int border_left_width = max(25, compute_text_string_width(m_node_box_sizing.border.left));
int border_right_width = max(25, compute_text_string_width(m_node_box_sizing.border.right));
int padding_left_width = max(25, compute_text_string_width(m_node_box_sizing.padding.left));
int padding_right_width = max(25, compute_text_string_width(m_node_box_sizing.padding.right));
// outer rect
auto margin_rect = to_widget_rect({ outer_margin,
outer_margin,
inner_content_width + border_left_width + border_right_width + margin_left_width + margin_right_width + padding_left_width + padding_right_width,
inner_content_height * 7 });
Gfx::IntSize content_size { margin_rect.width() + 2 * outer_margin, margin_rect.height() + 2 * outer_margin };
set_content_size(content_size);
auto border_rect = margin_rect;
border_rect.take_from_left(margin_left_width);
border_rect.take_from_right(margin_right_width);
border_rect.shrink({ 0, inner_content_height * 2 });
auto padding_rect = border_rect;
padding_rect.take_from_left(border_left_width);
padding_rect.take_from_right(border_right_width);
padding_rect.shrink({ 0, inner_content_height * 2 });
auto content_rect = padding_rect;
content_rect.take_from_left(padding_left_width);
content_rect.take_from_right(padding_right_width);
content_rect.shrink({ 0, inner_content_height * 2 });
auto draw_borders = [&](Gfx::IntRect rect, Color color) {
painter.fill_rect(rect.take_from_top(1), color);
painter.fill_rect(rect.take_from_right(1), color);
painter.fill_rect(rect.take_from_bottom(1), color);
painter.fill_rect(rect.take_from_left(1), color);
};
auto draw_size_texts = [&](Gfx::IntRect rect, Color color, Web::Layout::PixelBox box) {
painter.draw_text(rect, format_size_text(box.top), font(), Gfx::TextAlignment::TopCenter, color);
painter.draw_text(rect, format_size_text(box.right), font(), Gfx::TextAlignment::CenterRight, color);
painter.draw_text(rect, format_size_text(box.bottom), font(), Gfx::TextAlignment::BottomCenter, color);
painter.draw_text(rect, format_size_text(box.left), font(), Gfx::TextAlignment::CenterLeft, color);
};
// paint margin box
painter.fill_rect(margin_rect, Color(249, 204, 157));
draw_borders(margin_rect, Color::Black);
margin_rect.shrink(1, 1, 1, 1);
margin_rect.shrink(text_height_padding, text_width_padding, text_height_padding, text_width_padding);
painter.draw_text(margin_rect, "margin"sv, font(), Gfx::TextAlignment::TopLeft, Color::Black);
draw_size_texts(margin_rect, Color::Black, m_node_box_sizing.margin);
// paint border box
painter.fill_rect(border_rect, Color(253, 221, 155));
draw_borders(border_rect, Color::Black);
border_rect.shrink(1, 1, 1, 1);
border_rect.shrink(text_height_padding, text_width_padding, text_height_padding, text_width_padding);
painter.draw_text(border_rect, "border"sv, font(), Gfx::TextAlignment::TopLeft, Color::Black);
draw_size_texts(border_rect, Color::Black, m_node_box_sizing.border);
// paint padding box
painter.fill_rect(padding_rect, Color(195, 208, 139));
draw_borders(padding_rect, Color::Black);
padding_rect.shrink(1, 1, 1, 1);
padding_rect.shrink(text_height_padding, text_width_padding, text_height_padding, text_width_padding);
painter.draw_text(padding_rect, "padding"sv, font(), Gfx::TextAlignment::TopLeft, Color::Black);
draw_size_texts(padding_rect, Color::Black, m_node_box_sizing.padding);
// paint content box
painter.fill_rect(content_rect, Color(140, 182, 192));
draw_borders(content_rect, Color::Black);
content_rect.shrink(1, 1, 1, 1);
painter.draw_text(content_rect, content_size_text, font(), Gfx::TextAlignment::Center, Color::Black);
}
}

View file

@ -1,31 +0,0 @@
/*
* Copyright (c) 2022, Michiel Vrins
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGUI/AbstractScrollableWidget.h>
#include <LibGUI/Frame.h>
#include <LibGfx/Rect.h>
#include <LibWeb/Layout/BoxModelMetrics.h>
namespace Browser {
class ElementSizePreviewWidget final : public GUI::AbstractScrollableWidget {
C_OBJECT(ElementSizePreviewWidget)
public:
void set_box_model(Web::Layout::BoxModelMetrics box_model) { m_node_box_sizing = box_model; }
void set_node_content_height(float height) { m_node_content_height = height; }
void set_node_content_width(float width) { m_node_content_width = width; }
private:
virtual void paint_event(GUI::PaintEvent&) override;
Web::Layout::BoxModelMetrics m_node_box_sizing;
float m_node_content_height = 0;
float m_node_content_width = 0;
};
}

View file

@ -7,11 +7,7 @@
*/
#include "InspectorWidget.h"
#include "ModelAdapter.h"
#include <LibGUI/BoxLayout.h>
#include <LibGUI/Splitter.h>
#include <LibGUI/TabWidget.h>
#include <LibGUI/TableView.h>
#include <LibWebView/InspectorClient.h>
#include <LibWebView/OutOfProcessWebView.h>
@ -27,47 +23,9 @@ InspectorWidget::InspectorWidget(WebView::OutOfProcessWebView& content_view)
set_layout<GUI::VerticalBoxLayout>(4);
set_fill_with_background_color(true);
auto& splitter = add<GUI::VerticalSplitter>();
m_inspector_view = splitter.add<WebView::OutOfProcessWebView>();
m_inspector_view = add<WebView::OutOfProcessWebView>();
m_inspector_client = make<WebView::InspectorClient>(content_view, *m_inspector_view);
m_inspector_client->on_dom_node_properties_received = [this](auto properties_or_error) {
if (properties_or_error.is_error()) {
clear_style_json();
clear_node_box_model();
} else {
auto properties = properties_or_error.release_value();
load_style_json(properties.computed_style_json, properties.resolved_style_json, properties.custom_properties_json);
update_node_box_model(properties.node_box_sizing_json);
update_aria_properties_state_model(properties.aria_properties_state_json);
}
};
auto& bottom_tab_widget = splitter.add<GUI::TabWidget>();
auto& computed_style_table_container = bottom_tab_widget.add_tab<GUI::Widget>("Computed"_string);
computed_style_table_container.set_layout<GUI::VerticalBoxLayout>(4);
m_computed_style_table_view = computed_style_table_container.add<GUI::TableView>();
auto& resolved_style_table_container = bottom_tab_widget.add_tab<GUI::Widget>("Resolved"_string);
resolved_style_table_container.set_layout<GUI::VerticalBoxLayout>(4);
m_resolved_style_table_view = resolved_style_table_container.add<GUI::TableView>();
auto& custom_properties_table_container = bottom_tab_widget.add_tab<GUI::Widget>("Variables"_string);
custom_properties_table_container.set_layout<GUI::VerticalBoxLayout>(4);
m_custom_properties_table_view = custom_properties_table_container.add<GUI::TableView>();
auto& box_model_widget = bottom_tab_widget.add_tab<GUI::Widget>("Box Model"_string);
box_model_widget.set_layout<GUI::VerticalBoxLayout>(4);
m_element_size_view = box_model_widget.add<ElementSizePreviewWidget>();
m_element_size_view->set_should_hide_unnecessary_scrollbars(true);
auto& aria_properties_state_widget = bottom_tab_widget.add_tab<GUI::Widget>("ARIA"_string);
aria_properties_state_widget.set_layout<GUI::VerticalBoxLayout>(4);
m_aria_properties_state_view = aria_properties_state_widget.add<GUI::TableView>();
m_inspector_view->set_focus(true);
}
@ -81,8 +39,6 @@ void InspectorWidget::inspect()
void InspectorWidget::reset()
{
m_inspector_client->reset();
clear_style_json();
clear_node_box_model();
}
void InspectorWidget::select_default_node()
@ -95,69 +51,4 @@ void InspectorWidget::select_hovered_node()
m_inspector_client->select_hovered_node();
}
void InspectorWidget::load_style_json(StringView computed_values_json, StringView resolved_values_json, StringView custom_properties_json)
{
m_computed_style_table_view->set_model(PropertyTableModel::create(PropertyTableModel::Type::StyleProperties, computed_values_json).release_value_but_fixme_should_propagate_errors());
m_computed_style_table_view->set_searchable(true);
m_resolved_style_table_view->set_model(PropertyTableModel::create(PropertyTableModel::Type::StyleProperties, resolved_values_json).release_value_but_fixme_should_propagate_errors());
m_resolved_style_table_view->set_searchable(true);
m_custom_properties_table_view->set_model(PropertyTableModel::create(PropertyTableModel::Type::StyleProperties, custom_properties_json).release_value_but_fixme_should_propagate_errors());
m_custom_properties_table_view->set_searchable(true);
}
void InspectorWidget::clear_style_json()
{
m_computed_style_table_view->set_model(nullptr);
m_resolved_style_table_view->set_model(nullptr);
m_custom_properties_table_view->set_model(nullptr);
m_aria_properties_state_view->set_model(nullptr);
m_element_size_view->set_box_model({});
m_element_size_view->set_node_content_width(0);
m_element_size_view->set_node_content_height(0);
}
void InspectorWidget::update_node_box_model(StringView node_box_sizing_json)
{
auto json_or_error = JsonValue::from_string(node_box_sizing_json);
if (json_or_error.is_error() || !json_or_error.value().is_object()) {
return;
}
auto json_value = json_or_error.release_value();
auto const& json_object = json_value.as_object();
m_node_box_sizing.margin.top = Web::CSSPixels(json_object.get_float_with_precision_loss("margin_top"sv).value_or(0));
m_node_box_sizing.margin.right = Web::CSSPixels(json_object.get_float_with_precision_loss("margin_right"sv).value_or(0));
m_node_box_sizing.margin.bottom = Web::CSSPixels(json_object.get_float_with_precision_loss("margin_bottom"sv).value_or(0));
m_node_box_sizing.margin.left = Web::CSSPixels(json_object.get_float_with_precision_loss("margin_left"sv).value_or(0));
m_node_box_sizing.padding.top = Web::CSSPixels(json_object.get_float_with_precision_loss("padding_top"sv).value_or(0));
m_node_box_sizing.padding.right = Web::CSSPixels(json_object.get_float_with_precision_loss("padding_right"sv).value_or(0));
m_node_box_sizing.padding.bottom = Web::CSSPixels(json_object.get_float_with_precision_loss("padding_bottom"sv).value_or(0));
m_node_box_sizing.padding.left = Web::CSSPixels(json_object.get_float_with_precision_loss("padding_left"sv).value_or(0));
m_node_box_sizing.border.top = Web::CSSPixels(json_object.get_float_with_precision_loss("border_top"sv).value_or(0));
m_node_box_sizing.border.right = Web::CSSPixels(json_object.get_float_with_precision_loss("border_right"sv).value_or(0));
m_node_box_sizing.border.bottom = Web::CSSPixels(json_object.get_float_with_precision_loss("border_bottom"sv).value_or(0));
m_node_box_sizing.border.left = Web::CSSPixels(json_object.get_float_with_precision_loss("border_left"sv).value_or(0));
m_element_size_view->set_node_content_width(json_object.get_float_with_precision_loss("content_width"sv).value_or(0));
m_element_size_view->set_node_content_height(json_object.get_float_with_precision_loss("content_height"sv).value_or(0));
m_element_size_view->set_box_model(m_node_box_sizing);
}
void InspectorWidget::clear_node_box_model()
{
m_node_box_sizing = Web::Layout::BoxModelMetrics {};
m_element_size_view->set_node_content_width(0);
m_element_size_view->set_node_content_height(0);
m_element_size_view->set_box_model(m_node_box_sizing);
}
void InspectorWidget::update_aria_properties_state_model(StringView aria_properties_state_json)
{
m_aria_properties_state_view->set_model(PropertyTableModel::create(PropertyTableModel::Type::ARIAProperties, aria_properties_state_json).release_value_but_fixme_should_propagate_errors());
m_aria_properties_state_view->set_searchable(true);
}
}

View file

@ -8,11 +8,8 @@
#pragma once
#include "ElementSizePreviewWidget.h"
#include <AK/StringView.h>
#include <LibGUI/Widget.h>
#include <LibWeb/Forward.h>
#include <LibWeb/Layout/BoxModelMetrics.h>
#include <LibWebView/Forward.h>
namespace Browser {
@ -33,24 +30,8 @@ public:
private:
explicit InspectorWidget(WebView::OutOfProcessWebView& content_view);
void load_style_json(StringView computed_values_json, StringView resolved_values_json, StringView custom_properties_json);
void clear_style_json();
void update_node_box_model(StringView node_box_sizing_json);
void clear_node_box_model();
void update_aria_properties_state_model(StringView aria_properties_state_json);
RefPtr<WebView::OutOfProcessWebView> m_inspector_view;
OwnPtr<WebView::InspectorClient> m_inspector_client;
RefPtr<GUI::TableView> m_computed_style_table_view;
RefPtr<GUI::TableView> m_resolved_style_table_view;
RefPtr<GUI::TableView> m_custom_properties_table_view;
RefPtr<GUI::TableView> m_aria_properties_state_view;
RefPtr<ElementSizePreviewWidget> m_element_size_view;
Web::Layout::BoxModelMetrics m_node_box_sizing;
};
}

View file

@ -1,153 +0,0 @@
/*
* Copyright (c) 2023, Tim Flynn <trflynn89@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/JsonValue.h>
#include <AK/NonnullRefPtr.h>
#include <AK/Optional.h>
#include <AK/String.h>
#include <AK/StringView.h>
#include <LibGUI/Model.h>
#include <LibGUI/TreeView.h>
#include <LibGfx/Bitmap.h>
#include <LibGfx/Palette.h>
#include <LibWeb/CSS/Selector.h>
#include <LibWebView/ModelIndex.h>
#include <LibWebView/PropertyTableModel.h>
namespace Browser {
template<typename ModelType>
class ModelAdapter : public GUI::Model {
public:
using Type = typename ModelType::Type;
static ErrorOr<NonnullRefPtr<ModelAdapter>> create(Type type, StringView model)
{
return adopt_ref(*new ModelAdapter(type, TRY(parse_json_model(model))));
}
virtual ~ModelAdapter() = default;
virtual int row_count(GUI::ModelIndex const& parent) const override
{
return m_model.row_count(to_web_view_model_index(parent));
}
virtual int column_count(GUI::ModelIndex const& parent) const override
{
return m_model.column_count(to_web_view_model_index(parent));
}
virtual GUI::ModelIndex index(int row, int column, GUI::ModelIndex const& parent) const override
{
auto index = m_model.index(row, column, to_web_view_model_index(parent));
return to_gui_model_index(index);
}
virtual GUI::ModelIndex parent_index(GUI::ModelIndex const& index) const override
{
if constexpr (requires { m_model.parent(declval<WebView::ModelIndex const&>()); }) {
auto parent = m_model.parent(to_web_view_model_index(index));
return to_gui_model_index(parent);
} else {
return {};
}
}
virtual GUI::Variant data(GUI::ModelIndex const& index, GUI::ModelRole role) const override
{
if (role == GUI::ModelRole::Display) {
auto text = m_model.text_for_display(to_web_view_model_index(index));
return text.to_deprecated_string();
}
return {};
}
GUI::ModelIndex index_for_node(i32 node_id, Optional<Web::CSS::Selector::PseudoElement> const& pseudo_element) const
{
if constexpr (requires { m_model.index_for_node(node_id, pseudo_element); }) {
auto parent = m_model.index_for_node(node_id, pseudo_element);
return to_gui_model_index(parent);
} else {
return {};
}
}
protected:
ModelAdapter(Type type, JsonValue model)
: m_model(type, move(model))
{
}
static ErrorOr<JsonValue> parse_json_model(StringView model)
{
auto json_model = TRY(JsonValue::from_string(model));
if (!json_model.is_object())
return Error::from_string_literal("Expected model to be a JSON object");
return json_model;
}
ALWAYS_INLINE GUI::ModelIndex to_gui_model_index(WebView::ModelIndex const& index) const
{
if (!index.is_valid())
return {};
return create_index(index.row, index.column, index.internal_data);
}
ALWAYS_INLINE WebView::ModelIndex to_web_view_model_index(GUI::ModelIndex const& index) const
{
if (!index.is_valid())
return {};
return { index.row(), index.column(), index.internal_data() };
}
ModelType m_model;
};
class PropertyTableModel : public ModelAdapter<WebView::PropertyTableModel> {
public:
static ErrorOr<NonnullRefPtr<ModelAdapter>> create(Type type, StringView model)
{
return adopt_ref(*new PropertyTableModel(type, TRY(parse_json_model(model))));
}
virtual ErrorOr<String> column_name(int column_index) const override
{
return m_model.column_name(column_index);
}
virtual bool is_searchable() const override { return true; }
virtual Vector<GUI::ModelIndex> matches(StringView searching, unsigned flags, GUI::ModelIndex const&) override
{
Vector<GUI::ModelIndex> found_indices;
m_model.for_each_property_name([&](auto index, auto const& property_name) {
if (!string_matches(property_name, searching, flags))
return IterationDecision::Continue;
found_indices.append(to_gui_model_index(index));
if ((flags & FirstMatchOnly) != 0)
return IterationDecision::Break;
return IterationDecision::Continue;
});
return found_indices;
}
private:
PropertyTableModel(Type type, JsonValue model)
: ModelAdapter(type, move(model))
{
}
};
}

View file

@ -864,7 +864,7 @@ void Tab::show_inspector_window(Browser::Tab::InspectorTarget inspector_target)
if (!m_dom_inspector_widget) {
auto window = GUI::Window::construct(&this->window());
window->set_window_mode(GUI::WindowMode::Modeless);
window->resize(325, 500);
window->resize(750, 500);
window->set_title("Inspector");
window->set_icon(g_icon_bag.inspector_object);
window->on_close = [&]() {