diff options
author | Andreas Kling <kling@serenityos.org> | 2023-05-23 07:47:37 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2023-05-23 09:24:08 +0200 |
commit | e2c72922f624ffe594a847d0edaa50ca227677ea (patch) | |
tree | ad858cb6c49bfdff8e9e8c14f9e97648333d275b /Userland/Libraries | |
parent | e83681ee34a28861ac2c346580fe6d1959a80361 (diff) | |
download | serenity-e2c72922f624ffe594a847d0edaa50ca227677ea.zip |
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.
Diffstat (limited to 'Userland/Libraries')
-rw-r--r-- | Userland/Libraries/LibWeb/DOM/Document.cpp | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/DOM/Document.h | 5 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Layout/LayoutState.cpp | 54 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Layout/LayoutState.h | 10 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Layout/Node.cpp | 2 | ||||
-rw-r--r-- | Userland/Libraries/LibWeb/Layout/Node.h | 4 |
6 files changed, 35 insertions, 42 deletions
diff --git a/Userland/Libraries/LibWeb/DOM/Document.cpp b/Userland/Libraries/LibWeb/DOM/Document.cpp index ab9ee2df27..d2c5312f47 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.cpp +++ b/Userland/Libraries/LibWeb/DOM/Document.cpp @@ -859,13 +859,11 @@ void Document::update_layout() auto viewport_rect = browsing_context()->viewport_rect(); if (!m_layout_root) { - m_next_layout_node_serial_id = 0; Layout::TreeBuilder tree_builder; m_layout_root = verify_cast<Layout::Viewport>(*tree_builder.build(*this)); } 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); diff --git a/Userland/Libraries/LibWeb/DOM/Document.h b/Userland/Libraries/LibWeb/DOM/Document.h index c2e8f621f0..41563b822d 100644 --- a/Userland/Libraries/LibWeb/DOM/Document.h +++ b/Userland/Libraries/LibWeb/DOM/Document.h @@ -97,9 +97,6 @@ public: 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); void set_cookie(DeprecatedString const&, Cookie::Source = Cookie::Source::NonHttp); @@ -493,8 +490,6 @@ private: WebIDL::ExceptionOr<void> run_the_document_write_steps(DeprecatedString); - size_t m_next_layout_node_serial_id { 0 }; - OwnPtr<CSS::StyleComputer> m_style_computer; JS::GCPtr<CSS::StyleSheetList> m_style_sheets; JS::GCPtr<Node> m_hovered_node; diff --git a/Userland/Libraries/LibWeb/Layout/LayoutState.cpp b/Userland/Libraries/LibWeb/Layout/LayoutState.cpp index 46a58bfb37..cb702a51de 100644 --- a/Userland/Libraries/LibWeb/Layout/LayoutState.cpp +++ b/Userland/Libraries/LibWeb/Layout/LayoutState.cpp @@ -11,44 +11,56 @@ 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) { - auto serial_id = box.serial_id(); - if (used_values_per_layout_node[serial_id]) - return *used_values_per_layout_node[serial_id]; + if (auto* used_values = used_values_per_layout_node.get(&box).value_or(nullptr)) + return *used_values; for (auto const* ancestor = m_parent; ancestor; ancestor = ancestor->m_parent) { - if (ancestor->used_values_per_layout_node[serial_id]) { - auto cow_used_values = adopt_own(*new UsedValues(*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)); 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; } } 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); - used_values_per_layout_node[serial_id]->set_node(const_cast<NodeWithStyleAndBoxModelMetrics&>(box), containing_block_used_values); - return *used_values_per_layout_node[serial_id]; + auto new_used_values = adopt_own(*new UsedValues); + auto* new_used_values_ptr = new_used_values.ptr(); + 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 { - auto serial_id = box.serial_id(); - if (used_values_per_layout_node[serial_id]) - return *used_values_per_layout_node[serial_id]; + if (auto const* used_values = used_values_per_layout_node.get(&box).value_or(nullptr)) + return *used_values; - for (auto* ancestor = m_parent; ancestor; ancestor = ancestor->m_parent) { - if (ancestor->used_values_per_layout_node[serial_id]) - return *ancestor->used_values_per_layout_node[serial_id]; + for (auto const* ancestor = m_parent; ancestor; ancestor = ancestor->m_parent) { + if (auto const* ancestor_used_values = ancestor->used_values_per_layout_node.get(&box).value_or(nullptr)) + return *ancestor_used_values; } 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); - const_cast<LayoutState*>(this)->used_values_per_layout_node[serial_id]->set_node(const_cast<NodeWithStyleAndBoxModelMetrics&>(box), containing_block_used_values); - return *used_values_per_layout_node[serial_id]; + auto new_used_values = adopt_own(*new UsedValues); + auto* new_used_values_ptr = new_used_values.ptr(); + 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() @@ -58,10 +70,8 @@ void LayoutState::commit() HashTable<Layout::TextNode*> text_nodes; - for (auto& used_values_ptr : used_values_per_layout_node) { - if (!used_values_ptr) - continue; - auto& used_values = *used_values_ptr; + for (auto& it : used_values_per_layout_node) { + auto& used_values = *it.value; auto& node = const_cast<NodeWithStyleAndBoxModelMetrics&>(used_values.node()); // Transfer box model metrics. diff --git a/Userland/Libraries/LibWeb/Layout/LayoutState.h b/Userland/Libraries/LibWeb/Layout/LayoutState.h index e3d7fe94b6..96db5ad32a 100644 --- a/Userland/Libraries/LibWeb/Layout/LayoutState.h +++ b/Userland/Libraries/LibWeb/Layout/LayoutState.h @@ -29,12 +29,8 @@ struct LayoutState { { } - explicit LayoutState(LayoutState const* parent) - : m_parent(parent) - , m_root(find_root()) - { - used_values_per_layout_node.resize(m_root.used_values_per_layout_node.size()); - } + explicit LayoutState(LayoutState const* parent); + ~LayoutState(); LayoutState const& find_root() const { @@ -153,7 +149,7 @@ struct LayoutState { // NOTE: get() will not CoW the UsedValues. 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. // This avoids computing them several times while performing flex layout. diff --git a/Userland/Libraries/LibWeb/Layout/Node.cpp b/Userland/Libraries/LibWeb/Layout/Node.cpp index f2528fdca2..fc9c79f412 100644 --- a/Userland/Libraries/LibWeb/Layout/Node.cpp +++ b/Userland/Libraries/LibWeb/Layout/Node.cpp @@ -32,8 +32,6 @@ Node::Node(DOM::Document& document, DOM::Node* node) , m_browsing_context(*document.browsing_context()) , m_anonymous(node == nullptr) { - m_serial_id = document.next_layout_node_serial_id({}); - if (node) node->set_layout_node({}, *this); } diff --git a/Userland/Libraries/LibWeb/Layout/Node.h b/Userland/Libraries/LibWeb/Layout/Node.h index f5b69f0d92..70b1ebd267 100644 --- a/Userland/Libraries/LibWeb/Layout/Node.h +++ b/Userland/Libraries/LibWeb/Layout/Node.h @@ -42,8 +42,6 @@ class Node public: virtual ~Node(); - size_t serial_id() const { return m_serial_id; } - bool is_anonymous() const; DOM::Node const* dom_node() const; DOM::Node* dom_node(); @@ -165,8 +163,6 @@ private: JS::NonnullGCPtr<HTML::BrowsingContext> m_browsing_context; - size_t m_serial_id { 0 }; - bool m_anonymous { false }; bool m_has_style { false }; bool m_visible { true }; |