From fe820f6d5aa1f9b66a424b4c8f79596c3d611240 Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Tue, 24 Aug 2021 17:04:30 +0100 Subject: [PATCH] LibWeb: Replace unused DOMTreeModel with DOMTreeJSONModel The direct-Document-access DOMTreeModel is no longer used, since the DOM Inspector has to access the Document remotely over IPC. This commit removes it, and renames DOMTreeJSONModel to take its place, since it no longer has to differentiate itself from the non-JSON one. In case that didn't make sense: - Delete DOMTreeModel - Rename DOMTreeJSONModel -> DOMTreeModel --- .../Applications/Browser/InspectorWidget.cpp | 3 +- Userland/Libraries/LibWeb/CMakeLists.txt | 1 - .../Libraries/LibWeb/DOMTreeJSONModel.cpp | 169 ------------------ Userland/Libraries/LibWeb/DOMTreeJSONModel.h | 62 ------- Userland/Libraries/LibWeb/DOMTreeModel.cpp | 127 +++++++------ Userland/Libraries/LibWeb/DOMTreeModel.h | 33 +++- 6 files changed, 101 insertions(+), 294 deletions(-) delete mode 100644 Userland/Libraries/LibWeb/DOMTreeJSONModel.cpp delete mode 100644 Userland/Libraries/LibWeb/DOMTreeJSONModel.h diff --git a/Userland/Applications/Browser/InspectorWidget.cpp b/Userland/Applications/Browser/InspectorWidget.cpp index 615f12b58c..4e95216359 100644 --- a/Userland/Applications/Browser/InspectorWidget.cpp +++ b/Userland/Applications/Browser/InspectorWidget.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include @@ -59,7 +58,7 @@ void InspectorWidget::set_dom_json(String json) return; m_dom_json = json; - m_dom_tree_view->set_model(Web::DOMTreeJSONModel::create(m_dom_json->view())); + m_dom_tree_view->set_model(Web::DOMTreeModel::create(m_dom_json->view())); // FIXME: Support the LayoutTreeModel // m_layout_tree_view->set_model(Web::LayoutTreeModel::create(*document)); diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 4afd6dba2a..e911822397 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -65,7 +65,6 @@ set(SOURCES DOM/Timer.cpp DOM/Window.cpp DOMTreeModel.cpp - DOMTreeJSONModel.cpp Dump.cpp FontCache.cpp HTML/AttributeNames.cpp diff --git a/Userland/Libraries/LibWeb/DOMTreeJSONModel.cpp b/Userland/Libraries/LibWeb/DOMTreeJSONModel.cpp deleted file mode 100644 index dbff61cd71..0000000000 --- a/Userland/Libraries/LibWeb/DOMTreeJSONModel.cpp +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2018-2020, Adam Hodgen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "DOMTreeJSONModel.h" -#include -#include -#include - -namespace Web { - -DOMTreeJSONModel::DOMTreeJSONModel(JsonObject dom_tree) - : m_dom_tree(move(dom_tree)) -{ - m_document_icon.set_bitmap_for_size(16, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/filetype-html.png")); - m_element_icon.set_bitmap_for_size(16, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/inspector-object.png")); - m_text_icon.set_bitmap_for_size(16, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/filetype-unknown.png")); - - map_dom_nodes_to_parent(nullptr, &m_dom_tree); -} - -DOMTreeJSONModel::~DOMTreeJSONModel() -{ -} - -GUI::ModelIndex DOMTreeJSONModel::index(int row, int column, const GUI::ModelIndex& parent) const -{ - if (!parent.is_valid()) { - return create_index(row, column, &m_dom_tree); - } - - auto const& parent_node = *static_cast(parent.internal_data()); - auto const* children = get_children(parent_node); - if (!children) - return create_index(row, column, &m_dom_tree); - - auto const& child_node = children->at(row).as_object(); - return create_index(row, column, &child_node); -} - -GUI::ModelIndex DOMTreeJSONModel::parent_index(const GUI::ModelIndex& index) const -{ - // FIXME: Handle the template element (child elements are not stored in it, all of its children are in its document fragment "content") - // Probably in the JSON generation in Node.cpp? - if (!index.is_valid()) - return {}; - - auto const& node = *static_cast(index.internal_data()); - - auto const* parent_node = get_parent(node); - if (!parent_node) - return {}; - - // If the parent is the root document, we know it has index 0, 0 - if (parent_node == &m_dom_tree) { - return create_index(0, 0, parent_node); - } - - // Otherwise, we need to find the grandparent, to find the index of parent within that - auto const* grandparent_node = get_parent(*parent_node); - VERIFY(grandparent_node); - - auto const* grandparent_children = get_children(*grandparent_node); - if (!grandparent_children) - return {}; - - for (size_t grandparent_child_index = 0; grandparent_child_index < grandparent_children->size(); ++grandparent_child_index) { - auto const& child = grandparent_children->at(grandparent_child_index).as_object(); - if (&child == parent_node) - return create_index(grandparent_child_index, 0, parent_node); - } - - return {}; -} - -int DOMTreeJSONModel::row_count(const GUI::ModelIndex& index) const -{ - if (!index.is_valid()) - return 1; - - auto const& node = *static_cast(index.internal_data()); - auto const* children = get_children(node); - return children ? children->size() : 0; -} - -int DOMTreeJSONModel::column_count(const GUI::ModelIndex&) const -{ - return 1; -} - -static String with_whitespace_collapsed(const StringView& string) -{ - StringBuilder builder; - for (size_t i = 0; i < string.length(); ++i) { - if (isspace(string[i])) { - builder.append(' '); - while (i < string.length()) { - if (isspace(string[i])) { - ++i; - continue; - } - builder.append(string[i]); - break; - } - continue; - } - builder.append(string[i]); - } - return builder.to_string(); -} - -GUI::Variant DOMTreeJSONModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const -{ - auto const& node = *static_cast(index.internal_data()); - auto node_name = node.get("name").as_string(); - auto type = node.get("type").as_string_or("unknown"); - - if (role == GUI::ModelRole::Icon) { - if (type == "document") - return m_document_icon; - if (type == "element") - return m_element_icon; - // FIXME: More node type icons? - return m_text_icon; - } - if (role == GUI::ModelRole::Display) { - if (type == "text") - return with_whitespace_collapsed(node.get("text").as_string()); - if (type != "element") - return node_name; - - StringBuilder builder; - builder.append('<'); - builder.append(node_name.to_lowercase()); - if (node.has("attributes")) { - auto attributes = node.get("attributes").as_object(); - attributes.for_each_member([&builder](auto& name, JsonValue const& value) { - builder.append(' '); - builder.append(name); - builder.append('='); - builder.append('"'); - builder.append(value.to_string()); - builder.append('"'); - }); - } - builder.append('>'); - return builder.to_string(); - } - return {}; -} - -void DOMTreeJSONModel::map_dom_nodes_to_parent(JsonObject const* parent, JsonObject const* node) -{ - m_dom_node_to_parent_map.set(node, parent); - - auto const* children = get_children(*node); - if (!children) - return; - - children->for_each([&](auto const& child) { - auto const& child_node = child.as_object(); - map_dom_nodes_to_parent(node, &child_node); - }); -} - -} diff --git a/Userland/Libraries/LibWeb/DOMTreeJSONModel.h b/Userland/Libraries/LibWeb/DOMTreeJSONModel.h deleted file mode 100644 index f80f1b52b8..0000000000 --- a/Userland/Libraries/LibWeb/DOMTreeJSONModel.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2018-2020, Andreas Kling - * Copyright (c) 2018-2020, Adam Hodgen - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include - -namespace Web { - -class DOMTreeJSONModel final : public GUI::Model { -public: - static NonnullRefPtr create(StringView dom_tree) - { - auto json_or_error = JsonValue::from_string(dom_tree); - if (!json_or_error.has_value()) - VERIFY_NOT_REACHED(); - - return adopt_ref(*new DOMTreeJSONModel(json_or_error.value().as_object())); - } - - virtual ~DOMTreeJSONModel() override; - - virtual int row_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; - virtual int column_count(const GUI::ModelIndex& = GUI::ModelIndex()) const override; - virtual GUI::Variant data(const GUI::ModelIndex&, GUI::ModelRole) const override; - virtual GUI::ModelIndex index(int row, int column, const GUI::ModelIndex& parent = GUI::ModelIndex()) const override; - virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override; - -private: - explicit DOMTreeJSONModel(JsonObject); - - ALWAYS_INLINE JsonObject const* get_parent(const JsonObject& o) const - { - auto parent_node = m_dom_node_to_parent_map.get(&o); - VERIFY(parent_node.has_value()); - return *parent_node; - } - - ALWAYS_INLINE static JsonArray const* get_children(const JsonObject& o) - { - if (auto const* maybe_children = o.get_ptr("children"); maybe_children) - return &maybe_children->as_array(); - return nullptr; - } - - void map_dom_nodes_to_parent(JsonObject const* parent, JsonObject const* child); - - GUI::Icon m_document_icon; - GUI::Icon m_element_icon; - GUI::Icon m_text_icon; - JsonObject m_dom_tree; - HashMap m_dom_node_to_parent_map; -}; - -} diff --git a/Userland/Libraries/LibWeb/DOMTreeModel.cpp b/Userland/Libraries/LibWeb/DOMTreeModel.cpp index d3eaf157f6..da0a953682 100644 --- a/Userland/Libraries/LibWeb/DOMTreeModel.cpp +++ b/Userland/Libraries/LibWeb/DOMTreeModel.cpp @@ -1,25 +1,25 @@ /* * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2018-2020, Adam Hodgen * * SPDX-License-Identifier: BSD-2-Clause */ #include "DOMTreeModel.h" +#include #include -#include -#include -#include #include -#include namespace Web { -DOMTreeModel::DOMTreeModel(DOM::Document& document) - : m_document(document) +DOMTreeModel::DOMTreeModel(JsonObject dom_tree) + : m_dom_tree(move(dom_tree)) { m_document_icon.set_bitmap_for_size(16, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/filetype-html.png")); m_element_icon.set_bitmap_for_size(16, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/inspector-object.png")); m_text_icon.set_bitmap_for_size(16, Gfx::Bitmap::try_load_from_file("/res/icons/16x16/filetype-unknown.png")); + + map_dom_nodes_to_parent(nullptr, &m_dom_tree); } DOMTreeModel::~DOMTreeModel() @@ -29,37 +29,50 @@ DOMTreeModel::~DOMTreeModel() GUI::ModelIndex DOMTreeModel::index(int row, int column, const GUI::ModelIndex& parent) const { if (!parent.is_valid()) { - return create_index(row, column, m_document.ptr()); + return create_index(row, column, &m_dom_tree); } - auto& parent_node = *static_cast(parent.internal_data()); - return create_index(row, column, parent_node.child_at_index(row)); + + auto const& parent_node = *static_cast(parent.internal_data()); + auto const* children = get_children(parent_node); + if (!children) + return create_index(row, column, &m_dom_tree); + + auto const& child_node = children->at(row).as_object(); + return create_index(row, column, &child_node); } GUI::ModelIndex DOMTreeModel::parent_index(const GUI::ModelIndex& index) const { + // FIXME: Handle the template element (child elements are not stored in it, all of its children are in its document fragment "content") + // Probably in the JSON generation in Node.cpp? if (!index.is_valid()) return {}; - auto& node = *static_cast(index.internal_data()); - if (!node.parent()) + + auto const& node = *static_cast(index.internal_data()); + + auto const* parent_node = get_parent(node); + if (!parent_node) return {}; - // FIXME: Handle the template element (child elements are not stored in it, all of its children are in its document fragment "content") - - // No grandparent? Parent is the document! - if (!node.parent()->parent()) { - return create_index(0, 0, m_document.ptr()); + // If the parent is the root document, we know it has index 0, 0 + if (parent_node == &m_dom_tree) { + return create_index(0, 0, parent_node); } - // Walk the grandparent's children to find the index of node's parent in its parent. - // (This is needed to produce the row number of the GUI::ModelIndex corresponding to node's parent.) - int grandparent_child_index = 0; - for (auto* grandparent_child = node.parent()->parent()->first_child(); grandparent_child; grandparent_child = grandparent_child->next_sibling()) { - if (grandparent_child == node.parent()) - return create_index(grandparent_child_index, 0, node.parent()); - ++grandparent_child_index; + // Otherwise, we need to find the grandparent, to find the index of parent within that + auto const* grandparent_node = get_parent(*parent_node); + VERIFY(grandparent_node); + + auto const* grandparent_children = get_children(*grandparent_node); + if (!grandparent_children) + return {}; + + for (size_t grandparent_child_index = 0; grandparent_child_index < grandparent_children->size(); ++grandparent_child_index) { + auto const& child = grandparent_children->at(grandparent_child_index).as_object(); + if (&child == parent_node) + return create_index(grandparent_child_index, 0, parent_node); } - VERIFY_NOT_REACHED(); return {}; } @@ -67,8 +80,10 @@ int DOMTreeModel::row_count(const GUI::ModelIndex& index) const { if (!index.is_valid()) return 1; - auto& node = *static_cast(index.internal_data()); - return node.child_count(); + + auto const& node = *static_cast(index.internal_data()); + auto const* children = get_children(node); + return children ? children->size() : 0; } int DOMTreeModel::column_count(const GUI::ModelIndex&) const @@ -99,52 +114,56 @@ static String with_whitespace_collapsed(const StringView& string) GUI::Variant DOMTreeModel::data(const GUI::ModelIndex& index, GUI::ModelRole role) const { - auto& node = *static_cast(index.internal_data()); + auto const& node = *static_cast(index.internal_data()); + auto node_name = node.get("name").as_string(); + auto type = node.get("type").as_string_or("unknown"); + if (role == GUI::ModelRole::Icon) { - if (node.is_document()) + if (type == "document") return m_document_icon; - if (node.is_element()) + if (type == "element") return m_element_icon; // FIXME: More node type icons? return m_text_icon; } if (role == GUI::ModelRole::Display) { - if (node.is_text()) - return with_whitespace_collapsed(verify_cast(node).data()); - if (!node.is_element()) - return node.node_name(); - auto& element = verify_cast(node); + if (type == "text") + return with_whitespace_collapsed(node.get("text").as_string()); + if (type != "element") + return node_name; + StringBuilder builder; builder.append('<'); - builder.append(element.local_name()); - element.for_each_attribute([&](auto& name, auto& value) { - builder.append(' '); - builder.append(name); - builder.append('='); - builder.append('"'); - builder.append(value); - builder.append('"'); - }); + builder.append(node_name.to_lowercase()); + if (node.has("attributes")) { + auto attributes = node.get("attributes").as_object(); + attributes.for_each_member([&builder](auto& name, JsonValue const& value) { + builder.append(' '); + builder.append(name); + builder.append('='); + builder.append('"'); + builder.append(value.to_string()); + builder.append('"'); + }); + } builder.append('>'); return builder.to_string(); } return {}; } -GUI::ModelIndex DOMTreeModel::index_for_node(DOM::Node* node) const +void DOMTreeModel::map_dom_nodes_to_parent(JsonObject const* parent, JsonObject const* node) { - if (!node) - return {}; + m_dom_node_to_parent_map.set(node, parent); - DOM::Node* parent = node->parent(); - if (!parent) - return {}; + auto const* children = get_children(*node); + if (!children) + return; - auto maybe_row = parent->index_of_child(*node); - if (maybe_row.has_value()) - return create_index(maybe_row.value(), 0, node); - - return {}; + children->for_each([&](auto const& child) { + auto const& child_node = child.as_object(); + map_dom_nodes_to_parent(node, &child_node); + }); } } diff --git a/Userland/Libraries/LibWeb/DOMTreeModel.h b/Userland/Libraries/LibWeb/DOMTreeModel.h index bfd5875ab4..b53726d0b6 100644 --- a/Userland/Libraries/LibWeb/DOMTreeModel.h +++ b/Userland/Libraries/LibWeb/DOMTreeModel.h @@ -1,11 +1,14 @@ /* * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2018-2020, Adam Hodgen * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once +#include +#include #include #include @@ -13,9 +16,13 @@ namespace Web { class DOMTreeModel final : public GUI::Model { public: - static NonnullRefPtr create(DOM::Document& document) + static NonnullRefPtr create(StringView dom_tree) { - return adopt_ref(*new DOMTreeModel(document)); + auto json_or_error = JsonValue::from_string(dom_tree); + if (!json_or_error.has_value()) + VERIFY_NOT_REACHED(); + + return adopt_ref(*new DOMTreeModel(json_or_error.value().as_object())); } virtual ~DOMTreeModel() override; @@ -26,16 +33,30 @@ public: virtual GUI::ModelIndex index(int row, int column, const GUI::ModelIndex& parent = GUI::ModelIndex()) const override; virtual GUI::ModelIndex parent_index(const GUI::ModelIndex&) const override; - GUI::ModelIndex index_for_node(DOM::Node*) const; - private: - explicit DOMTreeModel(DOM::Document&); + explicit DOMTreeModel(JsonObject); - NonnullRefPtr m_document; + ALWAYS_INLINE JsonObject const* get_parent(const JsonObject& o) const + { + auto parent_node = m_dom_node_to_parent_map.get(&o); + VERIFY(parent_node.has_value()); + return *parent_node; + } + + ALWAYS_INLINE static JsonArray const* get_children(const JsonObject& o) + { + if (auto const* maybe_children = o.get_ptr("children"); maybe_children) + return &maybe_children->as_array(); + return nullptr; + } + + void map_dom_nodes_to_parent(JsonObject const* parent, JsonObject const* child); GUI::Icon m_document_icon; GUI::Icon m_element_icon; GUI::Icon m_text_icon; + JsonObject m_dom_tree; + HashMap m_dom_node_to_parent_map; }; }