ladybird/Userland/Libraries/LibWeb/DOMTreeJSONModel.cpp
sin-ack ca2c81251a Everywhere: Replace Model::update() with Model::invalidate()
Most of the models were just calling did_update anyway, which is
pointless since it can be unified to the base Model class. Instead, code
calling update() will now call invalidate(), which functions identically
and is more obvious in what it does.

Additionally, a default implementation is provided, which removes the
need to add empty implementations of update() for each model subclass.

Co-Authored-By: Ali Mohammad Pur <ali.mpfard@gmail.com>
2021-08-06 19:14:31 +02:00

170 lines
5.3 KiB
C++

/*
* 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)
: 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<JsonObject const*>(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<JsonObject const*>(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<JsonObject const*>(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<JsonObject const*>(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);
});
}
}