| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> | 
					
						
							|  |  |  |  * Copyright (c) 2018-2020, Adam Hodgen <ant1441@gmail.com> | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * SPDX-License-Identifier: BSD-2-Clause | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "DOMTreeJSONModel.h"
 | 
					
						
							|  |  |  | #include <AK/JsonObject.h>
 | 
					
						
							|  |  |  | #include <AK/StringBuilder.h>
 | 
					
						
							|  |  |  | #include <ctype.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace Web { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | DOMTreeJSONModel::DOMTreeJSONModel(JsonObject dom_tree) | 
					
						
							| 
									
										
										
										
											2021-06-30 07:07:38 -04:00
										 |  |  |     : m_dom_tree(move(dom_tree)) | 
					
						
							| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  | { | 
					
						
							|  |  |  |     m_document_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-html.png")); | 
					
						
							|  |  |  |     m_element_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/inspector-object.png")); | 
					
						
							|  |  |  |     m_text_icon.set_bitmap_for_size(16, Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-unknown.png")); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | DOMTreeJSONModel::~DOMTreeJSONModel() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | GUI::ModelIndex DOMTreeJSONModel::index(int row, int column, const GUI::ModelIndex& parent) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!parent.is_valid()) { | 
					
						
							| 
									
										
										
										
											2021-06-30 08:35:07 -04:00
										 |  |  |         return create_index(row, column, &m_dom_tree); | 
					
						
							| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-30 08:35:07 -04:00
										 |  |  |     auto const& parent_node = *static_cast<JsonObject const*>(parent.internal_data()); | 
					
						
							| 
									
										
										
										
											2021-06-30 07:07:38 -04:00
										 |  |  |     auto const* children = get_children(parent_node); | 
					
						
							|  |  |  |     if (!children) | 
					
						
							| 
									
										
										
										
											2021-06-30 08:35:07 -04:00
										 |  |  |         return create_index(row, column, &m_dom_tree); | 
					
						
							| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-30 07:07:38 -04:00
										 |  |  |     auto const& child_node = children->at(row).as_object(); | 
					
						
							| 
									
										
										
										
											2021-06-30 08:35:07 -04:00
										 |  |  |     return create_index(row, column, &child_node); | 
					
						
							| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-30 08:35:07 -04:00
										 |  |  |     auto const& node = *static_cast<JsonObject const*>(index.internal_data()); | 
					
						
							| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  |     auto node_internal_id = get_internal_id(node); | 
					
						
							| 
									
										
										
										
											2021-06-30 07:07:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     auto const* parent_node = find_parent_of_child_with_internal_id(node_internal_id); | 
					
						
							|  |  |  |     if (!parent_node) | 
					
						
							| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  |         return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // If the parent is the root document, we know it has index 0, 0
 | 
					
						
							| 
									
										
										
										
											2021-06-30 07:07:38 -04:00
										 |  |  |     auto parent_node_internal_id = get_internal_id(*parent_node); | 
					
						
							| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  |     if (parent_node_internal_id == get_internal_id(m_dom_tree)) { | 
					
						
							| 
									
										
										
										
											2021-06-30 08:35:07 -04:00
										 |  |  |         return create_index(0, 0, parent_node); | 
					
						
							| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Otherwise, we need to find the grandparent, to find the index of parent within that
 | 
					
						
							| 
									
										
										
										
											2021-06-30 07:07:38 -04:00
										 |  |  |     auto const* grandparent_node = find_parent_of_child_with_internal_id(parent_node_internal_id); | 
					
						
							|  |  |  |     VERIFY(grandparent_node); | 
					
						
							| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-30 07:07:38 -04:00
										 |  |  |     auto const* grandparent_children = get_children(*grandparent_node); | 
					
						
							|  |  |  |     if (!grandparent_children) | 
					
						
							| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  |         return {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-30 07:07:38 -04:00
										 |  |  |     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(); | 
					
						
							| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  |         if (get_internal_id(child) == parent_node_internal_id) | 
					
						
							| 
									
										
										
										
											2021-06-30 08:35:07 -04:00
										 |  |  |             return create_index(grandparent_child_index, 0, parent_node); | 
					
						
							| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-06-30 07:07:38 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  |     return {}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | int DOMTreeJSONModel::row_count(const GUI::ModelIndex& index) const | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     if (!index.is_valid()) | 
					
						
							|  |  |  |         return 1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-30 08:35:07 -04:00
										 |  |  |     auto const& node = *static_cast<JsonObject const*>(index.internal_data()); | 
					
						
							| 
									
										
										
										
											2021-06-30 07:07:38 -04:00
										 |  |  |     auto const* children = get_children(node); | 
					
						
							|  |  |  |     return children ? children->size() : 0; | 
					
						
							| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2021-06-30 08:35:07 -04:00
										 |  |  |     auto const& node = *static_cast<JsonObject const*>(index.internal_data()); | 
					
						
							| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  |     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(); | 
					
						
							| 
									
										
										
										
											2021-06-29 23:11:09 +02:00
										 |  |  |             attributes.for_each_member([&builder](auto& name, JsonValue const& value) { | 
					
						
							| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  |                 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::update() | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  |     did_update(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-30 07:07:38 -04:00
										 |  |  | JsonObject const* DOMTreeJSONModel::find_parent_of_child_with_internal_id(JsonObject const& node, size_t internal_id) const | 
					
						
							| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2021-06-30 07:07:38 -04:00
										 |  |  |     auto const* children = get_children(node); | 
					
						
							|  |  |  |     if (!children) | 
					
						
							|  |  |  |         return nullptr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for (size_t i = 0; i < children->size(); ++i) { | 
					
						
							|  |  |  |         auto const& child = children->at(i).as_object(); | 
					
						
							| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         auto child_internal_id = get_internal_id(child); | 
					
						
							|  |  |  |         if (child_internal_id == internal_id) | 
					
						
							| 
									
										
										
										
											2021-06-30 07:07:38 -04:00
										 |  |  |             return &node; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (auto const* maybe_node = find_parent_of_child_with_internal_id(child, internal_id); maybe_node) | 
					
						
							| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  |             return maybe_node; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-06-30 07:07:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return nullptr; | 
					
						
							| 
									
										
										
										
											2021-06-07 22:21:16 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } |