Repository: LadybirdBrowser/ladybird
File: Libraries/LibWeb/ViewTransition/ViewTransition.cpp (C++)

Code before:
    // 1. Assert: transition’s phase is "done", or before "update-callback-called".
    VERIFY(m_phase == Phase::Done || to_underlying(m_phase) < to_underlying(Phase::UpdateCallbackCalled));

    // 2. If transition’s phase is not "done", then set transition’s phase to "update-callback-called".
    if (m_phase != Phase::Done)
        m_phase = Phase::UpdateCallbackCalled;

    // 3. Let callbackPromise be null.
    WebIDL::Promise* callback_promise;

    // 4. If transition’s update callback is null, then set callbackPromise to a promise resolved with undefined, in
    //    transition’s relevant Realm.
    if (!m_update_callback) {
        auto& relevant_realm = HTML::relevant_realm(*this);
        callback_promise = WebIDL::create_promise(relevant_realm);
        WebIDL::resolve_promise(relevant_realm, *callback_promise, JS::js_undefined());
    }

    // 5. Otherwise, set callbackPromise to the result of invoking transition’s update callback.
    else {
        auto promise = MUST(WebIDL::invoke_callback(*m_update_callback, {}, {}));
        // FIXME: since WebIDL::invoke_callback does not yet convert the value for us,
        // We need to do it here manually.
        // https://webidl.spec.whatwg.org/#js-promise

        HTML::TemporaryExecutionContext context(realm);
        // 1. Let promiseCapability be ? NewPromiseCapability(%Promise%).
        auto promise_capability = WebIDL::create_promise(realm);
        // 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));
        HTML::main_thread_event_loop().pop_backup_incumbent_realm_stack();
        // 3. Return promiseCapability.
        callback_promise = GC::make_root(promise_capability);
    }

    // 6. Let fulfillSteps be to following steps:
    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.
        WebIDL::resolve_promise(realm, m_update_callback_done_promise, JS::js_undefined());

        // 2. Activate transition.
        activate_view_transition();

        return JS::js_undefined();
    });

    // 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> {

Review comment (suggestion):
If you pass in `CallbacksEnabled::Yes`, you should then not need to manually push/pop a realm below.

It looks like that FIXME was added in 56739b4b162f99ad2e1addbec179878175e2908d a couple months ago - in general, if you ever find yourself with an empty execution context or empty realm stack, that means you probably just need to bring in `TemporaryExecutionContext`.

Code after:
    // 1. Assert: transition’s phase is "done", or before "update-callback-called".
    VERIFY(m_phase == Phase::Done || to_underlying(m_phase) < to_underlying(Phase::UpdateCallbackCalled));

    // 2. If transition’s phase is not "done", then set transition’s phase to "update-callback-called".
    if (m_phase != Phase::Done)
        m_phase = Phase::UpdateCallbackCalled;

    // 3. Let callbackPromise be null.
    WebIDL::Promise* callback_promise;

    // 4. If transition’s update callback is null, then set callbackPromise to a promise resolved with undefined, in
    //    transition’s relevant Realm.
    if (!m_update_callback) {
        auto& relevant_realm = HTML::relevant_realm(*this);
        callback_promise = WebIDL::create_promise(relevant_realm);
        WebIDL::resolve_promise(relevant_realm, *callback_promise, JS::js_undefined());
    }

    // 5. Otherwise, set callbackPromise to the result of invoking transition’s update callback.
    else {
        auto promise = MUST(WebIDL::invoke_callback(*m_update_callback, {}, {}));
        // FIXME: since WebIDL::invoke_callback does not yet convert the value for us,
        // We need to do it here manually.
        // https://webidl.spec.whatwg.org/#js-promise

        HTML::TemporaryExecutionContext context(realm, HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
        // 1. Let promiseCapability be ? NewPromiseCapability(%Promise%).
        auto promise_capability = WebIDL::create_promise(realm);
        // 2. Perform ? Call(promiseCapability.[[Resolve]], undefined, « V »).
        MUST(JS::call(realm.vm(), *promise_capability->resolve(), JS::js_undefined(), promise));
        // 3. Return promiseCapability.
        callback_promise = GC::make_root(promise_capability);
    }

    // 6. Let fulfillSteps be to following steps:
    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.
        WebIDL::resolve_promise(realm, m_update_callback_done_promise, JS::js_undefined());

        // 2. Activate transition.
        activate_view_transition();

        return JS::js_undefined();
    });

    // 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> {
        HTML::TemporaryExecutionContext context(realm);
        // 1. Reject transition’s update callback done promise with reason.
        WebIDL::reject_promise(realm, m_update_callback_done_promise, reason);