diff --git a/Libraries/LibWeb/CMakeLists.txt b/Libraries/LibWeb/CMakeLists.txt index 582f6d4fc72..01f413f573e 100644 --- a/Libraries/LibWeb/CMakeLists.txt +++ b/Libraries/LibWeb/CMakeLists.txt @@ -36,6 +36,10 @@ set(SOURCES Clipboard/ClipboardItem.cpp Compression/CompressionStream.cpp Compression/DecompressionStream.cpp + CredentialManagement/Credential.cpp + CredentialManagement/CredentialsContainer.cpp + CredentialManagement/FederatedCredential.cpp + CredentialManagement/PasswordCredential.cpp Crypto/Crypto.cpp Crypto/CryptoAlgorithms.cpp Crypto/CryptoBindings.cpp diff --git a/Libraries/LibWeb/CredentialManagement/Credential.cpp b/Libraries/LibWeb/CredentialManagement/Credential.cpp new file mode 100644 index 00000000000..24b5802bbe1 --- /dev/null +++ b/Libraries/LibWeb/CredentialManagement/Credential.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2025, Altomani Gianluca + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Web::CredentialManagement { + +// https://www.w3.org/TR/credential-management-1/#dom-credential-isconditionalmediationavailable +GC::Ref Credential::is_conditional_mediation_available(JS::VM& vm) +{ + auto* realm = vm.current_realm(); + return WebIDL::create_rejected_promise_from_exception(*realm, vm.throw_completion(JS::ErrorType::NotImplemented, "is conditional mediation available"sv)); +} + +// https://www.w3.org/TR/credential-management-1/#dom-credential-willrequestconditionalcreation +GC::Ref Credential::will_request_conditional_creation(JS::VM& vm) +{ + auto* realm = vm.current_realm(); + return WebIDL::create_rejected_promise_from_exception(*realm, vm.throw_completion(JS::ErrorType::NotImplemented, "will request conditional creation"sv)); +} + +Credential::~Credential() { } + +Credential::Credential(JS::Realm& realm) + : PlatformObject(realm) +{ +} + +void Credential::initialize(JS::Realm& realm) +{ + Base::initialize(realm); + WEB_SET_PROTOTYPE_FOR_INTERFACE(Credential); +} +} diff --git a/Libraries/LibWeb/CredentialManagement/Credential.h b/Libraries/LibWeb/CredentialManagement/Credential.h new file mode 100644 index 00000000000..6cd341055b6 --- /dev/null +++ b/Libraries/LibWeb/CredentialManagement/Credential.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2025, Altomani Gianluca + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Web::CredentialManagement { + +class Credential : public Bindings::PlatformObject { + WEB_PLATFORM_OBJECT(Credential, Bindings::PlatformObject); + GC_DECLARE_ALLOCATOR(Credential); + +public: + [[nodiscard]] static GC::Ref create(JS::Realm&); + + static GC::Ref is_conditional_mediation_available(JS::VM&); + static GC::Ref will_request_conditional_creation(JS::VM&); + + virtual ~Credential() override; + + String const& id() { return m_id; } + String const& name() { return m_name; } + String const& icon_url() { return m_icon_url; } + + virtual String type() = 0; + +protected: + explicit Credential(JS::Realm&); + virtual void initialize(JS::Realm&) override; + + String m_id; + String m_name; + String m_icon_url; +}; + +struct CredentialData { + String id; +}; + +} diff --git a/Libraries/LibWeb/CredentialManagement/Credential.idl b/Libraries/LibWeb/CredentialManagement/Credential.idl new file mode 100644 index 00000000000..f9cc6e2a2b2 --- /dev/null +++ b/Libraries/LibWeb/CredentialManagement/Credential.idl @@ -0,0 +1,24 @@ +[Exposed=Window, SecureContext] +interface Credential { + readonly attribute USVString id; + readonly attribute DOMString type; + static Promise isConditionalMediationAvailable(); + static Promise willRequestConditionalCreation(); +}; + +[SecureContext] +interface mixin CredentialUserData { + readonly attribute USVString name; + readonly attribute USVString iconURL; +}; + +dictionary CredentialData { + required USVString id; +}; + +enum CredentialMediationRequirement { + "silent", + "optional", + "conditional", + "required" +}; diff --git a/Libraries/LibWeb/CredentialManagement/CredentialsContainer.cpp b/Libraries/LibWeb/CredentialManagement/CredentialsContainer.cpp new file mode 100644 index 00000000000..bea22e9248a --- /dev/null +++ b/Libraries/LibWeb/CredentialManagement/CredentialsContainer.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2025, Altomani Gianluca + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace Web::CredentialManagement { + +GC_DEFINE_ALLOCATOR(CredentialsContainer); + +GC::Ref CredentialsContainer::create(JS::Realm& realm) +{ + return realm.create(realm); +} + +CredentialsContainer::~CredentialsContainer() { } + +// https://www.w3.org/TR/credential-management-1/#dom-credentialscontainer-get +GC::Ref CredentialsContainer::get(CredentialRequestOptions const&) +{ + auto* realm = vm().current_realm(); + return WebIDL::create_rejected_promise_from_exception(*realm, vm().throw_completion(JS::ErrorType::NotImplemented, "get"sv)); +} + +// https://www.w3.org/TR/credential-management-1/#dom-credentialscontainer-store +GC::Ref CredentialsContainer::store(Credential const&) +{ + auto* realm = vm().current_realm(); + return WebIDL::create_rejected_promise_from_exception(*realm, vm().throw_completion(JS::ErrorType::NotImplemented, "store"sv)); +} + +// https://www.w3.org/TR/credential-management-1/#dom-credentialscontainer-create +GC::Ref CredentialsContainer::create(CredentialCreationOptions const&) +{ + auto* realm = vm().current_realm(); + return WebIDL::create_rejected_promise_from_exception(*realm, vm().throw_completion(JS::ErrorType::NotImplemented, "create"sv)); +} + +// https://www.w3.org/TR/credential-management-1/#dom-credentialscontainer-preventsilentaccess +GC::Ref CredentialsContainer::prevent_silent_access() +{ + auto* realm = vm().current_realm(); + return WebIDL::create_rejected_promise_from_exception(*realm, vm().throw_completion(JS::ErrorType::NotImplemented, "prevent silent access"sv)); +} + +CredentialsContainer::CredentialsContainer(JS::Realm& realm) + : PlatformObject(realm) +{ +} + +void CredentialsContainer::initialize(JS::Realm& realm) +{ + Base::initialize(realm); + WEB_SET_PROTOTYPE_FOR_INTERFACE(CredentialsContainer); +} + +} diff --git a/Libraries/LibWeb/CredentialManagement/CredentialsContainer.h b/Libraries/LibWeb/CredentialManagement/CredentialsContainer.h new file mode 100644 index 00000000000..e744ae11c92 --- /dev/null +++ b/Libraries/LibWeb/CredentialManagement/CredentialsContainer.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2025, Altomani Gianluca + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Web::CredentialManagement { + +class CredentialsContainer final : public Bindings::PlatformObject { + WEB_PLATFORM_OBJECT(CredentialsContainer, Bindings::PlatformObject); + GC_DECLARE_ALLOCATOR(CredentialsContainer); + +public: + [[nodiscard]] static GC::Ref create(JS::Realm&); + + virtual ~CredentialsContainer() override; + + GC::Ref get(CredentialRequestOptions const& options); + GC::Ref store(Credential const& credential); + GC::Ref create(CredentialCreationOptions const& options); + GC::Ref prevent_silent_access(); + +private: + explicit CredentialsContainer(JS::Realm&); + virtual void initialize(JS::Realm&) override; +}; + +struct CredentialRequestOptions { + Bindings::CredentialMediationRequirement mediation { Bindings::CredentialMediationRequirement::Optional }; + GC::Ptr signal; + + Optional password; + Optional federated; +}; + +struct CredentialCreationOptions { + Bindings::CredentialMediationRequirement mediation { Bindings::CredentialMediationRequirement::Optional }; + GC::Ptr signal; + + Optional password; + Optional federated; +}; + +} diff --git a/Libraries/LibWeb/CredentialManagement/CredentialsContainer.idl b/Libraries/LibWeb/CredentialManagement/CredentialsContainer.idl new file mode 100644 index 00000000000..26382ac0444 --- /dev/null +++ b/Libraries/LibWeb/CredentialManagement/CredentialsContainer.idl @@ -0,0 +1,21 @@ +#import +#import +#import + +[Exposed=Window, SecureContext] +interface CredentialsContainer { + Promise get(optional CredentialRequestOptions options = {}); + Promise store(Credential credential); + Promise create(optional CredentialCreationOptions options = {}); + Promise preventSilentAccess(); +}; + +dictionary CredentialRequestOptions { + CredentialMediationRequirement mediation = "optional"; + AbortSignal signal; +}; + +dictionary CredentialCreationOptions { + CredentialMediationRequirement mediation = "optional"; + AbortSignal signal; +}; diff --git a/Libraries/LibWeb/CredentialManagement/FederatedCredential.cpp b/Libraries/LibWeb/CredentialManagement/FederatedCredential.cpp new file mode 100644 index 00000000000..a7e0adee95f --- /dev/null +++ b/Libraries/LibWeb/CredentialManagement/FederatedCredential.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2025, Altomani Gianluca + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Web::CredentialManagement { + +GC_DEFINE_ALLOCATOR(FederatedCredential); + +GC::Ref FederatedCredential::create(JS::Realm& realm) +{ + return realm.create(realm); +} + +// https://www.w3.org/TR/credential-management-1/#dom-federatedcredential-federatedcredential +WebIDL::ExceptionOr> FederatedCredential::construct_impl(JS::Realm& realm, FederatedCredentialInit const&) +{ + return realm.vm().throw_completion(JS::ErrorType::NotImplemented, "construct"sv); +} + +FederatedCredential::~FederatedCredential() +{ +} + +FederatedCredential::FederatedCredential(JS::Realm& realm) + : Credential(realm) +{ +} + +void FederatedCredential::initialize(JS::Realm& realm) +{ + Base::initialize(realm); + WEB_SET_PROTOTYPE_FOR_INTERFACE(FederatedCredential); +} + +} diff --git a/Libraries/LibWeb/CredentialManagement/FederatedCredential.h b/Libraries/LibWeb/CredentialManagement/FederatedCredential.h new file mode 100644 index 00000000000..71df9996aa4 --- /dev/null +++ b/Libraries/LibWeb/CredentialManagement/FederatedCredential.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2025, Altomani Gianluca + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Web::CredentialManagement { + +class FederatedCredential final : public Credential { + WEB_PLATFORM_OBJECT(FederatedCredential, Credential); + GC_DECLARE_ALLOCATOR(FederatedCredential); + +public: + [[nodiscard]] static GC::Ref create(JS::Realm&); + static WebIDL::ExceptionOr> construct_impl(JS::Realm&, FederatedCredentialInit const&); + + virtual ~FederatedCredential() override; + + String const& provider() { return m_provider; } + Optional const& protocol() { return m_protocol; } + + String type() override { return "federated"_string; } + +private: + explicit FederatedCredential(JS::Realm&); + virtual void initialize(JS::Realm&) override; + + String m_provider; + Optional m_protocol; +}; + +struct FederatedCredentialRequestOptions { + Optional> providers; + Optional> protocols; +}; + +struct FederatedCredentialInit : CredentialData { + Optional name; + Optional icon_url; + String origin; + String provider; + Optional protocol; +}; + +} diff --git a/Libraries/LibWeb/CredentialManagement/FederatedCredential.idl b/Libraries/LibWeb/CredentialManagement/FederatedCredential.idl new file mode 100644 index 00000000000..84d7a8bdbcb --- /dev/null +++ b/Libraries/LibWeb/CredentialManagement/FederatedCredential.idl @@ -0,0 +1,30 @@ +#import + +[Exposed=Window, SecureContext] +interface FederatedCredential : Credential { + constructor(FederatedCredentialInit data); + readonly attribute USVString provider; + readonly attribute DOMString? protocol; +}; +FederatedCredential includes CredentialUserData; + +dictionary FederatedCredentialRequestOptions { + sequence providers; + sequence protocols; +}; + +partial dictionary CredentialRequestOptions { + FederatedCredentialRequestOptions federated; +}; + +dictionary FederatedCredentialInit : CredentialData { + USVString name; + USVString iconURL; + required USVString origin; + required USVString provider; + DOMString protocol; +}; + +partial dictionary CredentialCreationOptions { + FederatedCredentialInit federated; +}; diff --git a/Libraries/LibWeb/CredentialManagement/PasswordCredential.cpp b/Libraries/LibWeb/CredentialManagement/PasswordCredential.cpp new file mode 100644 index 00000000000..b7d207ab6e6 --- /dev/null +++ b/Libraries/LibWeb/CredentialManagement/PasswordCredential.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2025, Altomani Gianluca + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace Web::CredentialManagement { + +GC_DEFINE_ALLOCATOR(PasswordCredential); + +GC::Ref PasswordCredential::create(JS::Realm& realm) +{ + return realm.create(realm); +} + +// https://www.w3.org/TR/credential-management-1/#dom-passwordcredential-passwordcredential +WebIDL::ExceptionOr> PasswordCredential::construct_impl(JS::Realm& realm, HTML::HTMLFormElement const&) +{ + return realm.vm().throw_completion(JS::ErrorType::NotImplemented, "construct"sv); +} + +// https://www.w3.org/TR/credential-management-1/#dom-passwordcredential-passwordcredential-data +WebIDL::ExceptionOr> PasswordCredential::construct_impl(JS::Realm& realm, PasswordCredentialData const&) +{ + return realm.vm().throw_completion(JS::ErrorType::NotImplemented, "construct"sv); +} + +PasswordCredential::~PasswordCredential() +{ +} + +PasswordCredential::PasswordCredential(JS::Realm& realm) + : Credential(realm) +{ +} + +void PasswordCredential::initialize(JS::Realm& realm) +{ + Base::initialize(realm); + WEB_SET_PROTOTYPE_FOR_INTERFACE(PasswordCredential); +} + +} diff --git a/Libraries/LibWeb/CredentialManagement/PasswordCredential.h b/Libraries/LibWeb/CredentialManagement/PasswordCredential.h new file mode 100644 index 00000000000..d2c7bf0d711 --- /dev/null +++ b/Libraries/LibWeb/CredentialManagement/PasswordCredential.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2025, Altomani Gianluca + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include +#include + +namespace Web::CredentialManagement { + +class PasswordCredential final : public Credential { + WEB_PLATFORM_OBJECT(PasswordCredential, Credential); + GC_DECLARE_ALLOCATOR(PasswordCredential); + +public: + [[nodiscard]] static GC::Ref create(JS::Realm&); + static WebIDL::ExceptionOr> construct_impl(JS::Realm&, HTML::HTMLFormElement const&); + static WebIDL::ExceptionOr> construct_impl(JS::Realm&, PasswordCredentialData const&); + + virtual ~PasswordCredential() override; + + String const& password() { return m_password; } + + String type() override { return "password"_string; } + +private: + explicit PasswordCredential(JS::Realm&); + virtual void initialize(JS::Realm&) override; + + // TODO: Use Core::SecretString when it comes back + String m_password; +}; + +struct PasswordCredentialData : CredentialData { + Optional name; + Optional icon_url; + String origin; + String password; +}; + +using PasswordCredentialInit = Variant>; + +} diff --git a/Libraries/LibWeb/CredentialManagement/PasswordCredential.idl b/Libraries/LibWeb/CredentialManagement/PasswordCredential.idl new file mode 100644 index 00000000000..3aa9576e3d9 --- /dev/null +++ b/Libraries/LibWeb/CredentialManagement/PasswordCredential.idl @@ -0,0 +1,26 @@ +#import + +[Exposed=Window, SecureContext] +interface PasswordCredential : Credential { + constructor(HTMLFormElement form); + constructor(PasswordCredentialData data); + readonly attribute USVString password; +}; +PasswordCredential includes CredentialUserData; + +partial dictionary CredentialRequestOptions { + boolean password = false; +}; + +dictionary PasswordCredentialData : CredentialData { + USVString name; + USVString iconURL; + required USVString origin; + required USVString password; +}; + +typedef (PasswordCredentialData or HTMLFormElement) PasswordCredentialInit; + +partial dictionary CredentialCreationOptions { + PasswordCredentialInit password; +}; diff --git a/Libraries/LibWeb/Forward.h b/Libraries/LibWeb/Forward.h index c2f4d507984..228f288a03a 100644 --- a/Libraries/LibWeb/Forward.h +++ b/Libraries/LibWeb/Forward.h @@ -99,6 +99,20 @@ struct ParsedCookie; enum class Source; } +namespace Web::CredentialManagement { +class Credential; +class CredentialsContainer; +class FederatedCredential; +class PasswordCredential; + +struct CredentialData; +struct CredentialRequestOptions; +struct CredentialCreationOptions; +struct FederatedCredentialRequestOptions; +struct FederatedCredentialInit; +struct PasswordCredentialData; +} + namespace Web::Crypto { class Crypto; class SubtleCrypto; diff --git a/Libraries/LibWeb/HTML/Navigator.cpp b/Libraries/LibWeb/HTML/Navigator.cpp index a418e3c59c1..227b6055cb1 100644 --- a/Libraries/LibWeb/HTML/Navigator.cpp +++ b/Libraries/LibWeb/HTML/Navigator.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +69,7 @@ void Navigator::visit_edges(Cell::Visitor& visitor) visitor.visit(m_user_activation); visitor.visit(m_service_worker_container); visitor.visit(m_media_capabilities); + visitor.visit(m_credentials); } GC::Ref Navigator::mime_types() @@ -98,6 +100,13 @@ GC::Ref Navigator::user_activation() return *m_user_activation; } +GC::Ref Navigator::credentials() +{ + if (!m_credentials) + m_credentials = realm().create(realm()); + return *m_credentials; +} + // https://w3c.github.io/pointerevents/#dom-navigator-maxtouchpoints WebIDL::Long Navigator::max_touch_points() { diff --git a/Libraries/LibWeb/HTML/Navigator.h b/Libraries/LibWeb/HTML/Navigator.h index ffbcd26b64d..a046e5cd4b0 100644 --- a/Libraries/LibWeb/HTML/Navigator.h +++ b/Libraries/LibWeb/HTML/Navigator.h @@ -54,6 +54,7 @@ public: [[nodiscard]] GC::Ref plugins(); [[nodiscard]] GC::Ref clipboard(); [[nodiscard]] GC::Ref user_activation(); + [[nodiscard]] GC::Ref credentials(); Optional do_not_track() const; @@ -90,6 +91,9 @@ private: // https://w3c.github.io/media-capabilities/#dom-navigator-mediacapabilities GC::Ptr m_media_capabilities; + + // https://w3c.github.io/webappsec-credential-management/#framework-credential-management + GC::Ptr m_credentials; }; } diff --git a/Libraries/LibWeb/HTML/Navigator.idl b/Libraries/LibWeb/HTML/Navigator.idl index cae3a1e69d4..6f6d75e084e 100644 --- a/Libraries/LibWeb/HTML/Navigator.idl +++ b/Libraries/LibWeb/HTML/Navigator.idl @@ -1,4 +1,5 @@ #import +#import #import #import #import @@ -34,6 +35,9 @@ interface Navigator { // https://w3c.github.io/media-capabilities/#dom-navigator-mediacapabilities [SameObject] readonly attribute MediaCapabilities mediaCapabilities; + + // https://w3c.github.io/webappsec-credential-management/#framework-credential-management + [SecureContext, SameObject] readonly attribute CredentialsContainer credentials; }; // NOTE: As NavigatorContentUtils, NavigatorCookies, NavigatorPlugins, and NavigatorAutomationInformation diff --git a/Libraries/LibWeb/idl_files.cmake b/Libraries/LibWeb/idl_files.cmake index 46a61847a87..da7a42fd623 100644 --- a/Libraries/LibWeb/idl_files.cmake +++ b/Libraries/LibWeb/idl_files.cmake @@ -12,6 +12,10 @@ libweb_js_bindings(Clipboard/ClipboardEvent) libweb_js_bindings(Clipboard/ClipboardItem) libweb_js_bindings(Compression/CompressionStream) libweb_js_bindings(Compression/DecompressionStream) +libweb_js_bindings(CredentialManagement/Credential) +libweb_js_bindings(CredentialManagement/CredentialsContainer) +libweb_js_bindings(CredentialManagement/FederatedCredential) +libweb_js_bindings(CredentialManagement/PasswordCredential) libweb_js_bindings(Crypto/Crypto) libweb_js_bindings(Crypto/CryptoKey) libweb_js_bindings(Crypto/SubtleCrypto) diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp index 86bc55af779..7844e2fbbe2 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp @@ -48,6 +48,8 @@ static bool is_platform_object(Type const& type) "CanvasRenderingContext2D"sv, "ClipboardItem"sv, "CloseWatcher"sv, + "Credential"sv, + "CredentialsContainer"sv, "CryptoKey"sv, "DataTransfer"sv, "Document"sv, @@ -56,6 +58,7 @@ static bool is_platform_object(Type const& type) "DynamicsCompressorNode"sv, "ElementInternals"sv, "EventTarget"sv, + "FederatedCredential"sv, "File"sv, "FileList"sv, "FontFace"sv, @@ -80,6 +83,7 @@ static bool is_platform_object(Type const& type) "NavigationDestination"sv, "NavigationHistoryEntry"sv, "Node"sv, + "PasswordCredential"sv, "Path2D"sv, "PerformanceEntry"sv, "PerformanceMark"sv, @@ -4424,6 +4428,7 @@ static void generate_using_namespace_definitions(SourceGenerator& generator) // FIXME: This is a total hack until we can figure out the namespace for a given type somehow. using namespace Web::Animations; using namespace Web::Clipboard; +using namespace Web::CredentialManagement; using namespace Web::Crypto; using namespace Web::CSS; using namespace Web::DOM; diff --git a/Tests/LibWeb/Text/expected/all-window-properties.txt b/Tests/LibWeb/Text/expected/all-window-properties.txt index 5d1ae784068..98863bb8765 100644 --- a/Tests/LibWeb/Text/expected/all-window-properties.txt +++ b/Tests/LibWeb/Text/expected/all-window-properties.txt @@ -71,6 +71,8 @@ CompositionEvent CompressionStream ConstantSourceNode CountQueuingStrategy +Credential +CredentialsContainer Crypto CryptoKey CustomElementRegistry @@ -111,6 +113,7 @@ EvalError Event EventSource EventTarget +FederatedCredential File FileList FileReader @@ -269,6 +272,7 @@ Option OscillatorNode PageTransitionEvent PannerNode +PasswordCredential Path2D Performance PerformanceEntry