mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-12-07 21:59:54 +00:00
LibWeb: Prevent view transition crashes due to lack of execution context
This commit is contained in:
parent
674e1a5c47
commit
556699d601
Notes:
github-actions[bot]
2025-11-19 15:59:40 +00:00
Author: https://github.com/Psychpsyo
Commit: 556699d601
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/6858
Reviewed-by: https://github.com/trflynn89 ✅
2 changed files with 10 additions and 13 deletions
|
|
@ -621,19 +621,18 @@ void ViewTransition::call_the_update_callback()
|
||||||
// We need to do it here manually.
|
// We need to do it here manually.
|
||||||
// https://webidl.spec.whatwg.org/#js-promise
|
// https://webidl.spec.whatwg.org/#js-promise
|
||||||
|
|
||||||
|
HTML::TemporaryExecutionContext context(realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
|
||||||
// 1. Let promiseCapability be ? NewPromiseCapability(%Promise%).
|
// 1. Let promiseCapability be ? NewPromiseCapability(%Promise%).
|
||||||
auto promise_capability = WebIDL::create_promise(realm);
|
auto promise_capability = WebIDL::create_promise(realm);
|
||||||
// 2. Perform ? Call(promiseCapability.[[Resolve]], undefined, « V »).
|
// 2. Perform ? Call(promiseCapability.[[Resolve]], undefined, « V »).
|
||||||
// FIXME: We should not need to push an incumbent realm here, but http://wpt.live/css/css-view-transitions/update-callback-timeout.html crashes without it.
|
|
||||||
HTML::main_thread_event_loop().push_onto_backup_incumbent_realm_stack(realm);
|
|
||||||
MUST(JS::call(realm.vm(), *promise_capability->resolve(), JS::js_undefined(), promise));
|
MUST(JS::call(realm.vm(), *promise_capability->resolve(), JS::js_undefined(), promise));
|
||||||
HTML::main_thread_event_loop().pop_backup_incumbent_realm_stack();
|
|
||||||
// 3. Return promiseCapability.
|
// 3. Return promiseCapability.
|
||||||
callback_promise = GC::make_root(promise_capability);
|
callback_promise = GC::make_root(promise_capability);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. Let fulfillSteps be to following steps:
|
// 6. Let fulfillSteps be to following steps:
|
||||||
auto fulfill_steps = GC::create_function(realm.heap(), [this, &realm](JS::Value) -> WebIDL::ExceptionOr<JS::Value> {
|
auto fulfill_steps = GC::create_function(realm.heap(), [this, &realm](JS::Value) -> WebIDL::ExceptionOr<JS::Value> {
|
||||||
|
HTML::TemporaryExecutionContext context(realm);
|
||||||
// 1. Resolve transition’s update callback done promise with undefined.
|
// 1. Resolve transition’s update callback done promise with undefined.
|
||||||
WebIDL::resolve_promise(realm, m_update_callback_done_promise, JS::js_undefined());
|
WebIDL::resolve_promise(realm, m_update_callback_done_promise, JS::js_undefined());
|
||||||
|
|
||||||
|
|
@ -645,6 +644,7 @@ void ViewTransition::call_the_update_callback()
|
||||||
|
|
||||||
// 7. Let rejectSteps be the following steps given reason:
|
// 7. Let rejectSteps be the following steps given reason:
|
||||||
auto reject_steps = GC::create_function(realm.heap(), [this, &realm](JS::Value reason) -> WebIDL::ExceptionOr<JS::Value> {
|
auto reject_steps = GC::create_function(realm.heap(), [this, &realm](JS::Value reason) -> WebIDL::ExceptionOr<JS::Value> {
|
||||||
|
HTML::TemporaryExecutionContext context(realm);
|
||||||
// 1. Reject transition’s update callback done promise with reason.
|
// 1. Reject transition’s update callback done promise with reason.
|
||||||
WebIDL::reject_promise(realm, m_update_callback_done_promise, reason);
|
WebIDL::reject_promise(realm, m_update_callback_done_promise, reason);
|
||||||
|
|
||||||
|
|
@ -665,12 +665,8 @@ void ViewTransition::call_the_update_callback()
|
||||||
});
|
});
|
||||||
|
|
||||||
// 8. React to callbackPromise with fulfillSteps and rejectSteps.
|
// 8. React to callbackPromise with fulfillSteps and rejectSteps.
|
||||||
// AD-HOC: This can cause an assertion failure when the reaction algorithm ends up accessing the incumbent realm, which may not exist here.
|
HTML::TemporaryExecutionContext context(realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
|
||||||
// For now, lets just manually push something onto the incumbent realm stack here as a hack.
|
|
||||||
// A spec bug for this has been filed at https://github.com/w3c/csswg-drafts/issues/11990
|
|
||||||
HTML::main_thread_event_loop().push_onto_backup_incumbent_realm_stack(realm);
|
|
||||||
WebIDL::react_to_promise(*callback_promise, fulfill_steps, reject_steps);
|
WebIDL::react_to_promise(*callback_promise, fulfill_steps, reject_steps);
|
||||||
HTML::main_thread_event_loop().pop_backup_incumbent_realm_stack();
|
|
||||||
|
|
||||||
// 9. To skip a transition after a timeout, the user agent may perform the following steps in parallel:
|
// 9. To skip a transition after a timeout, the user agent may perform the following steps in parallel:
|
||||||
// FIXME: Figure out if we want to do this.
|
// FIXME: Figure out if we want to do this.
|
||||||
|
|
@ -729,12 +725,8 @@ void ViewTransition::skip_the_view_transition(JS::Value reason)
|
||||||
|
|
||||||
// 8. Resolve transition’s finished promise with the result of reacting to transition’s update callback done promise:
|
// 8. Resolve transition’s finished promise with the result of reacting to transition’s update callback done promise:
|
||||||
// - If the promise was fulfilled, then return undefined.
|
// - If the promise was fulfilled, then return undefined.
|
||||||
// AD-HOC: This can cause an assertion failure when the reaction algorithm ends up accessing the incumbent realm, which may not exist here.
|
HTML::TemporaryExecutionContext context(realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
|
||||||
// For now, lets just manually push something onto the incumbent realm stack here as a hack.
|
|
||||||
// A spec bug for this has been filed at https://github.com/w3c/csswg-drafts/issues/11990
|
|
||||||
HTML::main_thread_event_loop().push_onto_backup_incumbent_realm_stack(realm);
|
|
||||||
WebIDL::resolve_promise(realm, m_finished_promise, WebIDL::react_to_promise(m_update_callback_done_promise, GC::create_function(realm.heap(), [](JS::Value) -> WebIDL::ExceptionOr<JS::Value> { return JS::js_undefined(); }), nullptr)->promise());
|
WebIDL::resolve_promise(realm, m_finished_promise, WebIDL::react_to_promise(m_update_callback_done_promise, GC::create_function(realm.heap(), [](JS::Value) -> WebIDL::ExceptionOr<JS::Value> { return JS::js_undefined(); }), nullptr)->promise());
|
||||||
HTML::main_thread_event_loop().pop_backup_incumbent_realm_stack();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-view-transitions-1/#handle-transition-frame
|
// https://drafts.csswg.org/css-view-transitions-1/#handle-transition-frame
|
||||||
|
|
|
||||||
5
Tests/LibWeb/Crash/CSS/view-transition-crash.html
Normal file
5
Tests/LibWeb/Crash/CSS/view-transition-crash.html
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<script>
|
||||||
|
document.startViewTransition(() => {});
|
||||||
|
document.startViewTransition(() => {});
|
||||||
|
</script>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue