mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 06:27:45 +00:00
LibWeb: Make LayoutState use HashMap instead of potentially huge Vector
Before this change, LayoutState essentially had a Vector<UsedValues*> resized to the exact number of layout nodes in the current document. When a nested layout is performed (to calculate the intrinsic size of something), we make a new LayoutState with its own Vector. If an entry is missing in a nested LayoutState, we check the parent chain all the way up to the root. Because each nested LayoutState had to allocate a new Vector with space for all layout nodes, this could get really nasty on very large pages (such as the ECMA262 specification). This patch replaces the Vector with a HashMap. There's now a small cost to lookups, but what we get in return is the ability to handle huge layout trees without spending eternity in page faults.
This commit is contained in:
parent
e83681ee34
commit
e2c72922f6
6 changed files with 35 additions and 42 deletions
|
@ -859,13 +859,11 @@ void Document::update_layout()
|
||||||
auto viewport_rect = browsing_context()->viewport_rect();
|
auto viewport_rect = browsing_context()->viewport_rect();
|
||||||
|
|
||||||
if (!m_layout_root) {
|
if (!m_layout_root) {
|
||||||
m_next_layout_node_serial_id = 0;
|
|
||||||
Layout::TreeBuilder tree_builder;
|
Layout::TreeBuilder tree_builder;
|
||||||
m_layout_root = verify_cast<Layout::Viewport>(*tree_builder.build(*this));
|
m_layout_root = verify_cast<Layout::Viewport>(*tree_builder.build(*this));
|
||||||
}
|
}
|
||||||
|
|
||||||
Layout::LayoutState layout_state;
|
Layout::LayoutState layout_state;
|
||||||
layout_state.used_values_per_layout_node.resize(layout_node_count());
|
|
||||||
|
|
||||||
{
|
{
|
||||||
Layout::BlockFormattingContext root_formatting_context(layout_state, *m_layout_root, nullptr);
|
Layout::BlockFormattingContext root_formatting_context(layout_state, *m_layout_root, nullptr);
|
||||||
|
|
|
@ -97,9 +97,6 @@ public:
|
||||||
|
|
||||||
JS::GCPtr<Selection::Selection> get_selection() const;
|
JS::GCPtr<Selection::Selection> get_selection() const;
|
||||||
|
|
||||||
size_t next_layout_node_serial_id(Badge<Layout::Node>) { return m_next_layout_node_serial_id++; }
|
|
||||||
size_t layout_node_count() const { return m_next_layout_node_serial_id; }
|
|
||||||
|
|
||||||
DeprecatedString cookie(Cookie::Source = Cookie::Source::NonHttp);
|
DeprecatedString cookie(Cookie::Source = Cookie::Source::NonHttp);
|
||||||
void set_cookie(DeprecatedString const&, Cookie::Source = Cookie::Source::NonHttp);
|
void set_cookie(DeprecatedString const&, Cookie::Source = Cookie::Source::NonHttp);
|
||||||
|
|
||||||
|
@ -493,8 +490,6 @@ private:
|
||||||
|
|
||||||
WebIDL::ExceptionOr<void> run_the_document_write_steps(DeprecatedString);
|
WebIDL::ExceptionOr<void> run_the_document_write_steps(DeprecatedString);
|
||||||
|
|
||||||
size_t m_next_layout_node_serial_id { 0 };
|
|
||||||
|
|
||||||
OwnPtr<CSS::StyleComputer> m_style_computer;
|
OwnPtr<CSS::StyleComputer> m_style_computer;
|
||||||
JS::GCPtr<CSS::StyleSheetList> m_style_sheets;
|
JS::GCPtr<CSS::StyleSheetList> m_style_sheets;
|
||||||
JS::GCPtr<Node> m_hovered_node;
|
JS::GCPtr<Node> m_hovered_node;
|
||||||
|
|
|
@ -11,44 +11,56 @@
|
||||||
|
|
||||||
namespace Web::Layout {
|
namespace Web::Layout {
|
||||||
|
|
||||||
|
LayoutState::LayoutState(LayoutState const* parent)
|
||||||
|
: m_parent(parent)
|
||||||
|
, m_root(find_root())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
LayoutState::~LayoutState()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
LayoutState::UsedValues& LayoutState::get_mutable(NodeWithStyleAndBoxModelMetrics const& box)
|
LayoutState::UsedValues& LayoutState::get_mutable(NodeWithStyleAndBoxModelMetrics const& box)
|
||||||
{
|
{
|
||||||
auto serial_id = box.serial_id();
|
if (auto* used_values = used_values_per_layout_node.get(&box).value_or(nullptr))
|
||||||
if (used_values_per_layout_node[serial_id])
|
return *used_values;
|
||||||
return *used_values_per_layout_node[serial_id];
|
|
||||||
|
|
||||||
for (auto const* ancestor = m_parent; ancestor; ancestor = ancestor->m_parent) {
|
for (auto const* ancestor = m_parent; ancestor; ancestor = ancestor->m_parent) {
|
||||||
if (ancestor->used_values_per_layout_node[serial_id]) {
|
if (auto* ancestor_used_values = ancestor->used_values_per_layout_node.get(&box).value_or(nullptr)) {
|
||||||
auto cow_used_values = adopt_own(*new UsedValues(*ancestor->used_values_per_layout_node[serial_id]));
|
auto cow_used_values = adopt_own(*new UsedValues(*ancestor_used_values));
|
||||||
auto* cow_used_values_ptr = cow_used_values.ptr();
|
auto* cow_used_values_ptr = cow_used_values.ptr();
|
||||||
used_values_per_layout_node[serial_id] = move(cow_used_values);
|
used_values_per_layout_node.set(&box, move(cow_used_values));
|
||||||
return *cow_used_values_ptr;
|
return *cow_used_values_ptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const* containing_block_used_values = box.is_viewport() ? nullptr : &get(*box.containing_block());
|
auto const* containing_block_used_values = box.is_viewport() ? nullptr : &get(*box.containing_block());
|
||||||
|
|
||||||
used_values_per_layout_node[serial_id] = adopt_own(*new UsedValues);
|
auto new_used_values = adopt_own(*new UsedValues);
|
||||||
used_values_per_layout_node[serial_id]->set_node(const_cast<NodeWithStyleAndBoxModelMetrics&>(box), containing_block_used_values);
|
auto* new_used_values_ptr = new_used_values.ptr();
|
||||||
return *used_values_per_layout_node[serial_id];
|
new_used_values->set_node(const_cast<NodeWithStyleAndBoxModelMetrics&>(box), containing_block_used_values);
|
||||||
|
used_values_per_layout_node.set(&box, move(new_used_values));
|
||||||
|
return *new_used_values_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
LayoutState::UsedValues const& LayoutState::get(NodeWithStyleAndBoxModelMetrics const& box) const
|
LayoutState::UsedValues const& LayoutState::get(NodeWithStyleAndBoxModelMetrics const& box) const
|
||||||
{
|
{
|
||||||
auto serial_id = box.serial_id();
|
if (auto const* used_values = used_values_per_layout_node.get(&box).value_or(nullptr))
|
||||||
if (used_values_per_layout_node[serial_id])
|
return *used_values;
|
||||||
return *used_values_per_layout_node[serial_id];
|
|
||||||
|
|
||||||
for (auto* ancestor = m_parent; ancestor; ancestor = ancestor->m_parent) {
|
for (auto const* ancestor = m_parent; ancestor; ancestor = ancestor->m_parent) {
|
||||||
if (ancestor->used_values_per_layout_node[serial_id])
|
if (auto const* ancestor_used_values = ancestor->used_values_per_layout_node.get(&box).value_or(nullptr))
|
||||||
return *ancestor->used_values_per_layout_node[serial_id];
|
return *ancestor_used_values;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const* containing_block_used_values = box.is_viewport() ? nullptr : &get(*box.containing_block());
|
auto const* containing_block_used_values = box.is_viewport() ? nullptr : &get(*box.containing_block());
|
||||||
|
|
||||||
const_cast<LayoutState*>(this)->used_values_per_layout_node[serial_id] = adopt_own(*new UsedValues);
|
auto new_used_values = adopt_own(*new UsedValues);
|
||||||
const_cast<LayoutState*>(this)->used_values_per_layout_node[serial_id]->set_node(const_cast<NodeWithStyleAndBoxModelMetrics&>(box), containing_block_used_values);
|
auto* new_used_values_ptr = new_used_values.ptr();
|
||||||
return *used_values_per_layout_node[serial_id];
|
new_used_values->set_node(const_cast<NodeWithStyleAndBoxModelMetrics&>(box), containing_block_used_values);
|
||||||
|
const_cast<LayoutState*>(this)->used_values_per_layout_node.set(&box, move(new_used_values));
|
||||||
|
return *new_used_values_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LayoutState::commit()
|
void LayoutState::commit()
|
||||||
|
@ -58,10 +70,8 @@ void LayoutState::commit()
|
||||||
|
|
||||||
HashTable<Layout::TextNode*> text_nodes;
|
HashTable<Layout::TextNode*> text_nodes;
|
||||||
|
|
||||||
for (auto& used_values_ptr : used_values_per_layout_node) {
|
for (auto& it : used_values_per_layout_node) {
|
||||||
if (!used_values_ptr)
|
auto& used_values = *it.value;
|
||||||
continue;
|
|
||||||
auto& used_values = *used_values_ptr;
|
|
||||||
auto& node = const_cast<NodeWithStyleAndBoxModelMetrics&>(used_values.node());
|
auto& node = const_cast<NodeWithStyleAndBoxModelMetrics&>(used_values.node());
|
||||||
|
|
||||||
// Transfer box model metrics.
|
// Transfer box model metrics.
|
||||||
|
|
|
@ -29,12 +29,8 @@ struct LayoutState {
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit LayoutState(LayoutState const* parent)
|
explicit LayoutState(LayoutState const* parent);
|
||||||
: m_parent(parent)
|
~LayoutState();
|
||||||
, m_root(find_root())
|
|
||||||
{
|
|
||||||
used_values_per_layout_node.resize(m_root.used_values_per_layout_node.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
LayoutState const& find_root() const
|
LayoutState const& find_root() const
|
||||||
{
|
{
|
||||||
|
@ -153,7 +149,7 @@ struct LayoutState {
|
||||||
// NOTE: get() will not CoW the UsedValues.
|
// NOTE: get() will not CoW the UsedValues.
|
||||||
UsedValues const& get(NodeWithStyleAndBoxModelMetrics const&) const;
|
UsedValues const& get(NodeWithStyleAndBoxModelMetrics const&) const;
|
||||||
|
|
||||||
Vector<OwnPtr<UsedValues>> used_values_per_layout_node;
|
HashMap<Layout::Node const*, NonnullOwnPtr<UsedValues>> used_values_per_layout_node;
|
||||||
|
|
||||||
// We cache intrinsic sizes once determined, as they will not change over the course of a full layout.
|
// We cache intrinsic sizes once determined, as they will not change over the course of a full layout.
|
||||||
// This avoids computing them several times while performing flex layout.
|
// This avoids computing them several times while performing flex layout.
|
||||||
|
|
|
@ -32,8 +32,6 @@ Node::Node(DOM::Document& document, DOM::Node* node)
|
||||||
, m_browsing_context(*document.browsing_context())
|
, m_browsing_context(*document.browsing_context())
|
||||||
, m_anonymous(node == nullptr)
|
, m_anonymous(node == nullptr)
|
||||||
{
|
{
|
||||||
m_serial_id = document.next_layout_node_serial_id({});
|
|
||||||
|
|
||||||
if (node)
|
if (node)
|
||||||
node->set_layout_node({}, *this);
|
node->set_layout_node({}, *this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,8 +42,6 @@ class Node
|
||||||
public:
|
public:
|
||||||
virtual ~Node();
|
virtual ~Node();
|
||||||
|
|
||||||
size_t serial_id() const { return m_serial_id; }
|
|
||||||
|
|
||||||
bool is_anonymous() const;
|
bool is_anonymous() const;
|
||||||
DOM::Node const* dom_node() const;
|
DOM::Node const* dom_node() const;
|
||||||
DOM::Node* dom_node();
|
DOM::Node* dom_node();
|
||||||
|
@ -165,8 +163,6 @@ private:
|
||||||
|
|
||||||
JS::NonnullGCPtr<HTML::BrowsingContext> m_browsing_context;
|
JS::NonnullGCPtr<HTML::BrowsingContext> m_browsing_context;
|
||||||
|
|
||||||
size_t m_serial_id { 0 };
|
|
||||||
|
|
||||||
bool m_anonymous { false };
|
bool m_anonymous { false };
|
||||||
bool m_has_style { false };
|
bool m_has_style { false };
|
||||||
bool m_visible { true };
|
bool m_visible { true };
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue