Browse Source

LibWeb: Make BrowsingContext GC-allocated

(And BrowsingContextGroup had to come along for the ride as well.)
This solves a number of nasty reference cycles between browsing
contexts, history items, and their documents.
Andreas Kling 2 years ago
parent
commit
83c5ff57d8

+ 7 - 5
Userland/Libraries/LibWeb/DOM/Document.cpp

@@ -79,7 +79,7 @@
 namespace Web::DOM {
 namespace Web::DOM {
 
 
 // https://html.spec.whatwg.org/multipage/origin.html#obtain-browsing-context-navigation
 // https://html.spec.whatwg.org/multipage/origin.html#obtain-browsing-context-navigation
-static NonnullRefPtr<HTML::BrowsingContext> obtain_a_browsing_context_to_use_for_a_navigation_response(
+static JS::NonnullGCPtr<HTML::BrowsingContext> obtain_a_browsing_context_to_use_for_a_navigation_response(
     HTML::BrowsingContext& browsing_context,
     HTML::BrowsingContext& browsing_context,
     HTML::SandboxingFlagSet sandbox_flags,
     HTML::SandboxingFlagSet sandbox_flags,
     HTML::CrossOriginOpenerPolicy navigation_coop,
     HTML::CrossOriginOpenerPolicy navigation_coop,
@@ -130,7 +130,7 @@ JS::NonnullGCPtr<Document> Document::create_and_initialize(Type type, String con
     //    given navigationParams's browsing context, navigationParams's final sandboxing flag set,
     //    given navigationParams's browsing context, navigationParams's final sandboxing flag set,
     //    navigationParams's cross-origin opener policy, and navigationParams's COOP enforcement result.
     //    navigationParams's cross-origin opener policy, and navigationParams's COOP enforcement result.
     auto browsing_context = obtain_a_browsing_context_to_use_for_a_navigation_response(
     auto browsing_context = obtain_a_browsing_context_to_use_for_a_navigation_response(
-        navigation_params.browsing_context,
+        *navigation_params.browsing_context,
         navigation_params.final_sandboxing_flag_set,
         navigation_params.final_sandboxing_flag_set,
         navigation_params.cross_origin_opener_policy,
         navigation_params.cross_origin_opener_policy,
         navigation_params.coop_enforcement_result);
         navigation_params.coop_enforcement_result);
@@ -330,6 +330,8 @@ void Document::visit_edges(Cell::Visitor& visitor)
     visitor.visit(m_pending_parsing_blocking_script.ptr());
     visitor.visit(m_pending_parsing_blocking_script.ptr());
     visitor.visit(m_history.ptr());
     visitor.visit(m_history.ptr());
 
 
+    visitor.visit(m_browsing_context);
+
     visitor.visit(m_applets);
     visitor.visit(m_applets);
     visitor.visit(m_anchors);
     visitor.visit(m_anchors);
     visitor.visit(m_images);
     visitor.visit(m_images);
@@ -2051,10 +2053,10 @@ HTML::PolicyContainer Document::policy_container() const
 }
 }
 
 
 // https://html.spec.whatwg.org/multipage/browsers.html#list-of-the-descendant-browsing-contexts
 // https://html.spec.whatwg.org/multipage/browsers.html#list-of-the-descendant-browsing-contexts
-Vector<NonnullRefPtr<HTML::BrowsingContext>> Document::list_of_descendant_browsing_contexts() const
+Vector<JS::Handle<HTML::BrowsingContext>> Document::list_of_descendant_browsing_contexts() const
 {
 {
     // 1. Let list be an empty list.
     // 1. Let list be an empty list.
-    Vector<NonnullRefPtr<HTML::BrowsingContext>> list;
+    Vector<JS::Handle<HTML::BrowsingContext>> list;
 
 
     // 2. For each browsing context container container,
     // 2. For each browsing context container container,
     //    whose nested browsing context is non-null and whose shadow-including root is d, in shadow-including tree order:
     //    whose nested browsing context is non-null and whose shadow-including root is d, in shadow-including tree order:
@@ -2063,7 +2065,7 @@ Vector<NonnullRefPtr<HTML::BrowsingContext>> Document::list_of_descendant_browsi
     //       of this document's browsing context.
     //       of this document's browsing context.
     if (browsing_context()) {
     if (browsing_context()) {
         browsing_context()->for_each_in_subtree([&](auto& context) {
         browsing_context()->for_each_in_subtree([&](auto& context) {
-            list.append(context);
+            list.append(JS::make_handle(const_cast<HTML::BrowsingContext&>(context)));
             return IterationDecision::Continue;
             return IterationDecision::Continue;
         });
         });
     }
     }

+ 1 - 1
Userland/Libraries/LibWeb/DOM/Document.h

@@ -416,7 +416,7 @@ public:
     HTML::PolicyContainer policy_container() const;
     HTML::PolicyContainer policy_container() const;
 
 
     // https://html.spec.whatwg.org/multipage/browsers.html#list-of-the-descendant-browsing-contexts
     // https://html.spec.whatwg.org/multipage/browsers.html#list-of-the-descendant-browsing-contexts
-    Vector<NonnullRefPtr<HTML::BrowsingContext>> list_of_descendant_browsing_contexts() const;
+    Vector<JS::Handle<HTML::BrowsingContext>> list_of_descendant_browsing_contexts() const;
 
 
     // https://html.spec.whatwg.org/multipage/window-object.html#discard-a-document
     // https://html.spec.whatwg.org/multipage/window-object.html#discard-a-document
     void discard();
     void discard();

+ 80 - 12
Userland/Libraries/LibWeb/HTML/BrowsingContext.cpp

@@ -92,21 +92,21 @@ HTML::Origin determine_the_origin(BrowsingContext const& browsing_context, Optio
 }
 }
 
 
 // https://html.spec.whatwg.org/multipage/browsers.html#creating-a-new-top-level-browsing-context
 // https://html.spec.whatwg.org/multipage/browsers.html#creating-a-new-top-level-browsing-context
-NonnullRefPtr<BrowsingContext> BrowsingContext::create_a_new_top_level_browsing_context(Web::Page& page)
+JS::NonnullGCPtr<BrowsingContext> BrowsingContext::create_a_new_top_level_browsing_context(Web::Page& page)
 {
 {
     // 1. Let group be the result of creating a new browsing context group.
     // 1. Let group be the result of creating a new browsing context group.
     auto group = BrowsingContextGroup::create_a_new_browsing_context_group(page);
     auto group = BrowsingContextGroup::create_a_new_browsing_context_group(page);
 
 
     // 2. Return group's browsing context set[0].
     // 2. Return group's browsing context set[0].
-    return *group->browsing_context_set().begin();
+    return *(*group->browsing_context_set().begin());
 }
 }
 
 
 // https://html.spec.whatwg.org/multipage/browsers.html#creating-a-new-browsing-context
 // https://html.spec.whatwg.org/multipage/browsers.html#creating-a-new-browsing-context
-NonnullRefPtr<BrowsingContext> BrowsingContext::create_a_new_browsing_context(Page& page, JS::GCPtr<DOM::Document> creator, JS::GCPtr<DOM::Element> embedder, BrowsingContextGroup&)
+JS::NonnullGCPtr<BrowsingContext> BrowsingContext::create_a_new_browsing_context(Page& page, JS::GCPtr<DOM::Document> creator, JS::GCPtr<DOM::Element> embedder, BrowsingContextGroup&)
 {
 {
     // 1. Let browsingContext be a new browsing context.
     // 1. Let browsingContext be a new browsing context.
     BrowsingContextContainer* container = (embedder && is<BrowsingContextContainer>(*embedder)) ? static_cast<BrowsingContextContainer*>(embedder.ptr()) : nullptr;
     BrowsingContextContainer* container = (embedder && is<BrowsingContextContainer>(*embedder)) ? static_cast<BrowsingContextContainer*>(embedder.ptr()) : nullptr;
-    auto browsing_context = adopt_ref(*new BrowsingContext(page, container));
+    auto browsing_context = Bindings::main_thread_vm().heap().allocate_without_realm<BrowsingContext>(page, container);
 
 
     // 2. Let unsafeContextCreationTime be the unsafe shared current time.
     // 2. Let unsafeContextCreationTime be the unsafe shared current time.
     [[maybe_unused]] auto unsafe_context_creation_time = HighResolutionTime::unsafe_shared_current_time();
     [[maybe_unused]] auto unsafe_context_creation_time = HighResolutionTime::unsafe_shared_current_time();
@@ -125,7 +125,7 @@ NonnullRefPtr<BrowsingContext> BrowsingContext::create_a_new_browsing_context(Pa
     SandboxingFlagSet sandbox_flags;
     SandboxingFlagSet sandbox_flags;
 
 
     // 5. Let origin be the result of determining the origin given browsingContext, about:blank, sandboxFlags, and browsingContext's creator origin.
     // 5. Let origin be the result of determining the origin given browsingContext, about:blank, sandboxFlags, and browsingContext's creator origin.
-    auto origin = determine_the_origin(browsing_context, AK::URL("about:blank"), sandbox_flags, browsing_context->m_creator_origin);
+    auto origin = determine_the_origin(*browsing_context, AK::URL("about:blank"), sandbox_flags, browsing_context->m_creator_origin);
 
 
     // FIXME: 6. Let permissionsPolicy be the result of creating a permissions policy given browsingContext and origin. [PERMISSIONSPOLICY]
     // FIXME: 6. Let permissionsPolicy be the result of creating a permissions policy given browsingContext and origin. [PERMISSIONSPOLICY]
 
 
@@ -240,7 +240,7 @@ NonnullRefPtr<BrowsingContext> BrowsingContext::create_a_new_browsing_context(Pa
     document->completely_finish_loading();
     document->completely_finish_loading();
 
 
     // 24. Return browsingContext.
     // 24. Return browsingContext.
-    return browsing_context;
+    return *browsing_context;
 }
 }
 
 
 BrowsingContext::BrowsingContext(Page& page, HTML::BrowsingContextContainer* container)
 BrowsingContext::BrowsingContext(Page& page, HTML::BrowsingContextContainer* container)
@@ -261,6 +261,22 @@ BrowsingContext::BrowsingContext(Page& page, HTML::BrowsingContextContainer* con
 
 
 BrowsingContext::~BrowsingContext() = default;
 BrowsingContext::~BrowsingContext() = default;
 
 
+void BrowsingContext::visit_edges(Cell::Visitor& visitor)
+{
+    Base::visit_edges(visitor);
+
+    for (auto& entry : m_session_history)
+        visitor.visit(entry.document);
+    visitor.visit(m_container);
+    visitor.visit(m_window_proxy);
+    visitor.visit(m_group);
+    visitor.visit(m_parent);
+    visitor.visit(m_first_child);
+    visitor.visit(m_last_child);
+    visitor.visit(m_next_sibling);
+    visitor.visit(m_previous_sibling);
+}
+
 void BrowsingContext::did_edit(Badge<EditEventHandler>)
 void BrowsingContext::did_edit(Badge<EditEventHandler>)
 {
 {
     reset_cursor_blink_cycle();
     reset_cursor_blink_cycle();
@@ -446,7 +462,7 @@ Gfx::IntRect BrowsingContext::to_top_level_rect(Gfx::IntRect const& a_rect)
 Gfx::IntPoint BrowsingContext::to_top_level_position(Gfx::IntPoint const& a_position)
 Gfx::IntPoint BrowsingContext::to_top_level_position(Gfx::IntPoint const& a_position)
 {
 {
     auto position = a_position;
     auto position = a_position;
-    for (auto* ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
+    for (auto ancestor = parent(); ancestor; ancestor = ancestor->parent()) {
         if (ancestor->is_top_level())
         if (ancestor->is_top_level())
             break;
             break;
         if (!ancestor->container())
         if (!ancestor->container())
@@ -657,7 +673,7 @@ BrowsingContext* BrowsingContext::choose_a_browsing_context(StringView name, boo
     // name, a browsing context current, and a boolean noopener are as follows:
     // name, a browsing context current, and a boolean noopener are as follows:
 
 
     // 1. Let chosen be null.
     // 1. Let chosen be null.
-    BrowsingContext* chosen = nullptr;
+    JS::GCPtr<BrowsingContext> chosen = nullptr;
 
 
     // FIXME: 2. Let windowType be "existing or none".
     // FIXME: 2. Let windowType be "existing or none".
 
 
@@ -672,7 +688,7 @@ BrowsingContext* BrowsingContext::choose_a_browsing_context(StringView name, boo
     // set chosen to current's parent browsing context, if any, and current
     // set chosen to current's parent browsing context, if any, and current
     // otherwise.
     // otherwise.
     if (name.equals_ignoring_case("_parent"sv)) {
     if (name.equals_ignoring_case("_parent"sv)) {
-        if (auto* parent = this->parent())
+        if (auto parent = this->parent())
             chosen = parent;
             chosen = parent;
         else
         else
             chosen = this;
             chosen = this;
@@ -872,13 +888,13 @@ void BrowsingContext::remove()
     VERIFY(group());
     VERIFY(group());
 
 
     // 2. Let group be browsingContext's group.
     // 2. Let group be browsingContext's group.
-    NonnullRefPtr<BrowsingContextGroup> group = *this->group();
+    JS::NonnullGCPtr<BrowsingContextGroup> group = *this->group();
 
 
     // 3. Set browsingContext's group to null.
     // 3. Set browsingContext's group to null.
     set_group(nullptr);
     set_group(nullptr);
 
 
     // 4. Remove browsingContext from group's browsing context set.
     // 4. Remove browsingContext from group's browsing context set.
-    group->browsing_context_set().remove(*this);
+    group->browsing_context_set().remove(this);
 
 
     // 5. If group's browsing context set is empty, then remove group from the user agent's browsing context group set.
     // 5. If group's browsing context set is empty, then remove group from the user agent's browsing context group set.
     // NOTE: This is done by ~BrowsingContextGroup() when the refcount reaches 0.
     // NOTE: This is done by ~BrowsingContextGroup() when the refcount reaches 0.
@@ -1281,7 +1297,7 @@ Vector<JS::Handle<DOM::Document>> BrowsingContext::document_family() const
     for (auto& entry : m_session_history) {
     for (auto& entry : m_session_history) {
         if (!entry.document)
         if (!entry.document)
             continue;
             continue;
-        if (documents.set(entry.document.ptr()) == AK::HashSetResult::ReplacedExistingEntry)
+        if (documents.set(const_cast<DOM::Document*>(entry.document.ptr())) == AK::HashSetResult::ReplacedExistingEntry)
             continue;
             continue;
         for (auto& context : entry.document->list_of_descendant_browsing_contexts()) {
         for (auto& context : entry.document->list_of_descendant_browsing_contexts()) {
             for (auto& document : context->document_family()) {
             for (auto& document : context->document_family()) {
@@ -1367,4 +1383,56 @@ void BrowsingContext::close()
     discard();
     discard();
 }
 }
 
 
+void BrowsingContext::append_child(JS::NonnullGCPtr<BrowsingContext> child)
+{
+    VERIFY(!child->m_parent);
+
+    if (m_last_child)
+        m_last_child->m_next_sibling = child;
+    child->m_previous_sibling = m_last_child;
+    child->m_parent = this;
+    m_last_child = child;
+    if (!m_first_child)
+        m_first_child = m_last_child;
+}
+
+void BrowsingContext::remove_child(JS::NonnullGCPtr<BrowsingContext> child)
+{
+    VERIFY(child->m_parent.ptr() == this);
+
+    if (m_first_child == child)
+        m_first_child = child->m_next_sibling;
+
+    if (m_last_child == child)
+        m_last_child = child->m_previous_sibling;
+
+    if (child->m_next_sibling)
+        child->m_next_sibling->m_previous_sibling = child->m_previous_sibling;
+
+    if (child->m_previous_sibling)
+        child->m_previous_sibling->m_next_sibling = child->m_next_sibling;
+
+    child->m_next_sibling = nullptr;
+    child->m_previous_sibling = nullptr;
+    child->m_parent = nullptr;
+}
+
+JS::GCPtr<BrowsingContext> BrowsingContext::first_child() const
+{
+    return m_first_child;
+}
+JS::GCPtr<BrowsingContext> BrowsingContext::next_sibling() const
+{
+    return m_next_sibling;
+}
+
+bool BrowsingContext::is_ancestor_of(BrowsingContext const& other) const
+{
+    for (auto ancestor = other.parent(); ancestor; ancestor = ancestor->parent()) {
+        if (ancestor == this)
+            return true;
+    }
+    return false;
+}
+
 }
 }

+ 85 - 6
Userland/Libraries/LibWeb/HTML/BrowsingContext.h

@@ -13,6 +13,7 @@
 #include <LibGfx/Bitmap.h>
 #include <LibGfx/Bitmap.h>
 #include <LibGfx/Rect.h>
 #include <LibGfx/Rect.h>
 #include <LibGfx/Size.h>
 #include <LibGfx/Size.h>
+#include <LibJS/Heap/Cell.h>
 #include <LibWeb/DOM/Position.h>
 #include <LibWeb/DOM/Position.h>
 #include <LibWeb/HTML/BrowsingContextContainer.h>
 #include <LibWeb/HTML/BrowsingContextContainer.h>
 #include <LibWeb/HTML/HistoryHandlingBehavior.h>
 #include <LibWeb/HTML/HistoryHandlingBehavior.h>
@@ -26,13 +27,83 @@
 
 
 namespace Web::HTML {
 namespace Web::HTML {
 
 
-class BrowsingContext : public TreeNode<BrowsingContext> {
+class BrowsingContext final
+    : public JS::Cell
+    , public Weakable<BrowsingContext> {
+    JS_CELL(BrowsingContext, JS::Cell);
+
 public:
 public:
-    static NonnullRefPtr<BrowsingContext> create_a_new_browsing_context(Page&, JS::GCPtr<DOM::Document> creator, JS::GCPtr<DOM::Element> embedder, BrowsingContextGroup&);
-    static NonnullRefPtr<BrowsingContext> create_a_new_top_level_browsing_context(Page&);
+    static JS::NonnullGCPtr<BrowsingContext> create_a_new_browsing_context(Page&, JS::GCPtr<DOM::Document> creator, JS::GCPtr<DOM::Element> embedder, BrowsingContextGroup&);
+    static JS::NonnullGCPtr<BrowsingContext> create_a_new_top_level_browsing_context(Page&);
 
 
     ~BrowsingContext();
     ~BrowsingContext();
 
 
+    JS::GCPtr<BrowsingContext> parent() const { return m_parent; }
+    void append_child(JS::NonnullGCPtr<BrowsingContext>);
+    void remove_child(JS::NonnullGCPtr<BrowsingContext>);
+    JS::GCPtr<BrowsingContext> first_child() const;
+    JS::GCPtr<BrowsingContext> next_sibling() const;
+
+    bool is_ancestor_of(BrowsingContext const&) const;
+
+    template<typename Callback>
+    IterationDecision for_each_in_inclusive_subtree(Callback callback) const
+    {
+        if (callback(*this) == IterationDecision::Break)
+            return IterationDecision::Break;
+        for (auto child = first_child(); child; child = child->next_sibling()) {
+            if (child->for_each_in_inclusive_subtree(callback) == IterationDecision::Break)
+                return IterationDecision::Break;
+        }
+        return IterationDecision::Continue;
+    }
+
+    template<typename Callback>
+    IterationDecision for_each_in_inclusive_subtree(Callback callback)
+    {
+        if (callback(*this) == IterationDecision::Break)
+            return IterationDecision::Break;
+        for (auto child = first_child(); child; child = child->next_sibling()) {
+            if (child->for_each_in_inclusive_subtree(callback) == IterationDecision::Break)
+                return IterationDecision::Break;
+        }
+        return IterationDecision::Continue;
+    }
+
+    template<typename Callback>
+    void for_each_child(Callback callback) const
+    {
+        for (auto node = first_child(); node; node = node->next_sibling())
+            callback(*node);
+    }
+
+    template<typename Callback>
+    void for_each_child(Callback callback)
+    {
+        for (auto node = first_child(); node; node = node->next_sibling())
+            callback(*node);
+    }
+
+    template<typename Callback>
+    IterationDecision for_each_in_subtree(Callback callback) const
+    {
+        for (auto child = first_child(); child; child = child->next_sibling()) {
+            if (child->for_each_in_inclusive_subtree(callback) == IterationDecision::Break)
+                return IterationDecision::Break;
+        }
+        return IterationDecision::Continue;
+    }
+
+    template<typename Callback>
+    IterationDecision for_each_in_subtree(Callback callback)
+    {
+        for (auto child = first_child(); child; child = child->next_sibling()) {
+            if (child->for_each_in_inclusive_subtree(callback) == IterationDecision::Break)
+                return IterationDecision::Break;
+        }
+        return IterationDecision::Continue;
+    }
+
     class ViewportClient {
     class ViewportClient {
     public:
     public:
         virtual ~ViewportClient() = default;
         virtual ~ViewportClient() = default;
@@ -178,6 +249,8 @@ public:
 private:
 private:
     explicit BrowsingContext(Page&, HTML::BrowsingContextContainer*);
     explicit BrowsingContext(Page&, HTML::BrowsingContextContainer*);
 
 
+    virtual void visit_edges(Cell::Visitor&) override;
+
     void reset_cursor_blink_cycle();
     void reset_cursor_blink_cycle();
 
 
     void scroll_offset_did_change();
     void scroll_offset_did_change();
@@ -204,12 +277,12 @@ private:
     // https://html.spec.whatwg.org/multipage/browsers.html#creator-origin
     // https://html.spec.whatwg.org/multipage/browsers.html#creator-origin
     Optional<HTML::Origin> m_creator_origin;
     Optional<HTML::Origin> m_creator_origin;
 
 
-    WeakPtr<HTML::BrowsingContextContainer> m_container;
+    JS::GCPtr<HTML::BrowsingContextContainer> m_container;
     Gfx::IntSize m_size;
     Gfx::IntSize m_size;
     Gfx::IntPoint m_viewport_scroll_offset;
     Gfx::IntPoint m_viewport_scroll_offset;
 
 
     // https://html.spec.whatwg.org/multipage/browsers.html#browsing-context
     // https://html.spec.whatwg.org/multipage/browsers.html#browsing-context
-    JS::Handle<HTML::WindowProxy> m_window_proxy;
+    JS::GCPtr<HTML::WindowProxy> m_window_proxy;
 
 
     DOM::Position m_cursor_position;
     DOM::Position m_cursor_position;
     RefPtr<Platform::Timer> m_cursor_blink_timer;
     RefPtr<Platform::Timer> m_cursor_blink_timer;
@@ -221,10 +294,16 @@ private:
     String m_name;
     String m_name;
 
 
     // https://html.spec.whatwg.org/multipage/browsers.html#tlbc-group
     // https://html.spec.whatwg.org/multipage/browsers.html#tlbc-group
-    RefPtr<BrowsingContextGroup> m_group;
+    JS::GCPtr<BrowsingContextGroup> m_group;
 
 
     // https://html.spec.whatwg.org/multipage/interaction.html#system-visibility-state
     // https://html.spec.whatwg.org/multipage/interaction.html#system-visibility-state
     VisibilityState m_system_visibility_state { VisibilityState::Hidden };
     VisibilityState m_system_visibility_state { VisibilityState::Hidden };
+
+    JS::GCPtr<BrowsingContext> m_parent;
+    JS::GCPtr<BrowsingContext> m_first_child;
+    JS::GCPtr<BrowsingContext> m_last_child;
+    JS::GCPtr<BrowsingContext> m_next_sibling;
+    JS::GCPtr<BrowsingContext> m_previous_sibling;
 };
 };
 
 
 HTML::Origin determine_the_origin(BrowsingContext const& browsing_context, Optional<AK::URL> url, SandboxingFlagSet sandbox_flags, Optional<HTML::Origin> invocation_origin);
 HTML::Origin determine_the_origin(BrowsingContext const& browsing_context, Optional<AK::URL> url, SandboxingFlagSet sandbox_flags, Optional<HTML::Origin> invocation_origin);

+ 7 - 1
Userland/Libraries/LibWeb/HTML/BrowsingContextContainer.cpp

@@ -31,6 +31,12 @@ BrowsingContextContainer::BrowsingContextContainer(DOM::Document& document, DOM:
 
 
 BrowsingContextContainer::~BrowsingContextContainer() = default;
 BrowsingContextContainer::~BrowsingContextContainer() = default;
 
 
+void BrowsingContextContainer::visit_edges(Cell::Visitor& visitor)
+{
+    Base::visit_edges(visitor);
+    visitor.visit(m_nested_browsing_context);
+}
+
 // https://html.spec.whatwg.org/multipage/browsers.html#creating-a-new-nested-browsing-context
 // https://html.spec.whatwg.org/multipage/browsers.html#creating-a-new-nested-browsing-context
 void BrowsingContextContainer::create_new_nested_browsing_context()
 void BrowsingContextContainer::create_new_nested_browsing_context()
 {
 {
@@ -142,7 +148,7 @@ void BrowsingContextContainer::shared_attribute_processing_steps_for_iframe_and_
     // 3. If there exists an ancestor browsing context of element's nested browsing context
     // 3. If there exists an ancestor browsing context of element's nested browsing context
     //    whose active document's URL, ignoring fragments, is equal to url, then return.
     //    whose active document's URL, ignoring fragments, is equal to url, then return.
     if (m_nested_browsing_context) {
     if (m_nested_browsing_context) {
-        for (auto* ancestor = m_nested_browsing_context->parent(); ancestor; ancestor = ancestor->parent()) {
+        for (auto ancestor = m_nested_browsing_context->parent(); ancestor; ancestor = ancestor->parent()) {
             VERIFY(ancestor->active_document());
             VERIFY(ancestor->active_document());
             if (ancestor->active_document()->url().equals(url, AK::URL::ExcludeFragment::Yes))
             if (ancestor->active_document()->url().equals(url, AK::URL::ExcludeFragment::Yes))
                 return;
                 return;

+ 3 - 1
Userland/Libraries/LibWeb/HTML/BrowsingContextContainer.h

@@ -31,6 +31,8 @@ public:
 protected:
 protected:
     BrowsingContextContainer(DOM::Document&, DOM::QualifiedName);
     BrowsingContextContainer(DOM::Document&, DOM::QualifiedName);
 
 
+    virtual void visit_edges(Cell::Visitor&) override;
+
     // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#shared-attribute-processing-steps-for-iframe-and-frame-elements
     // https://html.spec.whatwg.org/multipage/iframe-embed-object.html#shared-attribute-processing-steps-for-iframe-and-frame-elements
     void shared_attribute_processing_steps_for_iframe_and_frame(bool initial_insertion);
     void shared_attribute_processing_steps_for_iframe_and_frame(bool initial_insertion);
 
 
@@ -39,7 +41,7 @@ protected:
 
 
     void create_new_nested_browsing_context();
     void create_new_nested_browsing_context();
 
 
-    RefPtr<BrowsingContext> m_nested_browsing_context;
+    JS::GCPtr<BrowsingContext> m_nested_browsing_context;
 
 
 private:
 private:
     virtual bool is_browsing_context_container() const override { return true; }
     virtual bool is_browsing_context_container() const override { return true; }

+ 13 - 5
Userland/Libraries/LibWeb/HTML/BrowsingContextGroup.cpp

@@ -4,6 +4,7 @@
  * SPDX-License-Identifier: BSD-2-Clause
  * SPDX-License-Identifier: BSD-2-Clause
  */
  */
 
 
+#include <LibWeb/Bindings/MainThreadVM.h>
 #include <LibWeb/HTML/BrowsingContext.h>
 #include <LibWeb/HTML/BrowsingContext.h>
 #include <LibWeb/HTML/BrowsingContextGroup.h>
 #include <LibWeb/HTML/BrowsingContextGroup.h>
 
 
@@ -27,21 +28,28 @@ BrowsingContextGroup::~BrowsingContextGroup()
     user_agent_browsing_context_group_set().remove(this);
     user_agent_browsing_context_group_set().remove(this);
 }
 }
 
 
+void BrowsingContextGroup::visit_edges(Cell::Visitor& visitor)
+{
+    Base::visit_edges(visitor);
+    for (auto& context : m_browsing_context_set)
+        visitor.visit(context);
+}
+
 // https://html.spec.whatwg.org/multipage/browsers.html#creating-a-new-browsing-context-group
 // https://html.spec.whatwg.org/multipage/browsers.html#creating-a-new-browsing-context-group
-NonnullRefPtr<BrowsingContextGroup> BrowsingContextGroup::create_a_new_browsing_context_group(Web::Page& page)
+JS::NonnullGCPtr<BrowsingContextGroup> BrowsingContextGroup::create_a_new_browsing_context_group(Web::Page& page)
 {
 {
     // 1. Let group be a new browsing context group.
     // 1. Let group be a new browsing context group.
     // 2. Append group to the user agent's browsing context group set.
     // 2. Append group to the user agent's browsing context group set.
-    auto group = adopt_ref(*new BrowsingContextGroup(page));
+    auto group = Bindings::main_thread_vm().heap().allocate_without_realm<BrowsingContextGroup>(page);
 
 
     // 3. Let browsingContext be the result of creating a new browsing context with null, null, and group.
     // 3. Let browsingContext be the result of creating a new browsing context with null, null, and group.
-    auto browsing_context = BrowsingContext::create_a_new_browsing_context(page, nullptr, nullptr, group);
+    auto browsing_context = BrowsingContext::create_a_new_browsing_context(page, nullptr, nullptr, *group);
 
 
     // 4. Append browsingContext to group.
     // 4. Append browsingContext to group.
     group->append(move(browsing_context));
     group->append(move(browsing_context));
 
 
     // 5. Return group.
     // 5. Return group.
-    return group;
+    return *group;
 }
 }
 
 
 // https://html.spec.whatwg.org/multipage/browsers.html#bcg-append
 // https://html.spec.whatwg.org/multipage/browsers.html#bcg-append
@@ -50,7 +58,7 @@ void BrowsingContextGroup::append(BrowsingContext& browsing_context)
     VERIFY(browsing_context.is_top_level());
     VERIFY(browsing_context.is_top_level());
 
 
     // 1. Append browsingContext to group's browsing context set.
     // 1. Append browsingContext to group's browsing context set.
-    m_browsing_context_set.set(browsing_context);
+    m_browsing_context_set.set(&browsing_context);
 
 
     // 2. Set browsingContext's group to group.
     // 2. Set browsingContext's group to group.
     browsing_context.set_group(this);
     browsing_context.set_group(this);

+ 8 - 4
Userland/Libraries/LibWeb/HTML/BrowsingContextGroup.h

@@ -8,14 +8,16 @@
 
 
 #include <AK/HashMap.h>
 #include <AK/HashMap.h>
 #include <AK/NonnullRefPtr.h>
 #include <AK/NonnullRefPtr.h>
-#include <AK/RefCounted.h>
+#include <LibJS/Heap/Cell.h>
 #include <LibWeb/Forward.h>
 #include <LibWeb/Forward.h>
 
 
 namespace Web::HTML {
 namespace Web::HTML {
 
 
-class BrowsingContextGroup : public RefCounted<BrowsingContextGroup> {
+class BrowsingContextGroup final : public JS::Cell {
+    JS_CELL(BrowsingContextGroup, JS::Cell);
+
 public:
 public:
-    static NonnullRefPtr<BrowsingContextGroup> create_a_new_browsing_context_group(Page&);
+    static JS::NonnullGCPtr<BrowsingContextGroup> create_a_new_browsing_context_group(Page&);
     ~BrowsingContextGroup();
     ~BrowsingContextGroup();
 
 
     Page* page() { return m_page; }
     Page* page() { return m_page; }
@@ -30,8 +32,10 @@ public:
 private:
 private:
     explicit BrowsingContextGroup(Web::Page&);
     explicit BrowsingContextGroup(Web::Page&);
 
 
+    virtual void visit_edges(Cell::Visitor&) override;
+
     // https://html.spec.whatwg.org/multipage/browsers.html#browsing-context-group-set
     // https://html.spec.whatwg.org/multipage/browsers.html#browsing-context-group-set
-    OrderedHashTable<NonnullRefPtr<BrowsingContext>> m_browsing_context_set;
+    OrderedHashTable<BrowsingContext*> m_browsing_context_set;
 
 
     WeakPtr<Page> m_page;
     WeakPtr<Page> m_page;
 };
 };

+ 1 - 1
Userland/Libraries/LibWeb/HTML/NavigationParams.h

@@ -47,7 +47,7 @@ struct NavigationParams {
     Optional<Environment> reserved_environment;
     Optional<Environment> reserved_environment;
 
 
     // the browsing context to be navigated (or discarded, if a browsing context group switch occurs)
     // the browsing context to be navigated (or discarded, if a browsing context group switch occurs)
-    NonnullRefPtr<HTML::BrowsingContext> browsing_context;
+    JS::Handle<HTML::BrowsingContext> browsing_context;
 
 
     // a history handling behavior
     // a history handling behavior
     HistoryHandlingBehavior history_handling { HistoryHandlingBehavior::Default };
     HistoryHandlingBehavior history_handling { HistoryHandlingBehavior::Default };

+ 1 - 0
Userland/Libraries/LibWeb/HTML/Scripting/Environments.cpp

@@ -32,6 +32,7 @@ EnvironmentSettingsObject::~EnvironmentSettingsObject()
 void EnvironmentSettingsObject::visit_edges(Cell::Visitor& visitor)
 void EnvironmentSettingsObject::visit_edges(Cell::Visitor& visitor)
 {
 {
     Base::visit_edges(visitor);
     Base::visit_edges(visitor);
+    visitor.visit(target_browsing_context);
     for (auto& promise : m_about_to_be_notified_rejected_promises_list)
     for (auto& promise : m_about_to_be_notified_rejected_promises_list)
         visitor.visit(promise);
         visitor.visit(promise);
 }
 }

+ 1 - 1
Userland/Libraries/LibWeb/HTML/Scripting/Environments.h

@@ -33,7 +33,7 @@ struct Environment {
     Origin top_level_origin;
     Origin top_level_origin;
 
 
     // https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-target-browsing-context
     // https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-target-browsing-context
-    RefPtr<BrowsingContext> target_browsing_context;
+    JS::GCPtr<BrowsingContext> target_browsing_context;
 
 
     // FIXME: An active service worker https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-active-service-worker
     // FIXME: An active service worker https://html.spec.whatwg.org/multipage/webappapis.html#concept-environment-active-service-worker
 
 

+ 2 - 2
Userland/Libraries/LibWeb/HTML/SessionHistoryEntry.h

@@ -30,7 +30,7 @@ struct SessionHistoryEntry {
     AK::URL url;
     AK::URL url;
 
 
     // document, a Document or null
     // document, a Document or null
-    WeakPtr<DOM::Document> document;
+    JS::GCPtr<DOM::Document> document;
 
 
     // serialized state, which is serialized state or null, initially null
     // serialized state, which is serialized state or null, initially null
     Optional<String> serialized_state;
     Optional<String> serialized_state;
@@ -49,7 +49,7 @@ struct SessionHistoryEntry {
     // FIXME: persisted user state, which is implementation-defined, initially null
     // FIXME: persisted user state, which is implementation-defined, initially null
     // NOTE: This is where we could remember the state of form controls, for example.
     // NOTE: This is where we could remember the state of form controls, for example.
 
 
-    WeakPtr<BrowsingContext> original_source_browsing_context;
+    JS::GCPtr<BrowsingContext> original_source_browsing_context;
 };
 };
 
 
 }
 }

+ 11 - 1
Userland/Libraries/LibWeb/Page/Page.cpp

@@ -13,7 +13,7 @@ namespace Web {
 Page::Page(PageClient& client)
 Page::Page(PageClient& client)
     : m_client(client)
     : m_client(client)
 {
 {
-    m_top_level_browsing_context = HTML::BrowsingContext::create_a_new_top_level_browsing_context(*this);
+    m_top_level_browsing_context = JS::make_handle(*HTML::BrowsingContext::create_a_new_top_level_browsing_context(*this));
 }
 }
 
 
 Page::~Page() = default;
 Page::~Page() = default;
@@ -95,4 +95,14 @@ bool Page::handle_keyup(KeyCode key, unsigned modifiers, u32 code_point)
     return focused_context().event_handler().handle_keyup(key, modifiers, code_point);
     return focused_context().event_handler().handle_keyup(key, modifiers, code_point);
 }
 }
 
 
+HTML::BrowsingContext& Page::top_level_browsing_context()
+{
+    return *m_top_level_browsing_context;
+}
+
+HTML::BrowsingContext const& Page::top_level_browsing_context() const
+{
+    return *m_top_level_browsing_context;
+}
+
 }
 }

+ 4 - 3
Userland/Libraries/LibWeb/Page/Page.h

@@ -17,6 +17,7 @@
 #include <LibGfx/Forward.h>
 #include <LibGfx/Forward.h>
 #include <LibGfx/Palette.h>
 #include <LibGfx/Palette.h>
 #include <LibGfx/StandardCursor.h>
 #include <LibGfx/StandardCursor.h>
+#include <LibJS/Heap/Handle.h>
 #include <LibWeb/CSS/PreferredColorScheme.h>
 #include <LibWeb/CSS/PreferredColorScheme.h>
 #include <LibWeb/Forward.h>
 #include <LibWeb/Forward.h>
 #include <LibWeb/Loader/FileRequest.h>
 #include <LibWeb/Loader/FileRequest.h>
@@ -36,8 +37,8 @@ public:
     PageClient& client() { return m_client; }
     PageClient& client() { return m_client; }
     PageClient const& client() const { return m_client; }
     PageClient const& client() const { return m_client; }
 
 
-    HTML::BrowsingContext& top_level_browsing_context() { return *m_top_level_browsing_context; }
-    HTML::BrowsingContext const& top_level_browsing_context() const { return *m_top_level_browsing_context; }
+    HTML::BrowsingContext& top_level_browsing_context();
+    HTML::BrowsingContext const& top_level_browsing_context() const;
 
 
     HTML::BrowsingContext& focused_context();
     HTML::BrowsingContext& focused_context();
     HTML::BrowsingContext const& focused_context() const { return const_cast<Page*>(this)->focused_context(); }
     HTML::BrowsingContext const& focused_context() const { return const_cast<Page*>(this)->focused_context(); }
@@ -74,7 +75,7 @@ public:
 private:
 private:
     PageClient& m_client;
     PageClient& m_client;
 
 
-    RefPtr<HTML::BrowsingContext> m_top_level_browsing_context;
+    JS::Handle<HTML::BrowsingContext> m_top_level_browsing_context;
     WeakPtr<HTML::BrowsingContext> m_focused_context;
     WeakPtr<HTML::BrowsingContext> m_focused_context;
 
 
     // FIXME: Enable this by default once CORS preflight checks are supported.
     // FIXME: Enable this by default once CORS preflight checks are supported.

+ 1 - 1
Userland/Libraries/LibWeb/Painting/LabelablePaintable.cpp

@@ -83,7 +83,7 @@ void LabelablePaintable::handle_associated_label_mouseup(Badge<Layout::Label>)
 {
 {
     // NOTE: Handling the click may run arbitrary JS, which could disappear this node.
     // NOTE: Handling the click may run arbitrary JS, which could disappear this node.
     NonnullRefPtr protected_this = *this;
     NonnullRefPtr protected_this = *this;
-    NonnullRefPtr protected_browsing_context = browsing_context();
+    JS::NonnullGCPtr protected_browsing_context { browsing_context() };
 
 
     set_being_pressed(false);
     set_being_pressed(false);
 }
 }