ladybird/Libraries/LibWeb/CredentialManagement/PasswordCredentialOperations.cpp
devgianlu f14410c19f LibWeb: Do not pass GC::Ptr by reference
Also change it to `GC::Ref` since it's nonnull.
2026-02-22 14:55:30 -05:00

130 lines
6.5 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* Copyright (c) 2025, Kenneth Myhra <kennethmyhra@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <LibWeb/CredentialManagement/PasswordCredentialOperations.h>
#include <LibWeb/HTML/HTMLFormElement.h>
#include <LibWeb/XHR/FormData.h>
namespace Web::CredentialManagement {
// https://www.w3.org/TR/credential-management-1/#abstract-opdef-create-a-passwordcredential-from-an-htmlformelement
WebIDL::ExceptionOr<GC::Ref<PasswordCredential>> create_password_credential(JS::Realm& realm, GC::Ref<HTML::HTMLFormElement> form, URL::Origin origin)
{
// 1. Let data be a new PasswordCredentialData dictionary.
PasswordCredentialData data;
// 2. Set datas origin members value to origins value.
// 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 fields name attribute.
// 3. If formDatas 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 fields autocomplete attributes 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 datas password members value to the result of executing formDatas 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 datas password members value to the result of executing
// formDatas 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 datas iconURL members value to the result of executing formDatas 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"
// - "nickname"
// Set datas name members value to the result of executing formDatas get() method on name.
if (token.equals_ignoring_ascii_case("name"sv)) {
if (auto name_ = form_data->get(name.value()); name_.has<String>())
data.name = name_.get<String>();
}
if (token.equals_ignoring_ascii_case("nickname"sv)) {
if (auto nickname = form_data->get(name.value()); nickname.has<String>())
data.name = nickname.get<String>();
}
// - "username"
// Set datas id members value to the result of executing formDatas get() method on name.
if (token.equals_ignoring_ascii_case("username"sv)) {
if (auto username = form_data->get(name.value()); username.has<String>()) {
auto id = username.get<String>();
data.id = id;
}
}
}
}
// 7. Let c be the result of executing Create a PasswordCredential from PasswordCredentialData on data.
// If that threw an exception, rethrow that exception.
// 8. Assert: c is a PasswordCredential.
// 9. Return c.
return create_password_credential(realm, data, move(origin));
}
// https://www.w3.org/TR/credential-management-1/#abstract-opdef-create-a-passwordcredential-from-passwordcredentialdata
WebIDL::ExceptionOr<GC::Ref<PasswordCredential>> create_password_credential(JS::Realm& realm, PasswordCredentialData const& data, URL::Origin origin)
{
// 1. Let c be a new PasswordCredential object.
// 2. If any of the following are the empty string, throw a TypeError exception:
// - datas id members value
// - datas origin members value
// NOTE: origin cannot be an empty string at this time since it is retrieved from the current settings object
// in the constructor.
// - datas password members value
if (data.id.is_empty())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "'id' must not be empty."sv };
if (data.password.is_empty())
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::TypeError, "'password' must not be empty."sv };
// 3. Set cs properties as follows:
// - password
// - datas password members value
// - id
// - datas id members value
// - iconUrl
// - datas iconURL members value
// - name
// - datas name members value
// - [[origin]]
// - datas origin members value.
// NOTE: origin is retrieved from the current settings object in the constructor.
// 4. Return c.
return realm.create<PasswordCredential>(realm, data, move(origin));
}
}