/* * Copyright (c) 2026, Niccolo Antonelli-Dziri * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Web::PermissionsAPI { bool is_permission_supported(String const& name) { if (name == "geolocation") { return true; } return false; } // https://w3c.github.io/permissions/#dfn-request-permission-to-use Bindings::PermissionState request_permission(Bindings::PermissionDescriptor const& descriptor) { // 1. Let current state be the descriptor's permission state. auto current_state = permission_state(descriptor); // 2. If current state is not "prompt", return current state and abort these steps. if (current_state != Bindings::PermissionState::Prompt) return current_state; // FIXME: 3. Ask the user for express permission for the calling algorithm to use the powerful feature described by descriptor. // 4. If the user gives express permission to use the powerful feature, set current state to "granted"; otherwise to "denied". // The user's interaction may provide new information about the user's intent for the origin. if (false) { current_state = Bindings::PermissionState::Granted; } else { current_state = Bindings::PermissionState::Denied; } // 5. Let settings be the current settings object. auto& settings = HTML::current_settings_object(); // 6. Let key be the result of generating a permission key for descriptor with settings's top-level origin and settings's origin. VERIFY(settings.top_level_origin.has_value()); auto key = permission_key_generation_algorithm(settings.top_level_origin.value(), settings.origin()); // 7. Queue a task on the current settings object's responsible event loop to set a permission store entry with descriptor, key, and current state. HTML::queue_global_task(HTML::Task::Source::Permissions, settings.global_object(), GC::create_function(settings.realm().heap(), [descriptor, key, current_state] { PermissionStore::the().set_permission_store_entry(descriptor, key, current_state); })); // 8. Return current state. return current_state; } // https://w3c.github.io/permissions/#dfn-permission-state Bindings::PermissionState permission_state(Bindings::PermissionDescriptor descriptor, Optional settings) { // 1. If settings wasn't passed, set it to the current settings object. auto& settings_object = settings.has_value() ? settings.value() : HTML::current_settings_object(); // 2. If settings is a non-secure context, return "denied". if (!HTML::is_secure_context(settings_object)) return Bindings::PermissionState::Denied; // FIXME: 3. Let feature be descriptor's name. // FIXME: 4. If there exists a policy-controlled feature for feature and settings' relevant global object has an associated Document run the following step: if (false) { // 1. Let document be settings' relevant global object's associated Document. // 2. If document is not allowed to use feature, return "denied". } // 5. Let key be the result of generating a permission key for descriptor with settings's top-level origin and settings's origin. VERIFY(settings_object.top_level_origin.has_value()); auto key = permission_key_generation_algorithm(settings_object.top_level_origin.value(), settings_object.origin()); // 6. Let entry be the result of getting a permission store entry with descriptor and key. auto entry = PermissionStore::the().get_permission_store_entry(descriptor, key); // 7. If entry is not null, return a PermissionState enum value from entry's state. if (entry.has_value()) return entry.release_value().state; // 8. Return the PermissionState enum value that represents the permission state of feature, taking into account any permission state constraints for descriptor's name. return Bindings::PermissionState::Prompt; } GC_DEFINE_ALLOCATOR(Permissions); GC::Ref Permissions::create(JS::Realm& realm) { return realm.create(realm); } Permissions::Permissions(JS::Realm& realm) : Bindings::PlatformObject(realm) { } void Permissions::initialize(JS::Realm& realm) { WEB_SET_PROTOTYPE_FOR_INTERFACE(Permissions); Base::initialize(realm); } // https://w3c.github.io/permissions/#query-method GC::Ref Permissions::query(JS::Value permission_desc) { auto& realm = this->realm(); auto& relevant_global_object = HTML::relevant_global_object(*this); // 1. If this's relevant global object is a Window object, then: if (auto* window = as_if(relevant_global_object)) { // 1. If the current settings object's associated Document is not fully active, return a promise rejected with an "InvalidStateError" DOMException. if (!window->associated_document().is_fully_active()) { auto error = WebIDL::InvalidStateError::create(realm, "The document is not fully active."_utf16); return WebIDL::create_rejected_promise_from_exception(realm, error); } } // 2. Let rootDesc be the object permissionDesc refers to, converted to an IDL value of type PermissionDescriptor. // 3. If the conversion throws an exception, return a promise rejected with that exception. auto root_desc_or_error = Bindings::convert_to_idl_value_for_permission_descriptor(vm(), permission_desc); if (root_desc_or_error.is_error()) { return WebIDL::create_rejected_promise_from_exception(realm, root_desc_or_error.release_error()); } auto root_desc = root_desc_or_error.release_value(); // 4. If rootDesc["name"] is not supported, return a promise rejected with a TypeError. if (!is_permission_supported(root_desc.name)) { auto error = vm().throw_completion(JS::ErrorType::InvalidEnumerationValue, root_desc.name, "PermissionName"sv); return WebIDL::create_rejected_promise_from_exception(realm, error.release_error()); } // 5. Let typedDescriptor be the object permissionDesc refers to, converted to an IDL value of rootDesc's name's permission descriptor type. // 6. If the conversion throws an exception, return a promise rejected with that exception. // FIXME: Support specific permission descriptor types. For now, we only support the base PermissionDescriptor. auto typed_descriptor = root_desc; // 7. Let promise be a new promise. auto promise = WebIDL::create_promise(realm); // 8. Return promise and continue in parallel: // FIXME: Continue in parallel. { // 1. Let status be create a PermissionStatus with typedDescriptor. auto status = PermissionStatus::create(realm, typed_descriptor); // 2. Let query be status's [[query]] internal slot. auto const& query = status->query(); // 3. Run query's name's permission query algorithm, passing query and status. // FIXME: For now, only the base PermissionDescriptor is supported so there is only one permission query algorithm. permission_query_algorithm(query, status); // 4. Queue a global task on the permissions task source with this's relevant global object to resolve promise with status. HTML::queue_global_task(HTML::Task::Source::Permissions, relevant_global_object, GC::create_function(realm.heap(), [&realm, promise, status]() mutable { HTML::TemporaryExecutionContext execution_context { realm }; WebIDL::resolve_promise(realm, promise, status); })); } return promise; } // https://w3c.github.io/permissions/#dfn-getting-the-current-permission-state Bindings::PermissionState get_current_permission_state(String const& name, Optional settings) { // 1. Let descriptor be a newly-created PermissionDescriptor with name initialized to name. Bindings::PermissionDescriptor descriptor { name }; // 2. Return the permission state of descriptor with settings. return permission_state(descriptor, settings); } // https://w3c.github.io/permissions/#dfn-permission-query-algorithm void permission_query_algorithm(Bindings::PermissionDescriptor const& permission_desc, PermissionStatus& status) { // 1. Set status's state to permissionDesc's permission state. status.set_state(permission_state(permission_desc)); } }