diff --git a/DevTools/Inspector/Makefile b/DevTools/Inspector/Makefile index a60cc4fbde..8c62b3864d 100644 --- a/DevTools/Inspector/Makefile +++ b/DevTools/Inspector/Makefile @@ -2,6 +2,9 @@ include ../../Makefile.common OBJS = \ RemoteObjectGraphModel.o \ + RemoteObjectPropertyModel.o \ + RemoteProcess.o \ + RemoteObject.o \ main.o APP = Inspector diff --git a/DevTools/Inspector/RemoteObject.cpp b/DevTools/Inspector/RemoteObject.cpp new file mode 100644 index 0000000000..e927f8d2b0 --- /dev/null +++ b/DevTools/Inspector/RemoteObject.cpp @@ -0,0 +1,13 @@ +#include "RemoteObject.h" +#include "RemoteObjectPropertyModel.h" + +RemoteObject::RemoteObject() + : m_property_model(RemoteObjectPropertyModel::create(*this)) +{ +} + +RemoteObjectPropertyModel& RemoteObject::property_model() +{ + m_property_model->update(); + return *m_property_model; +} diff --git a/DevTools/Inspector/RemoteObject.h b/DevTools/Inspector/RemoteObject.h new file mode 100644 index 0000000000..1718683276 --- /dev/null +++ b/DevTools/Inspector/RemoteObject.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include +#include + +class RemoteObjectPropertyModel; + +class RemoteObject { +public: + RemoteObject(); + + RemoteObjectPropertyModel& property_model(); + + RemoteObject* parent { nullptr }; + NonnullOwnPtrVector children; + + String address; + String parent_address; + String class_name; + String name; + + JsonObject json; + + NonnullRefPtr m_property_model; +}; diff --git a/DevTools/Inspector/RemoteObjectGraphModel.cpp b/DevTools/Inspector/RemoteObjectGraphModel.cpp index 749a59e78b..2c9a0c1910 100644 --- a/DevTools/Inspector/RemoteObjectGraphModel.cpp +++ b/DevTools/Inspector/RemoteObjectGraphModel.cpp @@ -1,12 +1,14 @@ #include "RemoteObjectGraphModel.h" +#include "RemoteObject.h" +#include "RemoteProcess.h" #include #include #include #include #include -RemoteObjectGraphModel::RemoteObjectGraphModel(pid_t pid) - : m_pid(pid) +RemoteObjectGraphModel::RemoteObjectGraphModel(RemoteProcess& process) + : m_process(process) { m_object_icon.set_bitmap_for_size(16, load_png("/res/icons/16x16/inspector-object.png")); m_window_icon.set_bitmap_for_size(16, load_png("/res/icons/16x16/window.png")); @@ -19,12 +21,12 @@ RemoteObjectGraphModel::~RemoteObjectGraphModel() GModelIndex RemoteObjectGraphModel::index(int row, int column, const GModelIndex& parent) const { if (!parent.is_valid()) { - if (m_remote_roots.is_empty()) + if (m_process.roots().is_empty()) return {}; - return create_index(row, column, &m_remote_roots.at(row)); + return create_index(row, column, &m_process.roots().at(row)); } auto& remote_parent = *static_cast(parent.internal_data()); - return create_index(row, column, remote_parent.children.at(row)); + return create_index(row, column, &remote_parent.children.at(row)); } GModelIndex RemoteObjectGraphModel::parent_index(const GModelIndex& index) const @@ -35,7 +37,7 @@ GModelIndex RemoteObjectGraphModel::parent_index(const GModelIndex& index) const if (!remote_object.parent) return {}; for (int row = 0; row < remote_object.parent->children.size(); ++row) { - if (remote_object.parent->children[row] == &remote_object) + if (&remote_object.parent->children[row] == &remote_object) return create_index(row, 0, remote_object.parent); } @@ -46,7 +48,7 @@ GModelIndex RemoteObjectGraphModel::parent_index(const GModelIndex& index) const int RemoteObjectGraphModel::row_count(const GModelIndex& index) const { if (!index.is_valid()) - return m_remote_roots.size(); + return m_process.roots().size(); auto& remote_object = *static_cast(index.internal_data()); return remote_object.children.size(); } @@ -72,54 +74,5 @@ GVariant RemoteObjectGraphModel::data(const GModelIndex& index, Role role) const void RemoteObjectGraphModel::update() { - auto success = m_socket.connect(CSocketAddress::local(String::format("/tmp/rpc.%d", m_pid))); - if (!success) { - fprintf(stderr, "Couldn't connect to PID %d\n", m_pid); - exit(1); - } - - m_socket.on_connected = [this] { - dbg() << "Connected to PID " << m_pid; - }; - - m_socket.on_ready_to_read = [this] { - if (m_socket.eof()) { - dbg() << "Disconnected from PID " << m_pid; - m_socket.close(); - return; - } - - auto data = m_socket.read_all(); - auto json_value = JsonValue::from_string(data); - ASSERT(json_value.is_array()); - m_json = json_value.as_array(); - - Vector> remote_objects; - HashMap objects_by_address; - - for (auto& value : m_json.values()) { - ASSERT(value.is_object()); - auto& object = value.as_object(); - auto remote_object = make(); - remote_object->address = object.get("address").to_string(); - remote_object->parent_address = object.get("parent").to_string(); - remote_object->name = object.get("name").to_string(); - remote_object->class_name = object.get("class_name").to_string(); - objects_by_address.set(remote_object->address, remote_object); - remote_objects.append(move(remote_object)); - } - - for (int i = 0; i < remote_objects.size(); ++i) { - auto& remote_object = remote_objects[i]; - auto* parent = objects_by_address.get(remote_object->parent_address).value_or(nullptr); - if (!parent) { - m_remote_roots.append(move(remote_object)); - } else { - remote_object->parent = parent; - parent->children.append(move(remote_object)); - } - } - - did_update(); - }; + did_update(); } diff --git a/DevTools/Inspector/RemoteObjectGraphModel.h b/DevTools/Inspector/RemoteObjectGraphModel.h index 0edbd2981a..5dc64dbbdf 100644 --- a/DevTools/Inspector/RemoteObjectGraphModel.h +++ b/DevTools/Inspector/RemoteObjectGraphModel.h @@ -1,15 +1,18 @@ #pragma once #include +#include #include #include #include +class RemoteProcess; + class RemoteObjectGraphModel final : public GModel { public: - static NonnullRefPtr create_with_pid(pid_t pid) + static NonnullRefPtr create(RemoteProcess& process) { - return adopt(*new RemoteObjectGraphModel(pid)); + return adopt(*new RemoteObjectGraphModel(process)); } virtual ~RemoteObjectGraphModel() override; @@ -22,22 +25,10 @@ public: virtual void update() override; private: - struct RemoteObject { - RemoteObject* parent { nullptr }; - Vector> children; + explicit RemoteObjectGraphModel(RemoteProcess&); - String address; - String parent_address; - String class_name; - String name; - }; + RemoteProcess& m_process; - explicit RemoteObjectGraphModel(pid_t); - - pid_t m_pid { -1 }; - CLocalSocket m_socket; - JsonArray m_json; - NonnullOwnPtrVector m_remote_roots; GIcon m_object_icon; GIcon m_window_icon; }; diff --git a/DevTools/Inspector/RemoteObjectPropertyModel.cpp b/DevTools/Inspector/RemoteObjectPropertyModel.cpp new file mode 100644 index 0000000000..3e9ad0e9d2 --- /dev/null +++ b/DevTools/Inspector/RemoteObjectPropertyModel.cpp @@ -0,0 +1,46 @@ +#include "RemoteObjectPropertyModel.h" +#include "RemoteObject.h" + +RemoteObjectPropertyModel::RemoteObjectPropertyModel(RemoteObject& object) + : m_object(object) +{ +} + +int RemoteObjectPropertyModel::row_count(const GModelIndex&) const +{ + return m_properties.size(); +} + +String RemoteObjectPropertyModel::column_name(int column) const +{ + switch (column) { + case Column::Name: + return "Name"; + case Column::Value: + return "Value"; + } + ASSERT_NOT_REACHED(); +} + +GVariant RemoteObjectPropertyModel::data(const GModelIndex& index, Role role) const +{ + auto& property = m_properties[index.row()]; + if (role == Role::Display) { + switch (index.column()) { + case Column::Name: + return property.name; + case Column::Value: + return property.value; + } + } + return {}; +} + +void RemoteObjectPropertyModel::update() +{ + m_properties.clear(); + m_object.json.for_each_member([this](auto& name, auto& value) { + m_properties.append({ name, value }); + }); + did_update(); +} diff --git a/DevTools/Inspector/RemoteObjectPropertyModel.h b/DevTools/Inspector/RemoteObjectPropertyModel.h new file mode 100644 index 0000000000..9ff905ecd1 --- /dev/null +++ b/DevTools/Inspector/RemoteObjectPropertyModel.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +class RemoteObject; + +class RemoteObjectPropertyModel final : public GModel { +public: + virtual ~RemoteObjectPropertyModel() override {} + static NonnullRefPtr create(RemoteObject& object) + { + return adopt(*new RemoteObjectPropertyModel(object)); + } + + enum Column { + Name, + Value, + __Count, + }; + + virtual int row_count(const GModelIndex& = GModelIndex()) const override; + virtual int column_count(const GModelIndex& = GModelIndex()) const override { return Column::__Count; } + virtual String column_name(int) const override; + virtual GVariant data(const GModelIndex&, Role = Role::Display) const override; + virtual void update() override; + +private: + explicit RemoteObjectPropertyModel(RemoteObject&); + + RemoteObject& m_object; + struct NameAndValue { + JsonValue name; + JsonValue value; + }; + Vector m_properties; +}; diff --git a/DevTools/Inspector/RemoteProcess.cpp b/DevTools/Inspector/RemoteProcess.cpp new file mode 100644 index 0000000000..50a21abc77 --- /dev/null +++ b/DevTools/Inspector/RemoteProcess.cpp @@ -0,0 +1,66 @@ +#include "RemoteProcess.h" +#include "RemoteObject.h" +#include "RemoteObjectGraphModel.h" +#include "RemoteObjectPropertyModel.h" +#include + +RemoteProcess::RemoteProcess(pid_t pid) + : m_pid(pid) + , m_object_graph_model(RemoteObjectGraphModel::create(*this)) +{ +} + +void RemoteProcess::update() +{ + auto success = m_socket.connect(CSocketAddress::local(String::format("/tmp/rpc.%d", m_pid))); + if (!success) { + fprintf(stderr, "Couldn't connect to PID %d\n", m_pid); + exit(1); + } + + m_socket.on_connected = [this] { + dbg() << "Connected to PID " << m_pid; + }; + + m_socket.on_ready_to_read = [this] { + if (m_socket.eof()) { + dbg() << "Disconnected from PID " << m_pid; + m_socket.close(); + return; + } + + auto data = m_socket.read_all(); + auto json_value = JsonValue::from_string(data); + ASSERT(json_value.is_array()); + auto& object_array = json_value.as_array(); + + Vector> remote_objects; + HashMap objects_by_address; + + for (auto& value : object_array.values()) { + ASSERT(value.is_object()); + auto& object = value.as_object(); + auto remote_object = make(); + remote_object->address = object.get("address").to_string(); + remote_object->parent_address = object.get("parent").to_string(); + remote_object->name = object.get("name").to_string(); + remote_object->class_name = object.get("class_name").to_string(); + remote_object->json = object; + objects_by_address.set(remote_object->address, remote_object); + remote_objects.append(move(remote_object)); + } + + for (int i = 0; i < remote_objects.size(); ++i) { + auto& remote_object = remote_objects[i]; + auto* parent = objects_by_address.get(remote_object->parent_address).value_or(nullptr); + if (!parent) { + m_roots.append(move(remote_object)); + } else { + remote_object->parent = parent; + parent->children.append(move(remote_object)); + } + } + + m_object_graph_model->update(); + }; +} diff --git a/DevTools/Inspector/RemoteProcess.h b/DevTools/Inspector/RemoteProcess.h new file mode 100644 index 0000000000..8b37242955 --- /dev/null +++ b/DevTools/Inspector/RemoteProcess.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +class RemoteObjectGraphModel; +class RemoteObject; + +class RemoteProcess { +public: + explicit RemoteProcess(pid_t); + void update(); + + RemoteObjectGraphModel& object_graph_model() { return *m_object_graph_model; } + const NonnullOwnPtrVector& roots() const { return m_roots; } + +private: + pid_t m_pid { -1 }; + NonnullRefPtr m_object_graph_model; + CLocalSocket m_socket; + NonnullOwnPtrVector m_roots; +}; diff --git a/DevTools/Inspector/main.cpp b/DevTools/Inspector/main.cpp index b0d1dd8ee6..0e57e39951 100644 --- a/DevTools/Inspector/main.cpp +++ b/DevTools/Inspector/main.cpp @@ -1,6 +1,11 @@ +#include "RemoteObject.h" #include "RemoteObjectGraphModel.h" +#include "RemoteObjectPropertyModel.h" +#include "RemoteProcess.h" #include #include +#include +#include #include #include #include @@ -32,10 +37,23 @@ int main(int argc, char** argv) widget->set_fill_with_background_color(true); widget->set_layout(make(Orientation::Vertical)); - auto* tree_view = new GTreeView(widget); - tree_view->set_model(RemoteObjectGraphModel::create_with_pid(pid)); - tree_view->model()->update(); + auto* splitter = new GSplitter(Orientation::Horizontal, widget); + + RemoteProcess remote_process(pid); + + auto* tree_view = new GTreeView(splitter); + tree_view->set_model(remote_process.object_graph_model()); + tree_view->set_activates_on_selection(true); + + auto* properties_table_view = new GTableView(splitter); + properties_table_view->set_size_columns_to_fit_content(true); + + tree_view->on_activation = [&](auto& index) { + auto* remote_object = static_cast(index.internal_data()); + properties_table_view->set_model(remote_object->property_model()); + }; window->show(); + remote_process.update(); return app.exec(); }