mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2026-04-18 18:00:31 +00:00
This implements the following AOs: - Create a PasswordCredential from PasswordCredentialData. - Create a PasswordCredential from an HTMLFormElement. Which corresponds to these PasswordCredential ctors: - constructor(PasswordCredentialData) - constructor(HTMLFormElement)
130 lines
6.5 KiB
C++
130 lines
6.5 KiB
C++
/*
|
||
* 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::Ptr<HTML::HTMLFormElement> const& form, URL::Origin origin)
|
||
{
|
||
// 1. Let data be a new PasswordCredentialData dictionary.
|
||
PasswordCredentialData data;
|
||
|
||
// 2. Set data’s origin member’s value to origin’s 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 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"
|
||
// - "nickname"
|
||
// Set data’s name member’s value to the result of executing formData’s 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 data’s id member’s value to the result of executing formData’s 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:
|
||
// - data’s id member’s value
|
||
// - data’s origin member’s value
|
||
// NOTE: origin cannot be an empty string at this time since it is retrieved from the current settings object
|
||
// in the constructor.
|
||
// - data’s password member’s 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 c’s properties as follows:
|
||
// - password
|
||
// - data’s password member’s value
|
||
// - id
|
||
// - data’s id member’s value
|
||
// - iconUrl
|
||
// - data’s iconURL member’s value
|
||
// - name
|
||
// - data’s name member’s value
|
||
// - [[origin]]
|
||
// - data’s origin member’s value.
|
||
// NOTE: origin is retrieved from the current settings object in the constructor.
|
||
// 4. Return c.
|
||
return realm.create<PasswordCredential>(realm, data, move(origin));
|
||
}
|
||
|
||
}
|