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

Code before:
        auto process_request_end_of_body = [this, request_body_length]() {
            // 1. Set this’s upload complete flag.
            m_upload_complete = true;

            // 2. If this’s upload listener flag is unset, then return.
            if (!m_upload_listener)
                return;

            // 3. Fire a progress event named progress at this’s upload object with requestBodyTransmitted and requestBodyLength.
            fire_progress_event(m_upload_object, EventNames::progress, m_request_body_transmitted, request_body_length);

            // 4. Fire a progress event named load at this’s upload object with requestBodyTransmitted and requestBodyLength.
            fire_progress_event(m_upload_object, EventNames::load, m_request_body_transmitted, request_body_length);

            // 5. Fire a progress event named loadend at this’s upload object with requestBodyTransmitted and requestBodyLength.
            fire_progress_event(m_upload_object, EventNames::loadend, m_request_body_transmitted, request_body_length);
        };

        // 9. Let processResponse, given a response, be these steps:
        // NOTE: `this` is kept alive by FetchAlgorithms using GC::HeapFunction.
        auto process_response = [this](GC::Ref<Fetch::Infrastructure::Response> response) {
            // 1. Set this’s response to response.
            m_response = response;

            // 2. Handle errors for this.
            // NOTE: This cannot throw, as `handle_errors` only throws in a synchronous context.
            MUST(handle_errors());

            // 3. If this’s response is a network error, then return.
            if (m_response->is_network_error())
                return;

            // 4. Set this’s state to headers received.
            m_state = State::HeadersReceived;

            // 5. Fire an event named readystatechange at this.
            // FIXME: We're in an async context, so we can't propagate the error anywhere.
            dispatch_event(*DOM::Event::create(this->realm(), EventNames::readystatechange));

            // 6. If this’s state is not headers received, then return.
            if (m_state != State::HeadersReceived)
                return;

            // 7. If this’s response’s body is null, then run handle response end-of-body for this and return.
            if (!m_response->body()) {
                // NOTE: This cannot throw, as `handle_response_end_of_body` only throws in a synchronous context.
                MUST(handle_response_end_of_body());
                return;
            }

            // 8. Let length be the result of extracting a length from this’s response’s header list.

Review comment (bug):
I believe it's correct to `MUST` these, the only place this can throw is in `request_errors_steps()`, and then only when we are in a synchronous context, same goes for `handle_response_end_of_body()`

https://github.com/LadybirdBrowser/ladybird/blob/cb1a1a5cb5838489472c0135c17b57c1ef3c2b2c/Libraries/LibWeb/XHR/XMLHttpRequest.cpp#L1251-L1254

Code after:
            // 1. Set this’s upload complete flag.
            m_upload_complete = true;

            // 2. If this’s upload listener flag is unset, then return.
            if (!m_upload_listener)
                return;

            // 3. Fire a progress event named progress at this’s upload object with requestBodyTransmitted and requestBodyLength.
            fire_progress_event(m_upload_object, EventNames::progress, m_request_body_transmitted, request_body_length);

            // 4. Fire a progress event named load at this’s upload object with requestBodyTransmitted and requestBodyLength.
            fire_progress_event(m_upload_object, EventNames::load, m_request_body_transmitted, request_body_length);

            // 5. Fire a progress event named loadend at this’s upload object with requestBodyTransmitted and requestBodyLength.
            fire_progress_event(m_upload_object, EventNames::loadend, m_request_body_transmitted, request_body_length);
        };

        // 9. Let processResponse, given a response, be these steps:
        // NOTE: `this` is kept alive by FetchAlgorithms using GC::HeapFunction.
        auto process_response = [this](GC::Ref<Fetch::Infrastructure::Response> response) {
            // 1. Set this’s response to response.
            m_response = response;

            // 2. Handle errors for this.
            // NOTE: This cannot throw, as `handle_errors` only throws in a synchronous context.
            MUST(handle_errors());

            // 3. If this’s response is a network error, then return.
            if (m_response->is_network_error())
                return;

            // 4. Set this’s state to headers received.
            m_state = State::HeadersReceived;

            // 5. Fire an event named readystatechange at this.
            dispatch_event(*DOM::Event::create(this->realm(), EventNames::readystatechange));

            // 6. If this’s state is not headers received, then return.
            if (m_state != State::HeadersReceived)
                return;

            // 7. If this’s response’s body is null, then run handle response end-of-body for this and return.
            if (!m_response->body()) {
                // NOTE: This cannot throw, as `handle_response_end_of_body` only throws in a synchronous context.
                MUST(handle_response_end_of_body());
                return;
            }

            // 8. Let length be the result of extracting a length from this’s response’s header list.
            auto length = m_response->header_list()->extract_length();