1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 07:17:35 +00:00

LibWeb: Update workarounds for the empty execution context stack

Use the new helper class to perform this workaround.
This commit is contained in:
Timothy Flynn 2023-07-06 07:43:23 -04:00 committed by Tim Flynn
parent 8ec7b4401a
commit f57310999d
7 changed files with 30 additions and 88 deletions

View file

@ -16,6 +16,7 @@
#include <LibWeb/Fetch/Body.h> #include <LibWeb/Fetch/Body.h>
#include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h> #include <LibWeb/Fetch/Infrastructure/HTTP/Bodies.h>
#include <LibWeb/FileAPI/Blob.h> #include <LibWeb/FileAPI/Blob.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/Infra/JSON.h> #include <LibWeb/Infra/JSON.h>
#include <LibWeb/MimeSniff/MimeType.h> #include <LibWeb/MimeSniff/MimeType.h>
#include <LibWeb/Streams/ReadableStream.h> #include <LibWeb/Streams/ReadableStream.h>
@ -165,15 +166,9 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> consume_body(JS::Realm& realm
// 3. Let errorSteps given error be to reject promise with error. // 3. Let errorSteps given error be to reject promise with error.
// NOTE: `promise` and `realm` is protected by JS::SafeFunction. // NOTE: `promise` and `realm` is protected by JS::SafeFunction.
auto error_steps = [promise, &realm](JS::GCPtr<WebIDL::DOMException> error) { auto error_steps = [promise, &realm](JS::GCPtr<WebIDL::DOMException> error) {
// NOTE: Not part of the spec, but we need to have an execution context on the stack to call native functions. // AD-HOC: An execution context is required for Promise's reject function.
// (In this case, Promise's reject function) HTML::TemporaryExecutionContext execution_context { Bindings::host_defined_environment_settings_object(realm) };
auto& environment_settings_object = Bindings::host_defined_environment_settings_object(realm);
environment_settings_object.prepare_to_run_script();
WebIDL::reject_promise(realm, promise, error); WebIDL::reject_promise(realm, promise, error);
// See above NOTE.
environment_settings_object.clean_up_after_running_script();
}; };
// 4. Let successSteps given a byte sequence data be to resolve promise with the result of running convertBytesToJSValue // 4. Let successSteps given a byte sequence data be to resolve promise with the result of running convertBytesToJSValue
@ -183,15 +178,8 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::Promise>> consume_body(JS::Realm& realm
auto success_steps = [promise, &realm, &object, type](ByteBuffer const& data) { auto success_steps = [promise, &realm, &object, type](ByteBuffer const& data) {
auto& vm = realm.vm(); auto& vm = realm.vm();
// NOTE: Not part of the spec, but we need to have an execution context on the stack to call native functions. // AD-HOC: An execution context is required for Promise's reject function and JSON.parse.
// (In this case, Promise's reject function and JSON.parse) HTML::TemporaryExecutionContext execution_context { Bindings::host_defined_environment_settings_object(realm) };
auto& environment_settings_object = Bindings::host_defined_environment_settings_object(realm);
environment_settings_object.prepare_to_run_script();
ScopeGuard guard = [&]() {
// See above NOTE.
environment_settings_object.clean_up_after_running_script();
};
auto value_or_error = Bindings::throw_dom_exception_if_needed(vm, [&]() -> WebIDL::ExceptionOr<JS::Value> { auto value_or_error = Bindings::throw_dom_exception_if_needed(vm, [&]() -> WebIDL::ExceptionOr<JS::Value> {
return package_data(realm, data, type, TRY_OR_THROW_OOM(vm, object.mime_type_impl())); return package_data(realm, data, type, TRY_OR_THROW_OOM(vm, object.mime_type_impl()));

View file

@ -19,6 +19,7 @@
#include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h> #include <LibWeb/Fetch/Infrastructure/HTTP/Responses.h>
#include <LibWeb/Fetch/Request.h> #include <LibWeb/Fetch/Request.h>
#include <LibWeb/Fetch/Response.h> #include <LibWeb/Fetch/Response.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/WebIDL/ExceptionOr.h> #include <LibWeb/WebIDL/ExceptionOr.h>
#include <LibWeb/WebIDL/Promise.h> #include <LibWeb/WebIDL/Promise.h>
@ -85,15 +86,8 @@ JS::NonnullGCPtr<JS::Promise> fetch(JS::VM& vm, RequestInfo const& input, Reques
if (locally_aborted->value()) if (locally_aborted->value())
return; return;
// NOTE: Not part of the spec, but we need to have an execution context on the stack to call native functions. // AD-HOC: An execution context is required for Promise functions.
// (In this case, Promise functions) HTML::TemporaryExecutionContext execution_context { Bindings::host_defined_environment_settings_object(relevant_realm) };
auto& environment_settings_object = Bindings::host_defined_environment_settings_object(relevant_realm);
environment_settings_object.prepare_to_run_script();
ScopeGuard guard = [&]() {
// See above NOTE.
environment_settings_object.clean_up_after_running_script();
};
// 2. If responses aborted flag is set, then: // 2. If responses aborted flag is set, then:
if (response->aborted()) { if (response->aborted()) {

View file

@ -14,6 +14,7 @@
#include <LibWeb/Bindings/ExceptionOrUtils.h> #include <LibWeb/Bindings/ExceptionOrUtils.h>
#include <LibWeb/Bindings/Intrinsics.h> #include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/FileAPI/Blob.h> #include <LibWeb/FileAPI/Blob.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/Infra/Strings.h> #include <LibWeb/Infra/Strings.h>
#include <LibWeb/Streams/AbstractOperations.h> #include <LibWeb/Streams/AbstractOperations.h>
#include <LibWeb/Streams/ReadableStreamDefaultReader.h> #include <LibWeb/Streams/ReadableStreamDefaultReader.h>
@ -282,14 +283,7 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<Streams::ReadableStream>> Blob::get_stream(
// 2. Queue a global task on the file reading task source given blobs relevant global object to perform the following steps: // 2. Queue a global task on the file reading task source given blobs relevant global object to perform the following steps:
HTML::queue_global_task(HTML::Task::Source::FileReading, realm.global_object(), [stream, bytes = move(bytes)]() { HTML::queue_global_task(HTML::Task::Source::FileReading, realm.global_object(), [stream, bytes = move(bytes)]() {
// NOTE: Not part of the spec, but we need to have an execution context on the stack to call native functions. HTML::TemporaryExecutionContext execution_context { Bindings::host_defined_environment_settings_object(stream->realm()) };
auto& environment_settings_object = Bindings::host_defined_environment_settings_object(stream->realm());
environment_settings_object.prepare_to_run_script();
ScopeGuard guard = [&]() {
// See above NOTE.
environment_settings_object.clean_up_after_running_script();
};
// 1. If bytes is failure, then error stream with a failure reason and abort these steps. // 1. If bytes is failure, then error stream with a failure reason and abort these steps.
// 2. Let chunk be a new Uint8Array wrapping an ArrayBuffer containing bytes. If creating the ArrayBuffer throws an exception, then error stream with that exception and abort these steps. // 2. Let chunk be a new Uint8Array wrapping an ArrayBuffer containing bytes. If creating the ArrayBuffer throws an exception, then error stream with that exception and abort these steps.

View file

@ -28,6 +28,7 @@
#include <LibWeb/HTML/MediaError.h> #include <LibWeb/HTML/MediaError.h>
#include <LibWeb/HTML/PotentialCORSRequest.h> #include <LibWeb/HTML/PotentialCORSRequest.h>
#include <LibWeb/HTML/Scripting/Environments.h> #include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/HTML/TimeRanges.h> #include <LibWeb/HTML/TimeRanges.h>
#include <LibWeb/HTML/TrackEvent.h> #include <LibWeb/HTML/TrackEvent.h>
#include <LibWeb/HTML/VideoTrack.h> #include <LibWeb/HTML/VideoTrack.h>
@ -368,12 +369,8 @@ WebIDL::ExceptionOr<void> HTMLMediaElement::pause()
WebIDL::ExceptionOr<void> HTMLMediaElement::toggle_playback() WebIDL::ExceptionOr<void> HTMLMediaElement::toggle_playback()
{ {
// FIXME: This runs from outside the context of any user script, so we do not have a running execution // AD-HOC: An execution context is required for Promise creation hooks.
// context. This pushes one to allow the promise creation hook to run. TemporaryExecutionContext execution_context { document().relevant_settings_object() };
auto& environment_settings = document().relevant_settings_object();
environment_settings.prepare_to_run_script();
ScopeGuard guard { [&] { environment_settings.clean_up_after_running_script(); } };
if (potentially_playing()) if (potentially_playing())
TRY(pause()); TRY(pause());
@ -1826,17 +1823,13 @@ void HTMLMediaElement::resolve_pending_play_promises(ReadonlySpan<JS::NonnullGCP
{ {
auto& realm = this->realm(); auto& realm = this->realm();
// FIXME: This AO runs from the media element task queue, at which point we do not have a running execution // AD-HOC: An execution context is required for Promise resolving hooks.
// context. This pushes one to allow the promise resolving hook to run. TemporaryExecutionContext execution_context { document().relevant_settings_object() };
auto& environment_settings = document().relevant_settings_object();
environment_settings.prepare_to_run_script();
// To resolve pending play promises for a media element with a list of promises promises, the user agent // To resolve pending play promises for a media element with a list of promises promises, the user agent
// must resolve each promise in promises with undefined. // must resolve each promise in promises with undefined.
for (auto const& promise : promises) for (auto const& promise : promises)
WebIDL::resolve_promise(realm, promise, JS::js_undefined()); WebIDL::resolve_promise(realm, promise, JS::js_undefined());
environment_settings.clean_up_after_running_script();
} }
// https://html.spec.whatwg.org/multipage/media.html#reject-pending-play-promises // https://html.spec.whatwg.org/multipage/media.html#reject-pending-play-promises
@ -1844,17 +1837,13 @@ void HTMLMediaElement::reject_pending_play_promises(ReadonlySpan<JS::NonnullGCPt
{ {
auto& realm = this->realm(); auto& realm = this->realm();
// FIXME: This AO runs from the media element task queue, at which point we do not have a running execution // AD-HOC: An execution context is required for Promise rejection hooks.
// context. This pushes one to allow the promise rejection hook to run. TemporaryExecutionContext execution_context { document().relevant_settings_object() };
auto& environment_settings = document().relevant_settings_object();
environment_settings.prepare_to_run_script();
// To reject pending play promises for a media element with a list of promise promises and an exception name // To reject pending play promises for a media element with a list of promise promises and an exception name
// error, the user agent must reject each promise in promises with error. // error, the user agent must reject each promise in promises with error.
for (auto const& promise : promises) for (auto const& promise : promises)
WebIDL::reject_promise(realm, promise, error); WebIDL::reject_promise(realm, promise, error);
environment_settings.clean_up_after_running_script();
} }
WebIDL::ExceptionOr<void> HTMLMediaElement::handle_keydown(Badge<Web::EventHandler>, KeyCode key) WebIDL::ExceptionOr<void> HTMLMediaElement::handle_keydown(Badge<Web::EventHandler>, KeyCode key)

View file

@ -19,6 +19,7 @@
#include <LibWeb/HTML/Scripting/Environments.h> #include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/HTML/Scripting/Fetching.h> #include <LibWeb/HTML/Scripting/Fetching.h>
#include <LibWeb/HTML/Scripting/ModuleScript.h> #include <LibWeb/HTML/Scripting/ModuleScript.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/HTML/Window.h> #include <LibWeb/HTML/Window.h>
#include <LibWeb/Infra/Strings.h> #include <LibWeb/Infra/Strings.h>
#include <LibWeb/Loader/LoadRequest.h> #include <LibWeb/Loader/LoadRequest.h>
@ -568,9 +569,7 @@ void fetch_descendants_of_and_link_a_module_script(JavaScriptModuleScript& modul
return; return;
} }
// FIXME: This is an ad-hoc hack to make sure that there's an execution context on the VM stack in case linking throws an exception. TemporaryExecutionContext execution_context { fetch_client_settings_object };
auto& vm = fetch_client_settings_object.vm();
vm.push_execution_context(fetch_client_settings_object.realm_execution_context());
// FIXME: 2. Let parse error be the result of finding the first parse error given result. // FIXME: 2. Let parse error be the result of finding the first parse error given result.
@ -591,9 +590,6 @@ void fetch_descendants_of_and_link_a_module_script(JavaScriptModuleScript& modul
TODO(); TODO();
} }
// FIXME: This undoes the ad-hoc hack above.
vm.pop_execution_context();
// 5. Run onComplete given result. // 5. Run onComplete given result.
on_complete(result); on_complete(result);
}); });

View file

@ -14,6 +14,7 @@
#include <LibWeb/HTML/EventLoop/EventLoop.h> #include <LibWeb/HTML/EventLoop/EventLoop.h>
#include <LibWeb/HTML/HTMLMediaElement.h> #include <LibWeb/HTML/HTMLMediaElement.h>
#include <LibWeb/HTML/Scripting/Environments.h> #include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/Page/Page.h> #include <LibWeb/Page/Page.h>
#include <LibWeb/Platform/EventLoopPlugin.h> #include <LibWeb/Platform/EventLoopPlugin.h>
@ -300,12 +301,8 @@ WebIDL::ExceptionOr<void> Page::toggle_media_play_state()
if (!media_element) if (!media_element)
return {}; return {};
// FIXME: This runs from outside the context of any user script, so we do not have a running execution // AD-HOC: An execution context is required for Promise creation hooks.
// context. This pushes one to allow the promise creation hook to run. HTML::TemporaryExecutionContext execution_context { media_element->document().relevant_settings_object() };
auto& environment_settings = media_element->document().relevant_settings_object();
environment_settings.prepare_to_run_script();
ScopeGuard guard { [&] { environment_settings.clean_up_after_running_script(); } };
if (media_element->potentially_playing()) if (media_element->potentially_playing())
TRY(media_element->pause()); TRY(media_element->pause());
@ -321,12 +318,9 @@ void Page::toggle_media_mute_state()
if (!media_element) if (!media_element)
return; return;
// FIXME: This runs from outside the context of any user script, so we do not have a running execution // AD-HOC: An execution context is required for Promise creation hooks.
// context. This pushes one to allow the promise creation hook to run. HTML::TemporaryExecutionContext execution_context { media_element->document().relevant_settings_object() };
auto& environment_settings = media_element->document().relevant_settings_object();
environment_settings.prepare_to_run_script();
ScopeGuard guard { [&] { environment_settings.clean_up_after_running_script(); } };
media_element->set_muted(!media_element->muted()); media_element->set_muted(!media_element->muted());
} }
@ -336,12 +330,8 @@ WebIDL::ExceptionOr<void> Page::toggle_media_loop_state()
if (!media_element) if (!media_element)
return {}; return {};
// FIXME: This runs from outside the context of any user script, so we do not have a running execution // AD-HOC: An execution context is required for Promise creation hooks.
// context. This pushes one to allow the promise creation hook to run. HTML::TemporaryExecutionContext execution_context { media_element->document().relevant_settings_object() };
auto& environment_settings = media_element->document().relevant_settings_object();
environment_settings.prepare_to_run_script();
ScopeGuard guard { [&] { environment_settings.clean_up_after_running_script(); } };
if (media_element->has_attribute(HTML::AttributeNames::loop)) if (media_element->has_attribute(HTML::AttributeNames::loop))
media_element->remove_attribute(HTML::AttributeNames::loop); media_element->remove_attribute(HTML::AttributeNames::loop);
@ -357,12 +347,7 @@ WebIDL::ExceptionOr<void> Page::toggle_media_controls_state()
if (!media_element) if (!media_element)
return {}; return {};
// FIXME: This runs from outside the context of any user script, so we do not have a running execution HTML::TemporaryExecutionContext execution_context { media_element->document().relevant_settings_object() };
// context. This pushes one to allow the promise creation hook to run.
auto& environment_settings = media_element->document().relevant_settings_object();
environment_settings.prepare_to_run_script();
ScopeGuard guard { [&] { environment_settings.clean_up_after_running_script(); } };
if (media_element->has_attribute(HTML::AttributeNames::controls)) if (media_element->has_attribute(HTML::AttributeNames::controls))
media_element->remove_attribute(HTML::AttributeNames::controls); media_element->remove_attribute(HTML::AttributeNames::controls);

View file

@ -25,6 +25,7 @@
#include <LibWeb/HTML/BrowsingContext.h> #include <LibWeb/HTML/BrowsingContext.h>
#include <LibWeb/HTML/HTMLOptionsCollection.h> #include <LibWeb/HTML/HTMLOptionsCollection.h>
#include <LibWeb/HTML/Scripting/Environments.h> #include <LibWeb/HTML/Scripting/Environments.h>
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
#include <LibWeb/HTML/Window.h> #include <LibWeb/HTML/Window.h>
#include <LibWeb/Page/Page.h> #include <LibWeb/Page/Page.h>
#include <LibWeb/WebDriver/Contexts.h> #include <LibWeb/WebDriver/Contexts.h>
@ -325,18 +326,13 @@ ExecuteScriptResultSerialized execute_script(Web::Page& page, DeprecatedString c
ExecuteScriptResultSerialized execute_async_script(Web::Page& page, DeprecatedString const& body, JS::MarkedVector<JS::Value> arguments, Optional<u64> const& timeout) ExecuteScriptResultSerialized execute_async_script(Web::Page& page, DeprecatedString const& body, JS::MarkedVector<JS::Value> arguments, Optional<u64> const& timeout)
{ {
auto* document = page.top_level_browsing_context().active_document(); auto* document = page.top_level_browsing_context().active_document();
auto& settings_object = document->relevant_settings_object();
auto* window = page.top_level_browsing_context().active_window(); auto* window = page.top_level_browsing_context().active_window();
auto& realm = window->realm(); auto& realm = window->realm();
auto& vm = window->vm(); auto& vm = window->vm();
auto start = MonotonicTime::now(); auto start = MonotonicTime::now();
// NOTE: We need to push an execution context in order to make create_resolving_functions() succeed. // AD-HOC: An execution context is required for Promise creation hooks.
vm.push_execution_context(settings_object.realm_execution_context()); HTML::TemporaryExecutionContext execution_context { document->relevant_settings_object() };
ScopeGuard pop_guard = [&] {
VERIFY(&settings_object.realm_execution_context() == &vm.running_execution_context());
vm.pop_execution_context();
};
// 4. Let promise be a new Promise. // 4. Let promise be a new Promise.
auto promise_capability = WebIDL::create_promise(realm); auto promise_capability = WebIDL::create_promise(realm);