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

Code before:
    data.origin = origin.serialize();

    // 3. Let formData be the result of executing the FormData constructor on form.
    auto form_data = TRY(XHR::FormData::construct_impl(realm, form));

    // 4. Let elements be a list of all the submittable elements whose form owner is form, in tree order.
    auto elements = form->get_submittable_elements();

    // 5. Let newPasswordObserved be false.
    bool new_password_observed = false;

    // 6. For each field in elements, run the following steps:
    for (auto const& field : elements) {
        dbgln("field->name(): {}", field->name());
        // 1. If field does not have an autocomplete attribute, then skip to the next field.
        if (auto attr = field->attribute(HTML::AttributeNames::autocomplete); !attr.has_value() || attr->is_empty())
            continue;

        // 2. Let name be the value of field’s name attribute.
        // 3. If formData’s has() method returns false when executed on name, then skip to the next field.
        auto name = field->attribute(HTML::AttributeNames::name);
        if (!name.has_value() || !form_data->has(name.value()))
            continue;

        // 4. If field’s autocomplete attribute’s value contains one or more autofill detail tokens (tokens), then:
        if (auto tokens = field->attribute(HTML::AttributeNames::autocomplete); tokens.has_value()) {
            // 1. For each token in tokens:
            for (auto& token : MUST(tokens->split(' '))) {
                // 1. If token is an ASCII case-insensitive match for one of the following strings, run the associated steps:
                //    - "new-password"
                //       Set data’s password member’s value to the result of executing formData’s get() method on name,
                //       and newPasswordObserved to true.
                if (token.equals_ignoring_ascii_case("new-password"sv)) {
                    if (auto password = form_data->get(name.value()); password.has<String>()) {
                        data.password = password.get<String>();
                        new_password_observed = true;
                    }
                }
                //    - "current-password"
                //       If newPasswordObserved is false, set data’s password member’s value to the result of executing
                //       formData’s get() method on name.
                //       Note: By checking that newPasswordObserved is false, new-password fields take precedence over
                //             current-password fields.
                if (!new_password_observed && token.equals_ignoring_ascii_case("current-password"sv)) {
                    if (auto password = form_data->get(name.value()); password.has<String>())
                        data.password = password.get<String>();
                }
                //    - "photo"
                //      Set data’s iconURL member’s value to the result of executing formData’s get() method on name.
                if (token.equals_ignoring_ascii_case("photo"sv)) {
                    if (auto photo = form_data->get(name.value()); photo.has<String>())

Review comment (suggestion):
suggestion: This is always true because the field is skipped by step 1 if there is no autocomplete attribute. It would be nice to have the check for `one or more autofill detail tokens`, but it's non-blocking since the following code works on a subset of known good tokens.

Code after:

    // 3. Let formData be the result of executing the FormData constructor on form.
    auto form_data = TRY(XHR::FormData::construct_impl(realm, form));

    // 4. Let elements be a list of all the submittable elements whose form owner is form, in tree order.
    auto elements = form->get_submittable_elements();

    // 5. Let newPasswordObserved be false.
    bool new_password_observed = false;

    // 6. For each field in elements, run the following steps:
    for (auto const& field : elements) {
        // 1. If field does not have an autocomplete attribute, then skip to the next field.
        if (auto attr = field->attribute(HTML::AttributeNames::autocomplete); !attr.has_value() || attr->is_empty())
            continue;

        // 2. Let name be the value of field’s name attribute.
        // 3. If formData’s has() method returns false when executed on name, then skip to the next field.
        auto name = field->attribute(HTML::AttributeNames::name);
        if (!name.has_value() || !form_data->has(name.value()))
            continue;

        // 4. If field’s autocomplete attribute’s value contains one or more autofill detail tokens (tokens), then:
        // 1. For each token in tokens:
        for (auto tokens = field->attribute(HTML::AttributeNames::autocomplete); auto& token : MUST(tokens->split(' '))) {
            // 1. If token is an ASCII case-insensitive match for one of the following strings, run the associated steps:
            //    - "new-password"
            //       Set data’s password member’s value to the result of executing formData’s get() method on name,
            //       and newPasswordObserved to true.
            if (token.equals_ignoring_ascii_case("new-password"sv)) {
                if (auto password = form_data->get(name.value()); password.has<String>()) {
                    data.password = password.get<String>();
                    new_password_observed = true;
                }
            }
            //    - "current-password"
            //       If newPasswordObserved is false, set data’s password member’s value to the result of executing
            //       formData’s get() method on name.
            //       Note: By checking that newPasswordObserved is false, new-password fields take precedence over
            //             current-password fields.
            if (!new_password_observed && token.equals_ignoring_ascii_case("current-password"sv)) {
                if (auto password = form_data->get(name.value()); password.has<String>())
                    data.password = password.get<String>();
            }
            //    - "photo"
            //      Set data’s iconURL member’s value to the result of executing formData’s get() method on name.
            if (token.equals_ignoring_ascii_case("photo"sv)) {
                if (auto photo = form_data->get(name.value()); photo.has<String>())
                    data.icon_url = photo.get<String>();
            }
            //    - "name"