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

Code before:
        //    the load failed.
        m_media_element->fetch_resource(*url_record, [self = GC::make_root(this)](auto) { self->failed_with_elements().release_value_but_fixme_should_propagate_errors(); });

        return {};
    }

    WebIDL::ExceptionOr<void> process_next_candidate()
    {
        if (!m_previously_failed_candidate)
            return {};

        TRY(wait_for_next_candidate(*m_previously_failed_candidate));
        return {};
    }

private:
    WebIDL::ExceptionOr<void> failed_with_elements()
    {
        // 9. Failed with elements: Queue a media element task given the media element to fire an event named error at candidate.
        m_media_element->queue_a_media_element_task([this]() {
            m_candidate->dispatch_event(DOM::Event::create(m_candidate->realm(), HTML::EventNames::error));
        });

        // 10. Await a stable state. The synchronous section consists of all the remaining steps of this algorithm until
        //     the algorithm says the synchronous section has ended. (Steps in synchronous sections are marked with ⌛.)
        // NB: We're already in a stable state here.

        // 11. ⌛ Forget the media element's media-resource-specific tracks.
        m_media_element->forget_media_resource_specific_tracks();

        TRY(find_next_candidate(m_candidate));
        return {};
    }

    WebIDL::ExceptionOr<void> find_next_candidate(GC::Ref<DOM::Node> previous_candidate)
    {
        // 12. ⌛ Find next candidate: Let candidate be null.
        GC::Ptr<HTMLSourceElement> candidate;

        // 13. ⌛ Search loop: If the node after pointer is the end of the list, then jump to the waiting step below.
        auto* next_sibling = previous_candidate->next_sibling();
        if (!next_sibling) {
            TRY(waiting(previous_candidate));
            return {};
        }

        // 14. ⌛ If the node after pointer is a source element, let candidate be that element.
        if (auto* source_element = as_if<HTMLSourceElement>(next_sibling))
            candidate = source_element;

        // 15. ⌛ Advance pointer so that the node before pointer is now the node that was after pointer, and the node

Review comment (suggestion):
My understanding of the normative requirements here is a little different - my understanding is that this is not the specs only requirement here - https://html.spec.whatwg.org/#await-a-stable-state says that awaiting a stable state should be invoked inside of a queued micro task, which indicates to me that the timing of find_next_candidate is not immediate with respect to javascript

Code after:
        // 7. ⌛ Set the currentSrc attribute to the result of applying the URL serializer to urlRecord.
        m_media_element->m_current_src = url_record->serialize();

        // 8. End the synchronous section, continuing the remaining steps in parallel.

        // 9. Run the resource fetch algorithm with urlRecord. If that algorithm returns without aborting this one, then
        //    the load failed.
        m_media_element->fetch_resource(*url_record, [self = GC::make_root(this)](auto const&) { self->failed_with_elements(); });
    }

    void process_next_candidate()
    {
        if (!m_previously_failed_candidate)
            return;

        wait_for_next_candidate(*m_previously_failed_candidate);
    }

private:
    void failed_with_elements()
    {
        // 9. Failed with elements: Queue a media element task given the media element to fire an event named error at candidate.
        m_media_element->queue_a_media_element_task([this]() {
            m_candidate->dispatch_event(DOM::Event::create(m_candidate->realm(), HTML::EventNames::error));

            // 10. Await a stable state. The synchronous section consists of all the remaining steps of this algorithm until
            //     the algorithm says the synchronous section has ended. (Steps in synchronous sections are marked with ⌛.)
            // FIXME: Also run the next steps within this task, instead of waiting for a stable state.
            //        This seems to match the intent in https://github.com/whatwg/html/issues/2882#issuecomment-1108531815.
            //        Update this if the spec is clarified in regard to this step.

            // 11. ⌛ Forget the media element's media-resource-specific tracks.
            m_media_element->forget_media_resource_specific_tracks();

            find_next_candidate(m_candidate);
        });
    }

    void find_next_candidate(GC::Ref<DOM::Node> previous_candidate)
    {
        // 12. ⌛ Find next candidate: Let candidate be null.
        GC::Ptr<HTMLSourceElement> candidate;

        // 13. ⌛ Search loop: If the node after pointer is the end of the list, then jump to the waiting step below.
        auto* next_sibling = previous_candidate->next_sibling();
        if (!next_sibling) {
            waiting(previous_candidate);
            return;
        }

        // 14. ⌛ If the node after pointer is a source element, let candidate be that element.