소스 검색

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.
Andreas Kling 2 년 전
부모
커밋
e2c72922f6

+ 0 - 2
Userland/Libraries/LibWeb/DOM/Document.cpp

@@ -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);

+ 0 - 5
Userland/Libraries/LibWeb/DOM/Document.h

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

+ 32 - 22
Userland/Libraries/LibWeb/Layout/LayoutState.cpp

@@ -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 (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) {
     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();
             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);
-    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
 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());
     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()
 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) {
-        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());
         auto& node = const_cast<NodeWithStyleAndBoxModelMetrics&>(used_values.node());
 
 
         // Transfer box model metrics.
         // Transfer box model metrics.

+ 3 - 7
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
     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.

+ 0 - 2
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_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);
 }
 }

+ 0 - 4
Userland/Libraries/LibWeb/Layout/Node.h

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