Browse Source

LibWeb: Port EventLoop::spin_XXX to HeapFunction

Shannon Booth 8 months ago
parent
commit
1c18b900e2

+ 6 - 6
Userland/Libraries/LibWeb/DOM/Document.cpp

@@ -606,9 +606,9 @@ WebIDL::ExceptionOr<Document*> Document::open(Optional<String> const&, Optional<
     // because subsequent steps will modify "initial about:blank" to false, which would cause
     // because subsequent steps will modify "initial about:blank" to false, which would cause
     // initial navigation to fail in case it was "about:blank".
     // initial navigation to fail in case it was "about:blank".
     if (auto navigable = this->navigable(); navigable && navigable->container() && !navigable->container()->content_navigable_initialized()) {
     if (auto navigable = this->navigable(); navigable && navigable->container() && !navigable->container()->content_navigable_initialized()) {
-        HTML::main_thread_event_loop().spin_processing_tasks_with_source_until(HTML::Task::Source::NavigationAndTraversal, [navigable_container = navigable->container()] {
+        HTML::main_thread_event_loop().spin_processing_tasks_with_source_until(HTML::Task::Source::NavigationAndTraversal, JS::create_heap_function(heap(), [navigable_container = navigable->container()] {
             return navigable_container->content_navigable_initialized();
             return navigable_container->content_navigable_initialized();
-        });
+        }));
     }
     }
 
 
     // 1. If document is an XML document, then throw an "InvalidStateError" DOMException exception.
     // 1. If document is an XML document, then throw an "InvalidStateError" DOMException exception.
@@ -3451,9 +3451,9 @@ void Document::destroy_a_document_and_its_descendants(JS::GCPtr<JS::HeapFunction
     }
     }
 
 
     // 5. Wait until numberDestroyed equals childNavigable's size.
     // 5. Wait until numberDestroyed equals childNavigable's size.
-    HTML::main_thread_event_loop().spin_until([&] {
+    HTML::main_thread_event_loop().spin_until(JS::create_heap_function(heap(), [&] {
         return number_destroyed == child_navigables.size();
         return number_destroyed == child_navigables.size();
-    });
+    }));
 
 
     // 6. Queue a global task on the navigation and traversal task source given document's relevant global object to perform the following steps:
     // 6. Queue a global task on the navigation and traversal task source given document's relevant global object to perform the following steps:
     HTML::queue_global_task(HTML::Task::Source::NavigationAndTraversal, relevant_global_object(*this), JS::create_heap_function(heap(), [after_all_destruction = move(after_all_destruction), this] {
     HTML::queue_global_task(HTML::Task::Source::NavigationAndTraversal, relevant_global_object(*this), JS::create_heap_function(heap(), [after_all_destruction = move(after_all_destruction), this] {
@@ -3675,9 +3675,9 @@ void Document::unload_a_document_and_its_descendants(JS::GCPtr<Document> new_doc
         }));
         }));
     }
     }
 
 
-    HTML::main_thread_event_loop().spin_until([&] {
+    HTML::main_thread_event_loop().spin_until(JS::create_heap_function(heap(), [&] {
         return number_unloaded == unloaded_documents_count;
         return number_unloaded == unloaded_documents_count;
-    });
+    }));
 
 
     destroy_a_document_and_its_descendants(move(after_all_unloads));
     destroy_a_document_and_its_descendants(move(after_all_unloads));
 }
 }

+ 7 - 7
Userland/Libraries/LibWeb/Fetch/Fetching/Fetching.cpp

@@ -332,15 +332,15 @@ WebIDL::ExceptionOr<JS::GCPtr<PendingResponse>> main_fetch(JS::Realm& realm, Inf
         request->current_url().set_scheme("https"_string);
         request->current_url().set_scheme("https"_string);
     }
     }
 
 
-    JS::SafeFunction<WebIDL::ExceptionOr<JS::NonnullGCPtr<PendingResponse>>()> get_response = [&realm, &vm, &fetch_params, request]() -> WebIDL::ExceptionOr<JS::NonnullGCPtr<PendingResponse>> {
+    auto get_response = JS::create_heap_function(vm.heap(), [&realm, &vm, &fetch_params, request]() -> WebIDL::ExceptionOr<JS::NonnullGCPtr<PendingResponse>> {
         dbgln_if(WEB_FETCH_DEBUG, "Fetch: Running 'main fetch' get_response() function");
         dbgln_if(WEB_FETCH_DEBUG, "Fetch: Running 'main fetch' get_response() function");
 
 
         // -> fetchParams’s preloaded response candidate is not null
         // -> fetchParams’s preloaded response candidate is not null
         if (!fetch_params.preloaded_response_candidate().has<Empty>()) {
         if (!fetch_params.preloaded_response_candidate().has<Empty>()) {
             // 1. Wait until fetchParams’s preloaded response candidate is not "pending".
             // 1. Wait until fetchParams’s preloaded response candidate is not "pending".
-            HTML::main_thread_event_loop().spin_until([&] {
+            HTML::main_thread_event_loop().spin_until(JS::create_heap_function(vm.heap(), [&] {
                 return !fetch_params.preloaded_response_candidate().has<Infrastructure::FetchParams::PreloadedResponseCandidatePendingTag>();
                 return !fetch_params.preloaded_response_candidate().has<Infrastructure::FetchParams::PreloadedResponseCandidatePendingTag>();
-            });
+            }));
 
 
             // 2. Assert: fetchParams’s preloaded response candidate is a response.
             // 2. Assert: fetchParams’s preloaded response candidate is a response.
             VERIFY(fetch_params.preloaded_response_candidate().has<JS::NonnullGCPtr<Infrastructure::Response>>());
             VERIFY(fetch_params.preloaded_response_candidate().has<JS::NonnullGCPtr<Infrastructure::Response>>());
@@ -426,13 +426,13 @@ WebIDL::ExceptionOr<JS::GCPtr<PendingResponse>> main_fetch(JS::Realm& realm, Inf
             // 2. Return the result of running HTTP fetch given fetchParams.
             // 2. Return the result of running HTTP fetch given fetchParams.
             return http_fetch(realm, fetch_params);
             return http_fetch(realm, fetch_params);
         }
         }
-    };
+    });
 
 
     if (recursive == Recursive::Yes) {
     if (recursive == Recursive::Yes) {
         // 12. If response is null, then set response to the result of running the steps corresponding to the first
         // 12. If response is null, then set response to the result of running the steps corresponding to the first
         //     matching statement:
         //     matching statement:
         auto pending_response = !response
         auto pending_response = !response
-            ? TRY(get_response())
+            ? TRY(get_response->function()())
             : PendingResponse::create(vm, request, *response);
             : PendingResponse::create(vm, request, *response);
 
 
         // 13. If recursive is true, then return response.
         // 13. If recursive is true, then return response.
@@ -440,12 +440,12 @@ WebIDL::ExceptionOr<JS::GCPtr<PendingResponse>> main_fetch(JS::Realm& realm, Inf
     }
     }
 
 
     // 11. If recursive is false, then run the remaining steps in parallel.
     // 11. If recursive is false, then run the remaining steps in parallel.
-    Platform::EventLoopPlugin::the().deferred_invoke(JS::create_heap_function(realm.heap(), [&realm, &vm, &fetch_params, request, response, get_response = move(get_response)] {
+    Platform::EventLoopPlugin::the().deferred_invoke(JS::create_heap_function(realm.heap(), [&realm, &vm, &fetch_params, request, response, get_response] {
         // 12. If response is null, then set response to the result of running the steps corresponding to the first
         // 12. If response is null, then set response to the result of running the steps corresponding to the first
         //     matching statement:
         //     matching statement:
         auto pending_response = PendingResponse::create(vm, request, Infrastructure::Response::create(vm));
         auto pending_response = PendingResponse::create(vm, request, Infrastructure::Response::create(vm));
         if (!response) {
         if (!response) {
-            auto pending_response_or_error = get_response();
+            auto pending_response_or_error = get_response->function()();
             if (pending_response_or_error.is_error())
             if (pending_response_or_error.is_error())
                 return;
                 return;
             pending_response = pending_response_or_error.release_value();
             pending_response = pending_response_or_error.release_value();

+ 8 - 8
Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.cpp

@@ -69,7 +69,7 @@ EventLoop& main_thread_event_loop()
 }
 }
 
 
 // https://html.spec.whatwg.org/multipage/webappapis.html#spin-the-event-loop
 // https://html.spec.whatwg.org/multipage/webappapis.html#spin-the-event-loop
-void EventLoop::spin_until(JS::SafeFunction<bool()> goal_condition)
+void EventLoop::spin_until(JS::NonnullGCPtr<JS::HeapFunction<bool()>> goal_condition)
 {
 {
     // FIXME: The spec wants us to do the rest of the enclosing algorithm (i.e. the caller)
     // FIXME: The spec wants us to do the rest of the enclosing algorithm (i.e. the caller)
     //    in the context of the currently running task on entry. That's not possible with this implementation.
     //    in the context of the currently running task on entry. That's not possible with this implementation.
@@ -92,15 +92,15 @@ void EventLoop::spin_until(JS::SafeFunction<bool()> goal_condition)
     //       2. Perform any steps that appear after this spin the event loop instance in the original algorithm.
     //       2. Perform any steps that appear after this spin the event loop instance in the original algorithm.
     //       NOTE: This is achieved by returning from the function.
     //       NOTE: This is achieved by returning from the function.
 
 
-    Platform::EventLoopPlugin::the().spin_until(JS::create_heap_function(heap(), [&] {
-        if (goal_condition())
+    Platform::EventLoopPlugin::the().spin_until(JS::create_heap_function(heap(), [this, goal_condition] {
+        if (goal_condition->function()())
             return true;
             return true;
         if (m_task_queue->has_runnable_tasks()) {
         if (m_task_queue->has_runnable_tasks()) {
             schedule();
             schedule();
             // FIXME: Remove the platform event loop plugin so that this doesn't look out of place
             // FIXME: Remove the platform event loop plugin so that this doesn't look out of place
             Core::EventLoop::current().wake();
             Core::EventLoop::current().wake();
         }
         }
-        return goal_condition();
+        return goal_condition->function()();
     }));
     }));
 
 
     vm.restore_execution_context_stack();
     vm.restore_execution_context_stack();
@@ -109,7 +109,7 @@ void EventLoop::spin_until(JS::SafeFunction<bool()> goal_condition)
     // NOTE: This is achieved by returning from the function.
     // NOTE: This is achieved by returning from the function.
 }
 }
 
 
-void EventLoop::spin_processing_tasks_with_source_until(Task::Source source, JS::SafeFunction<bool()> goal_condition)
+void EventLoop::spin_processing_tasks_with_source_until(Task::Source source, JS::NonnullGCPtr<JS::HeapFunction<bool()>> goal_condition)
 {
 {
     auto& vm = this->vm();
     auto& vm = this->vm();
     vm.save_execution_context_stack();
     vm.save_execution_context_stack();
@@ -120,8 +120,8 @@ void EventLoop::spin_processing_tasks_with_source_until(Task::Source source, JS:
     // NOTE: HTML event loop processing steps could run a task with arbitrary source
     // NOTE: HTML event loop processing steps could run a task with arbitrary source
     m_skip_event_loop_processing_steps = true;
     m_skip_event_loop_processing_steps = true;
 
 
-    Platform::EventLoopPlugin::the().spin_until(JS::create_heap_function(heap(), [&] {
-        if (goal_condition())
+    Platform::EventLoopPlugin::the().spin_until(JS::create_heap_function(heap(), [this, source, goal_condition] {
+        if (goal_condition->function()())
             return true;
             return true;
         if (m_task_queue->has_runnable_tasks()) {
         if (m_task_queue->has_runnable_tasks()) {
             auto tasks = m_task_queue->take_tasks_matching([&](auto& task) {
             auto tasks = m_task_queue->take_tasks_matching([&](auto& task) {
@@ -137,7 +137,7 @@ void EventLoop::spin_processing_tasks_with_source_until(Task::Source source, JS:
 
 
         // FIXME: Remove the platform event loop plugin so that this doesn't look out of place
         // FIXME: Remove the platform event loop plugin so that this doesn't look out of place
         Core::EventLoop::current().wake();
         Core::EventLoop::current().wake();
-        return goal_condition();
+        return goal_condition->function()();
     }));
     }));
 
 
     m_skip_event_loop_processing_steps = false;
     m_skip_event_loop_processing_steps = false;

+ 2 - 3
Userland/Libraries/LibWeb/HTML/EventLoop/EventLoop.h

@@ -10,7 +10,6 @@
 #include <AK/WeakPtr.h>
 #include <AK/WeakPtr.h>
 #include <LibCore/Forward.h>
 #include <LibCore/Forward.h>
 #include <LibJS/Forward.h>
 #include <LibJS/Forward.h>
-#include <LibJS/SafeFunction.h>
 #include <LibWeb/HTML/EventLoop/TaskQueue.h>
 #include <LibWeb/HTML/EventLoop/TaskQueue.h>
 
 
 namespace Web::HTML {
 namespace Web::HTML {
@@ -39,8 +38,8 @@ public:
     TaskQueue& microtask_queue() { return *m_microtask_queue; }
     TaskQueue& microtask_queue() { return *m_microtask_queue; }
     TaskQueue const& microtask_queue() const { return *m_microtask_queue; }
     TaskQueue const& microtask_queue() const { return *m_microtask_queue; }
 
 
-    void spin_until(JS::SafeFunction<bool()> goal_condition);
-    void spin_processing_tasks_with_source_until(Task::Source, JS::SafeFunction<bool()> goal_condition);
+    void spin_until(JS::NonnullGCPtr<JS::HeapFunction<bool()>> goal_condition);
+    void spin_processing_tasks_with_source_until(Task::Source, JS::NonnullGCPtr<JS::HeapFunction<bool()>> goal_condition);
     void process();
     void process();
     void queue_task_to_update_the_rendering();
     void queue_task_to_update_the_rendering();
 
 

+ 3 - 3
Userland/Libraries/LibWeb/HTML/EventSource.cpp

@@ -298,9 +298,9 @@ void EventSource::reestablish_the_connection()
     }));
     }));
 
 
     // 2. Wait a delay equal to the reconnection time of the event source.
     // 2. Wait a delay equal to the reconnection time of the event source.
-    HTML::main_thread_event_loop().spin_until([&, delay_start = MonotonicTime::now()]() {
+    HTML::main_thread_event_loop().spin_until(JS::create_heap_function(heap(), [&, delay_start = MonotonicTime::now()]() {
         return (MonotonicTime::now() - delay_start) >= m_reconnection_time;
         return (MonotonicTime::now() - delay_start) >= m_reconnection_time;
-    });
+    }));
 
 
     // 3. Optionally, wait some more. In particular, if the previous attempt failed, then user agents might introduce
     // 3. Optionally, wait some more. In particular, if the previous attempt failed, then user agents might introduce
     //    an exponential backoff delay to avoid overloading a potentially already overloaded server. Alternatively, if
     //    an exponential backoff delay to avoid overloading a potentially already overloaded server. Alternatively, if
@@ -309,7 +309,7 @@ void EventSource::reestablish_the_connection()
 
 
     // 4. Wait until the aforementioned task has run, if it has not yet run.
     // 4. Wait until the aforementioned task has run, if it has not yet run.
     if (!initial_task_has_run) {
     if (!initial_task_has_run) {
-        HTML::main_thread_event_loop().spin_until([&]() { return initial_task_has_run; });
+        HTML::main_thread_event_loop().spin_until(JS::create_heap_function(heap(), [&]() { return initial_task_has_run; }));
     }
     }
 
 
     // 5. Queue a task to run the following steps:
     // 5. Queue a task to run the following steps:

+ 2 - 2
Userland/Libraries/LibWeb/HTML/HTMLMediaElement.cpp

@@ -862,7 +862,7 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::select_resource()
             });
             });
 
 
             // 7. Wait for the task queued by the previous step to have executed.
             // 7. Wait for the task queued by the previous step to have executed.
-            HTML::main_thread_event_loop().spin_until([&]() { return ran_media_element_task; });
+            HTML::main_thread_event_loop().spin_until(JS::create_heap_function(heap(), [&]() { return ran_media_element_task; }));
         };
         };
 
 
         // 1. ⌛ If the src attribute's value is the empty string, then end the synchronous section, and jump down to the failed with attribute step below.
         // 1. ⌛ If the src attribute's value is the empty string, then end the synchronous section, and jump down to the failed with attribute step below.
@@ -1580,7 +1580,7 @@ void HTMLMediaElement::seek_element(double playback_position, MediaSeekMode seek
     //     available, and, if it is, until it has decoded enough data to play back that position.
     //     available, and, if it is, until it has decoded enough data to play back that position.
     m_seek_in_progress = true;
     m_seek_in_progress = true;
     on_seek(playback_position, seek_mode);
     on_seek(playback_position, seek_mode);
-    HTML::main_thread_event_loop().spin_until([&]() { return !m_seek_in_progress; });
+    HTML::main_thread_event_loop().spin_until(JS::create_heap_function(heap(), [&]() { return !m_seek_in_progress; }));
 
 
     // FIXME: 13. Await a stable state. The synchronous section consists of all the remaining steps of this algorithm. (Steps in the
     // FIXME: 13. Await a stable state. The synchronous section consists of all the remaining steps of this algorithm. (Steps in the
     //            synchronous section are marked with ⌛.)
     //            synchronous section are marked with ⌛.)

+ 1 - 1
Userland/Libraries/LibWeb/HTML/HTMLScriptElement.cpp

@@ -86,7 +86,7 @@ void HTMLScriptElement::execute_script()
     // https://html.spec.whatwg.org/multipage/document-lifecycle.html#read-html
     // https://html.spec.whatwg.org/multipage/document-lifecycle.html#read-html
     // Before any script execution occurs, the user agent must wait for scripts may run for the newly-created document to be true for document.
     // Before any script execution occurs, the user agent must wait for scripts may run for the newly-created document to be true for document.
     if (!m_document->ready_to_run_scripts())
     if (!m_document->ready_to_run_scripts())
-        main_thread_event_loop().spin_until([&] { return m_document->ready_to_run_scripts(); });
+        main_thread_event_loop().spin_until(JS::create_heap_function(heap(), [&] { return m_document->ready_to_run_scripts(); }));
 
 
     // 1. Let document be el's node document.
     // 1. Let document be el's node document.
     JS::NonnullGCPtr<DOM::Document> document = this->document();
     JS::NonnullGCPtr<DOM::Document> document = this->document();

+ 2 - 2
Userland/Libraries/LibWeb/HTML/Navigable.cpp

@@ -883,7 +883,7 @@ static WebIDL::ExceptionOr<Navigable::NavigationParamsVariant> create_navigation
         }
         }
 
 
         // 7. Wait until either response is non-null, or navigable's ongoing navigation changes to no longer equal navigationId.
         // 7. Wait until either response is non-null, or navigable's ongoing navigation changes to no longer equal navigationId.
-        HTML::main_thread_event_loop().spin_until([&]() {
+        HTML::main_thread_event_loop().spin_until(JS::create_heap_function(vm.heap(), [&]() {
             if (response_holder->response() != nullptr)
             if (response_holder->response() != nullptr)
                 return true;
                 return true;
 
 
@@ -891,7 +891,7 @@ static WebIDL::ExceptionOr<Navigable::NavigationParamsVariant> create_navigation
                 return true;
                 return true;
 
 
             return false;
             return false;
-        });
+        }));
         // If the latter condition occurs, then abort fetchController, and return. Otherwise, proceed onward.
         // If the latter condition occurs, then abort fetchController, and return. Otherwise, proceed onward.
         if (navigation_id.has_value() && (!navigable->ongoing_navigation().has<String>() || navigable->ongoing_navigation().get<String>() != *navigation_id)) {
         if (navigation_id.has_value() && (!navigable->ongoing_navigation().has<String>() || navigable->ongoing_navigation().get<String>() != *navigation_id)) {
             fetch_controller->abort(realm, {});
             fetch_controller->abort(realm, {});

+ 11 - 9
Userland/Libraries/LibWeb/HTML/Parser/HTMLParser.cpp

@@ -231,6 +231,8 @@ void HTMLParser::run(const URL::URL& url, HTMLTokenizer::StopAtInsertionPoint st
 // https://html.spec.whatwg.org/multipage/parsing.html#the-end
 // https://html.spec.whatwg.org/multipage/parsing.html#the-end
 void HTMLParser::the_end(JS::NonnullGCPtr<DOM::Document> document, JS::GCPtr<HTMLParser> parser)
 void HTMLParser::the_end(JS::NonnullGCPtr<DOM::Document> document, JS::GCPtr<HTMLParser> parser)
 {
 {
+    auto& heap = document->heap();
+
     // Once the user agent stops parsing the document, the user agent must run the following steps:
     // Once the user agent stops parsing the document, the user agent must run the following steps:
 
 
     // NOTE: This is a static method because the spec sometimes wants us to "act as if the user agent had stopped
     // NOTE: This is a static method because the spec sometimes wants us to "act as if the user agent had stopped
@@ -281,10 +283,10 @@ void HTMLParser::the_end(JS::NonnullGCPtr<DOM::Document> document, JS::GCPtr<HTM
     while (!document->scripts_to_execute_when_parsing_has_finished().is_empty()) {
     while (!document->scripts_to_execute_when_parsing_has_finished().is_empty()) {
         // 1. Spin the event loop until the first script in the list of scripts that will execute when the document has finished parsing
         // 1. Spin the event loop until the first script in the list of scripts that will execute when the document has finished parsing
         //    has its "ready to be parser-executed" flag set and the parser's Document has no style sheet that is blocking scripts.
         //    has its "ready to be parser-executed" flag set and the parser's Document has no style sheet that is blocking scripts.
-        main_thread_event_loop().spin_until([&] {
+        main_thread_event_loop().spin_until(JS::create_heap_function(heap, [&] {
             return document->scripts_to_execute_when_parsing_has_finished().first()->is_ready_to_be_parser_executed()
             return document->scripts_to_execute_when_parsing_has_finished().first()->is_ready_to_be_parser_executed()
                 && !document->has_a_style_sheet_that_is_blocking_scripts();
                 && !document->has_a_style_sheet_that_is_blocking_scripts();
-        });
+        }));
 
 
         // 2. Execute the first script in the list of scripts that will execute when the document has finished parsing.
         // 2. Execute the first script in the list of scripts that will execute when the document has finished parsing.
         document->scripts_to_execute_when_parsing_has_finished().first()->execute_script();
         document->scripts_to_execute_when_parsing_has_finished().first()->execute_script();
@@ -294,7 +296,7 @@ void HTMLParser::the_end(JS::NonnullGCPtr<DOM::Document> document, JS::GCPtr<HTM
     }
     }
 
 
     // 6. Queue a global task on the DOM manipulation task source given the Document's relevant global object to run the following substeps:
     // 6. Queue a global task on the DOM manipulation task source given the Document's relevant global object to run the following substeps:
-    queue_global_task(HTML::Task::Source::DOMManipulation, *document, JS::create_heap_function(document->heap(), [document = document] {
+    queue_global_task(HTML::Task::Source::DOMManipulation, *document, JS::create_heap_function(heap, [document = document] {
         // 1. Set the Document's load timing info's DOM content loaded event start time to the current high resolution time given the Document's relevant global object.
         // 1. Set the Document's load timing info's DOM content loaded event start time to the current high resolution time given the Document's relevant global object.
         document->load_timing_info().dom_content_loaded_event_start_time = HighResolutionTime::current_high_resolution_time(relevant_global_object(*document));
         document->load_timing_info().dom_content_loaded_event_start_time = HighResolutionTime::current_high_resolution_time(relevant_global_object(*document));
 
 
@@ -312,14 +314,14 @@ void HTMLParser::the_end(JS::NonnullGCPtr<DOM::Document> document, JS::GCPtr<HTM
     }));
     }));
 
 
     // 7. Spin the event loop until the set of scripts that will execute as soon as possible and the list of scripts that will execute in order as soon as possible are empty.
     // 7. Spin the event loop until the set of scripts that will execute as soon as possible and the list of scripts that will execute in order as soon as possible are empty.
-    main_thread_event_loop().spin_until([&] {
+    main_thread_event_loop().spin_until(JS::create_heap_function(heap, [&] {
         return document->scripts_to_execute_as_soon_as_possible().is_empty();
         return document->scripts_to_execute_as_soon_as_possible().is_empty();
-    });
+    }));
 
 
     // 8. Spin the event loop until there is nothing that delays the load event in the Document.
     // 8. Spin the event loop until there is nothing that delays the load event in the Document.
-    main_thread_event_loop().spin_until([&] {
+    main_thread_event_loop().spin_until(JS::create_heap_function(heap, [&] {
         return !document->anything_is_delaying_the_load_event();
         return !document->anything_is_delaying_the_load_event();
-    });
+    }));
 
 
     // 9. Queue a global task on the DOM manipulation task source given the Document's relevant global object to run the following steps:
     // 9. Queue a global task on the DOM manipulation task source given the Document's relevant global object to run the following steps:
     queue_global_task(HTML::Task::Source::DOMManipulation, *document, JS::create_heap_function(document->heap(), [document = document] {
     queue_global_task(HTML::Task::Source::DOMManipulation, *document, JS::create_heap_function(document->heap(), [document = document] {
@@ -2940,9 +2942,9 @@ void HTMLParser::handle_text(HTMLToken& token)
                     if (m_document->has_a_style_sheet_that_is_blocking_scripts() || the_script->is_ready_to_be_parser_executed() == false) {
                     if (m_document->has_a_style_sheet_that_is_blocking_scripts() || the_script->is_ready_to_be_parser_executed() == false) {
                         // spin the event loop until the parser's Document has no style sheet that is blocking scripts
                         // spin the event loop until the parser's Document has no style sheet that is blocking scripts
                         // and the script's ready to be parser-executed becomes true.
                         // and the script's ready to be parser-executed becomes true.
-                        main_thread_event_loop().spin_until([&] {
+                        main_thread_event_loop().spin_until(JS::create_heap_function(heap(), [&] {
                             return !m_document->has_a_style_sheet_that_is_blocking_scripts() && the_script->is_ready_to_be_parser_executed();
                             return !m_document->has_a_style_sheet_that_is_blocking_scripts() && the_script->is_ready_to_be_parser_executed();
-                        });
+                        }));
                     }
                     }
 
 
                     // 6. If this parser has been aborted in the meantime, return.
                     // 6. If this parser has been aborted in the meantime, return.

+ 2 - 2
Userland/Libraries/LibWeb/HTML/Scripting/Fetching.cpp

@@ -505,9 +505,9 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<ClassicScript>> fetch_a_classic_worker_impo
 
 
     // 5. Pause until response is not null.
     // 5. Pause until response is not null.
     auto& event_loop = settings_object.responsible_event_loop();
     auto& event_loop = settings_object.responsible_event_loop();
-    event_loop.spin_until([&]() {
+    event_loop.spin_until(JS::create_heap_function(vm.heap(), [&]() -> bool {
         return response;
         return response;
-    });
+    }));
 
 
     // 6. Set response to response's unsafe response.
     // 6. Set response to response's unsafe response.
     response = response->unsafe_response();
     response = response->unsafe_response();

+ 11 - 11
Userland/Libraries/LibWeb/HTML/TraversableNavigable.cpp

@@ -659,16 +659,16 @@ TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_history_
         }));
         }));
     }
     }
 
 
-    auto check_if_document_population_tasks_completed = JS::SafeFunction<bool()>([&] {
+    auto check_if_document_population_tasks_completed = JS::create_heap_function(heap(), [&] {
         return changing_navigable_continuations.size() + completed_change_jobs == total_change_jobs;
         return changing_navigable_continuations.size() + completed_change_jobs == total_change_jobs;
     });
     });
 
 
     if (synchronous_navigation == SynchronousNavigation::Yes) {
     if (synchronous_navigation == SynchronousNavigation::Yes) {
         // NOTE: Synchronous navigation should never require document population, so it is safe to process only NavigationAndTraversal source.
         // NOTE: Synchronous navigation should never require document population, so it is safe to process only NavigationAndTraversal source.
-        main_thread_event_loop().spin_processing_tasks_with_source_until(Task::Source::NavigationAndTraversal, move(check_if_document_population_tasks_completed));
+        main_thread_event_loop().spin_processing_tasks_with_source_until(Task::Source::NavigationAndTraversal, check_if_document_population_tasks_completed);
     } else {
     } else {
         // NOTE: Process all task sources while waiting because reloading or back/forward navigation might require fetching to populate a document.
         // NOTE: Process all task sources while waiting because reloading or back/forward navigation might require fetching to populate a document.
-        main_thread_event_loop().spin_until(move(check_if_document_population_tasks_completed));
+        main_thread_event_loop().spin_until(check_if_document_population_tasks_completed);
     }
     }
 
 
     // 13. Let navigablesThatMustWaitBeforeHandlingSyncNavigation be an empty set.
     // 13. Let navigablesThatMustWaitBeforeHandlingSyncNavigation be an empty set.
@@ -788,9 +788,9 @@ TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_history_
         }
         }
     }
     }
 
 
-    main_thread_event_loop().spin_processing_tasks_with_source_until(Task::Source::NavigationAndTraversal, [&] {
+    main_thread_event_loop().spin_processing_tasks_with_source_until(Task::Source::NavigationAndTraversal, JS::create_heap_function(heap(), [&] {
         return completed_change_jobs == total_change_jobs;
         return completed_change_jobs == total_change_jobs;
-    });
+    }));
 
 
     // 15. Let totalNonchangingJobs be the size of nonchangingNavigablesThatStillNeedUpdates.
     // 15. Let totalNonchangingJobs be the size of nonchangingNavigablesThatStillNeedUpdates.
     auto total_non_changing_jobs = non_changing_navigables_that_still_need_updates.size();
     auto total_non_changing_jobs = non_changing_navigables_that_still_need_updates.size();
@@ -836,9 +836,9 @@ TraversableNavigable::HistoryStepResult TraversableNavigable::apply_the_history_
     // AD-HOC: Since currently populate_session_history_entry_document does not run in parallel
     // AD-HOC: Since currently populate_session_history_entry_document does not run in parallel
     //         we call spin_until to interrupt execution of this function and let document population
     //         we call spin_until to interrupt execution of this function and let document population
     //         to complete.
     //         to complete.
-    main_thread_event_loop().spin_processing_tasks_with_source_until(Task::Source::NavigationAndTraversal, [&] {
+    main_thread_event_loop().spin_processing_tasks_with_source_until(Task::Source::NavigationAndTraversal, JS::create_heap_function(heap(), [&] {
         return completed_non_changing_jobs == total_non_changing_jobs;
         return completed_non_changing_jobs == total_non_changing_jobs;
-    });
+    }));
 
 
     // 20. Set traversable's current session history step to targetStep.
     // 20. Set traversable's current session history step to targetStep.
     m_current_session_history_step = target_step;
     m_current_session_history_step = target_step;
@@ -941,9 +941,9 @@ TraversableNavigable::CheckIfUnloadingIsCanceledResult TraversableNavigable::che
             }));
             }));
 
 
             // 6. Wait for eventsFired to be true.
             // 6. Wait for eventsFired to be true.
-            main_thread_event_loop().spin_until([&] {
+            main_thread_event_loop().spin_until(JS::create_heap_function(heap(), [&] {
                 return events_fired;
                 return events_fired;
-            });
+            }));
 
 
             // 7. If finalStatus is not "continue", then return finalStatus.
             // 7. If finalStatus is not "continue", then return finalStatus.
             if (final_status != CheckIfUnloadingIsCanceledResult::Continue)
             if (final_status != CheckIfUnloadingIsCanceledResult::Continue)
@@ -977,9 +977,9 @@ TraversableNavigable::CheckIfUnloadingIsCanceledResult TraversableNavigable::che
     }
     }
 
 
     // 8. Wait for completedTasks to be totalTasks.
     // 8. Wait for completedTasks to be totalTasks.
-    main_thread_event_loop().spin_until([&] {
+    main_thread_event_loop().spin_until(JS::create_heap_function(heap(), [&] {
         return completed_tasks == total_tasks;
         return completed_tasks == total_tasks;
-    });
+    }));
 
 
     // 9. Return finalStatus.
     // 9. Return finalStatus.
     return final_status;
     return final_status;

+ 1 - 1
Userland/Libraries/LibWeb/SVG/SVGScriptElement.cpp

@@ -61,7 +61,7 @@ void SVGScriptElement::process_the_script_element()
     // https://html.spec.whatwg.org/multipage/document-lifecycle.html#read-html
     // https://html.spec.whatwg.org/multipage/document-lifecycle.html#read-html
     // Before any script execution occurs, the user agent must wait for scripts may run for the newly-created document to be true for document.
     // Before any script execution occurs, the user agent must wait for scripts may run for the newly-created document to be true for document.
     if (!m_document->ready_to_run_scripts())
     if (!m_document->ready_to_run_scripts())
-        HTML::main_thread_event_loop().spin_until([&] { return m_document->ready_to_run_scripts(); });
+        HTML::main_thread_event_loop().spin_until(JS::create_heap_function(heap(), [&] { return m_document->ready_to_run_scripts(); }));
 
 
     // FIXME: Support non-inline scripts.
     // FIXME: Support non-inline scripts.
     auto& settings_object = document().relevant_settings_object();
     auto& settings_object = document().relevant_settings_object();

+ 2 - 2
Userland/Libraries/LibWeb/ServiceWorker/Job.cpp

@@ -359,7 +359,7 @@ static void update(JS::VM& vm, JS::NonnullGCPtr<Job> job)
 
 
         // FIXME: This feels.. uncomfortable but it should work to block the current task until the fetch has progressed past our processResponse hook or aborted
         // FIXME: This feels.. uncomfortable but it should work to block the current task until the fetch has progressed past our processResponse hook or aborted
         auto& event_loop = job->client ? job->client->responsible_event_loop() : HTML::main_thread_event_loop();
         auto& event_loop = job->client ? job->client->responsible_event_loop() : HTML::main_thread_event_loop();
-        event_loop.spin_until([fetch_controller, &realm, &process_response_completion_result]() -> bool {
+        event_loop.spin_until(JS::create_heap_function(realm.heap(), [fetch_controller, &realm, &process_response_completion_result]() -> bool {
             if (process_response_completion_result.has_value())
             if (process_response_completion_result.has_value())
                 return true;
                 return true;
             if (fetch_controller->state() == Fetch::Infrastructure::FetchController::State::Terminated || fetch_controller->state() == Fetch::Infrastructure::FetchController::State::Aborted) {
             if (fetch_controller->state() == Fetch::Infrastructure::FetchController::State::Terminated || fetch_controller->state() == Fetch::Infrastructure::FetchController::State::Aborted) {
@@ -367,7 +367,7 @@ static void update(JS::VM& vm, JS::NonnullGCPtr<Job> job)
                 return true;
                 return true;
             }
             }
             return false;
             return false;
-        });
+        }));
 
 
         return process_response_completion_result.release_value();
         return process_response_completion_result.release_value();
     };
     };

+ 10 - 8
Userland/Libraries/LibWeb/XML/XMLDocumentBuilder.cpp

@@ -177,9 +177,9 @@ void XMLDocumentBuilder::element_end(const XML::Name& name)
 
 
             // 2. Spin the event loop until the parser's Document has no style sheet that is blocking scripts and the pending parsing-blocking script's "ready to be parser-executed" flag is set.
             // 2. Spin the event loop until the parser's Document has no style sheet that is blocking scripts and the pending parsing-blocking script's "ready to be parser-executed" flag is set.
             if (m_document->has_a_style_sheet_that_is_blocking_scripts() || !pending_parsing_blocking_script->is_ready_to_be_parser_executed()) {
             if (m_document->has_a_style_sheet_that_is_blocking_scripts() || !pending_parsing_blocking_script->is_ready_to_be_parser_executed()) {
-                HTML::main_thread_event_loop().spin_until([&] {
+                HTML::main_thread_event_loop().spin_until(JS::create_heap_function(script_element.heap(), [&] {
                     return !m_document->has_a_style_sheet_that_is_blocking_scripts() && pending_parsing_blocking_script->is_ready_to_be_parser_executed();
                     return !m_document->has_a_style_sheet_that_is_blocking_scripts() && pending_parsing_blocking_script->is_ready_to_be_parser_executed();
-                });
+                }));
             }
             }
 
 
             // 3. Unblock this instance of the XML parser, such that tasks that invoke it can again be run.
             // 3. Unblock this instance of the XML parser, such that tasks that invoke it can again be run.
@@ -231,6 +231,8 @@ void XMLDocumentBuilder::comment(StringView data)
 
 
 void XMLDocumentBuilder::document_end()
 void XMLDocumentBuilder::document_end()
 {
 {
+    auto& heap = m_document->heap();
+
     // When an XML parser reaches the end of its input, it must stop parsing.
     // When an XML parser reaches the end of its input, it must stop parsing.
     // If the active speculative HTML parser is not null, then stop the speculative HTML parser and return.
     // If the active speculative HTML parser is not null, then stop the speculative HTML parser and return.
     // NOTE: Noop.
     // NOTE: Noop.
@@ -248,10 +250,10 @@ void XMLDocumentBuilder::document_end()
     while (!m_document->scripts_to_execute_when_parsing_has_finished().is_empty()) {
     while (!m_document->scripts_to_execute_when_parsing_has_finished().is_empty()) {
         // Spin the event loop until the first script in the list of scripts that will execute when the document has finished parsing has its "ready to be parser-executed" flag set
         // Spin the event loop until the first script in the list of scripts that will execute when the document has finished parsing has its "ready to be parser-executed" flag set
         // and the parser's Document has no style sheet that is blocking scripts.
         // and the parser's Document has no style sheet that is blocking scripts.
-        HTML::main_thread_event_loop().spin_until([&] {
+        HTML::main_thread_event_loop().spin_until(JS::create_heap_function(heap, [&] {
             return m_document->scripts_to_execute_when_parsing_has_finished().first()->is_ready_to_be_parser_executed()
             return m_document->scripts_to_execute_when_parsing_has_finished().first()->is_ready_to_be_parser_executed()
                 && !m_document->has_a_style_sheet_that_is_blocking_scripts();
                 && !m_document->has_a_style_sheet_that_is_blocking_scripts();
-        });
+        }));
 
 
         // Execute the first script in the list of scripts that will execute when the document has finished parsing.
         // Execute the first script in the list of scripts that will execute when the document has finished parsing.
         m_document->scripts_to_execute_when_parsing_has_finished().first()->execute_script();
         m_document->scripts_to_execute_when_parsing_has_finished().first()->execute_script();
@@ -278,14 +280,14 @@ void XMLDocumentBuilder::document_end()
     }));
     }));
 
 
     // Spin the event loop until the set of scripts that will execute as soon as possible and the list of scripts that will execute in order as soon as possible are empty.
     // Spin the event loop until the set of scripts that will execute as soon as possible and the list of scripts that will execute in order as soon as possible are empty.
-    HTML::main_thread_event_loop().spin_until([&] {
+    HTML::main_thread_event_loop().spin_until(JS::create_heap_function(heap, [&] {
         return m_document->scripts_to_execute_as_soon_as_possible().is_empty();
         return m_document->scripts_to_execute_as_soon_as_possible().is_empty();
-    });
+    }));
 
 
     // Spin the event loop until there is nothing that delays the load event in the Document.
     // Spin the event loop until there is nothing that delays the load event in the Document.
-    HTML::main_thread_event_loop().spin_until([&] {
+    HTML::main_thread_event_loop().spin_until(JS::create_heap_function(heap, [&] {
         return !m_document->anything_is_delaying_the_load_event();
         return !m_document->anything_is_delaying_the_load_event();
-    });
+    }));
 
 
     // Queue a global task on the DOM manipulation task source given the Document's relevant global object to run the following steps:
     // Queue a global task on the DOM manipulation task source given the Document's relevant global object to run the following steps:
     queue_global_task(HTML::Task::Source::DOMManipulation, m_document, JS::create_heap_function(m_document->heap(), [document = m_document] {
     queue_global_task(HTML::Task::Source::DOMManipulation, m_document, JS::create_heap_function(m_document->heap(), [document = m_document] {