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; }; }