2022-11-17 12:57:14 -05:00
/*
2025-02-05 11:36:19 -05:00
* Copyright ( c ) 2022 - 2025 , Tim Flynn < trflynn89 @ ladybird . org >
2022-11-17 12:57:14 -05:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
2022-11-18 08:54:27 -05:00
# include <AK/Debug.h>
2022-11-17 12:57:14 -05:00
# include <AK/JsonArray.h>
# include <AK/JsonObject.h>
# include <AK/JsonValue.h>
# include <AK/Optional.h>
2024-05-26 11:12:49 -04:00
# include <LibWeb/Loader/UserAgent.h>
2022-11-17 12:57:14 -05:00
# include <LibWeb/WebDriver/Capabilities.h>
2025-02-07 13:52:41 -05:00
# include <LibWeb/WebDriver/Proxy.h>
2022-11-17 12:57:14 -05:00
# include <LibWeb/WebDriver/TimeoutsConfiguration.h>
2025-02-05 11:36:19 -05:00
# include <LibWeb/WebDriver/UserPrompt.h>
2022-11-17 12:57:14 -05:00
namespace Web : : WebDriver {
// https://w3c.github.io/webdriver/#dfn-deserialize-as-a-page-load-strategy
static Response deserialize_as_a_page_load_strategy ( JsonValue value )
{
// 1. If value is not a string return an error with error code invalid argument.
if ( ! value . is_string ( ) )
return Error : : from_code ( ErrorCode : : InvalidArgument , " Capability pageLoadStrategy must be a string " sv ) ;
// 2. If there is no entry in the table of page load strategies with keyword value return an error with error code invalid argument.
if ( ! value . as_string ( ) . is_one_of ( " none " sv , " eager " sv , " normal " sv ) )
return Error : : from_code ( ErrorCode : : InvalidArgument , " Invalid pageLoadStrategy capability " sv ) ;
// 3. Return success with data value.
return value ;
}
2024-10-21 19:07:55 -04:00
static InterfaceMode default_interface_mode { InterfaceMode : : Graphical } ;
void set_default_interface_mode ( InterfaceMode interface_mode )
{
default_interface_mode = interface_mode ;
}
2025-02-07 12:11:38 -05:00
static Response deserialize_as_ladybird_capability ( StringView name , JsonValue value )
2022-11-22 07:38:07 -05:00
{
2025-02-07 12:11:38 -05:00
if ( name = = " ladybird:headless " sv ) {
if ( ! value . is_bool ( ) )
return Error : : from_code ( ErrorCode : : InvalidArgument , " Extension capability ladybird:headless must be a boolean " sv ) ;
}
2022-11-22 07:38:07 -05:00
return value ;
}
2025-02-07 12:11:38 -05:00
static void set_default_ladybird_capabilities ( JsonObject & options )
2022-11-22 07:38:07 -05:00
{
2025-02-07 12:11:38 -05:00
options . set ( " ladybird:headless " sv , default_interface_mode = = InterfaceMode : : Headless ) ;
2022-11-22 07:38:07 -05:00
}
2022-11-17 12:57:14 -05:00
// https://w3c.github.io/webdriver/#dfn-validate-capabilities
static ErrorOr < JsonObject , Error > validate_capabilities ( JsonValue const & capability )
{
// 1. If capability is not a JSON Object return an error with error code invalid argument.
if ( ! capability . is_object ( ) )
return Error : : from_code ( ErrorCode : : InvalidArgument , " Capability is not an Object " sv ) ;
// 2. Let result be an empty JSON Object.
JsonObject result ;
// 3. For each enumerable own property in capability, run the following substeps:
2023-11-05 17:42:31 +01:00
TRY ( capability . as_object ( ) . try_for_each_member ( [ & ] ( auto const & name , JsonValue const & value ) - > ErrorOr < void , Error > {
2022-11-17 12:57:14 -05:00
// a. Let name be the name of the property.
// b. Let value be the result of getting a property named name from capability.
// c. Run the substeps of the first matching condition:
JsonValue deserialized ;
// -> value is null
if ( value . is_null ( ) ) {
// Let deserialized be set to null.
}
// -> name equals "acceptInsecureCerts"
else if ( name = = " acceptInsecureCerts " sv ) {
// If value is not a boolean return an error with error code invalid argument. Otherwise, let deserialized be set to value
if ( ! value . is_bool ( ) )
return Error : : from_code ( ErrorCode : : InvalidArgument , " Capability acceptInsecureCerts must be a boolean " sv ) ;
deserialized = value ;
}
// -> name equals "browserName"
// -> name equals "browserVersion"
// -> name equals "platformName"
2023-03-13 17:34:00 -04:00
else if ( name . is_one_of ( " browserName " sv , " browserVersion " sv , " platformName " sv ) ) {
2022-11-17 12:57:14 -05:00
// If value is not a string return an error with error code invalid argument. Otherwise, let deserialized be set to value.
if ( ! value . is_string ( ) )
2025-02-17 13:58:21 -05:00
return Error : : from_code ( ErrorCode : : InvalidArgument , MUST ( String : : formatted ( " Capability {} must be a string " , name ) ) ) ;
2022-11-17 12:57:14 -05:00
deserialized = value ;
}
// -> name equals "pageLoadStrategy"
else if ( name = = " pageLoadStrategy " sv ) {
// Let deserialized be the result of trying to deserialize as a page load strategy with argument value.
deserialized = TRY ( deserialize_as_a_page_load_strategy ( value ) ) ;
}
2024-02-09 13:47:27 -07:00
// -> name equals "proxy"
else if ( name = = " proxy " sv ) {
// Let deserialized be the result of trying to deserialize as a proxy with argument value.
deserialized = TRY ( deserialize_as_a_proxy ( value ) ) ;
}
2022-11-17 12:57:14 -05:00
// -> name equals "strictFileInteractability"
else if ( name = = " strictFileInteractability " sv ) {
// If value is not a boolean return an error with error code invalid argument. Otherwise, let deserialized be set to value
if ( ! value . is_bool ( ) )
return Error : : from_code ( ErrorCode : : InvalidArgument , " Capability strictFileInteractability must be a boolean " sv ) ;
deserialized = value ;
}
// -> name equals "timeouts"
else if ( name = = " timeouts " sv ) {
// Let deserialized be the result of trying to JSON deserialize as a timeouts configuration the value.
auto timeouts = TRY ( json_deserialize_as_a_timeouts_configuration ( value ) ) ;
deserialized = JsonValue { timeouts_object ( timeouts ) } ;
}
// -> name equals "unhandledPromptBehavior"
else if ( name = = " unhandledPromptBehavior " sv ) {
// Let deserialized be the result of trying to deserialize as an unhandled prompt behavior with argument value.
deserialized = TRY ( deserialize_as_an_unhandled_prompt_behavior ( value ) ) ;
}
// FIXME: -> name is the name of an additional WebDriver capability
// FIXME: Let deserialized be the result of trying to run the additional capability deserialization algorithm for the extension capability corresponding to name, with argument value.
2022-11-22 07:38:07 -05:00
2024-05-30 14:44:35 +02:00
// https://w3c.github.io/webdriver-bidi/#type-session-CapabilityRequest
else if ( name = = " webSocketUrl " sv ) {
// 1. If value is not a boolean, return error with code invalid argument.
if ( ! value . is_bool ( ) )
return Error : : from_code ( ErrorCode : : InvalidArgument , " Capability webSocketUrl must be a boolean " sv ) ;
// 2. Return success with data value.
deserialized = value ;
}
2022-11-22 07:38:07 -05:00
// -> name is the key of an extension capability
2025-02-07 11:57:46 -05:00
else if ( name . contains ( ' : ' ) ) {
// If name is known to the implementation, let deserialized be the result of trying to deserialize value in
// an implementation-specific way. Otherwise, let deserialized be set to value.
2025-02-17 12:18:27 -05:00
if ( name . starts_with_bytes ( " ladybird: " sv ) )
2025-02-07 12:11:38 -05:00
deserialized = TRY ( deserialize_as_ladybird_capability ( name , value ) ) ;
2022-11-22 07:38:07 -05:00
}
2022-11-17 12:57:14 -05:00
// -> The remote end is an endpoint node
else {
// Return an error with error code invalid argument.
2025-02-17 13:58:21 -05:00
return Error : : from_code ( ErrorCode : : InvalidArgument , MUST ( String : : formatted ( " Unrecognized capability: {} " , name ) ) ) ;
2022-11-17 12:57:14 -05:00
}
// d. If deserialized is not null, set a property on result with name name and value deserialized.
if ( ! deserialized . is_null ( ) )
result . set ( name , move ( deserialized ) ) ;
return { } ;
} ) ) ;
// 4. Return success with data result.
return result ;
}
// https://w3c.github.io/webdriver/#dfn-merging-capabilities
static ErrorOr < JsonObject , Error > merge_capabilities ( JsonObject const & primary , Optional < JsonObject const & > const & secondary )
{
// 1. Let result be a new JSON Object.
JsonObject result ;
// 2. For each enumerable own property in primary, run the following substeps:
primary . for_each_member ( [ & ] ( auto const & name , auto const & value ) {
// a. Let name be the name of the property.
// b. Let value be the result of getting a property named name from primary.
// c. Set a property on result with name name and value value.
result . set ( name , value ) ;
} ) ;
// 3. If secondary is undefined, return result.
if ( ! secondary . has_value ( ) )
return result ;
// 4. For each enumerable own property in secondary, run the following substeps:
TRY ( secondary - > try_for_each_member ( [ & ] ( auto const & name , auto const & value ) - > ErrorOr < void , Error > {
// a. Let name be the name of the property.
// b. Let value be the result of getting a property named name from secondary.
// c. Let primary value be the result of getting the property name from primary.
2022-12-21 17:28:20 +00:00
auto primary_value = primary . get ( name ) ;
2022-11-17 12:57:14 -05:00
// d. If primary value is not undefined, return an error with error code invalid argument.
2022-12-21 17:28:20 +00:00
if ( primary_value . has_value ( ) )
2025-02-17 13:58:21 -05:00
return Error : : from_code ( ErrorCode : : InvalidArgument , MUST ( String : : formatted ( " Unable to merge capability {} " , name ) ) ) ;
2022-11-17 12:57:14 -05:00
// e. Set a property on result with name name and value value.
result . set ( name , value ) ;
return { } ;
} ) ) ;
// 5. Return result.
return result ;
}
2022-11-18 08:54:27 -05:00
static bool matches_browser_version ( StringView requested_version , StringView required_version )
{
// FIXME: Handle relative (>, >=, <. <=) comparisons. For now, require an exact match.
return requested_version = = required_version ;
}
static bool matches_platform_name ( StringView requested_platform_name , StringView required_platform_name )
{
if ( requested_platform_name = = required_platform_name )
return true ;
// The following platform names are in common usage with well-understood semantics and, when matching capabilities, greatest interoperability can be achieved by honoring them as valid synonyms for well-known Operating Systems:
// "linux" Any server or desktop system based upon the Linux kernel.
// "mac" Any version of Apple’ s macOS.
// "windows" Any version of Microsoft Windows, including desktop and mobile versions.
// This list is not exhaustive.
// NOTE: Of the synonyms listed in the spec, the only one that differs for us is macOS.
// Further, we are allowed to handle synonyms for SerenityOS.
if ( requested_platform_name = = " mac " sv & & required_platform_name = = " macos " sv )
return true ;
if ( requested_platform_name = = " serenity " sv & & required_platform_name = = " serenityos " sv )
return true ;
return false ;
}
// https://w3c.github.io/webdriver/#dfn-matching-capabilities
2025-02-07 11:09:28 -05:00
static JsonValue match_capabilities ( JsonObject const & capabilities , SessionFlags flags )
2022-11-18 08:54:27 -05:00
{
2025-02-17 13:21:07 -05:00
static auto browser_name = String : : from_utf8_without_validation ( { BROWSER_NAME , __builtin_strlen ( BROWSER_NAME ) } ) . to_ascii_lowercase ( ) ;
static auto browser_version = String : : from_utf8_without_validation ( { BROWSER_VERSION , __builtin_strlen ( BROWSER_VERSION ) } ) ;
static auto platform_name = String : : from_utf8_without_validation ( { OS_STRING , __builtin_strlen ( OS_STRING ) } ) . to_ascii_lowercase ( ) ;
2022-11-18 08:54:27 -05:00
// 1. Let matched capabilities be a JSON Object with the following entries:
JsonObject matched_capabilities ;
// "browserName"
// ASCII Lowercase name of the user agent as a string.
matched_capabilities . set ( " browserName " sv , browser_name ) ;
// "browserVersion"
// The user agent version, as a string.
2025-02-17 11:59:45 -05:00
matched_capabilities . set ( " browserVersion " sv , browser_version ) ;
2022-11-18 08:54:27 -05:00
// "platformName"
// ASCII Lowercase name of the current platform as a string.
matched_capabilities . set ( " platformName " sv , platform_name ) ;
// "acceptInsecureCerts"
// Boolean initially set to false, indicating the session will not implicitly trust untrusted or self-signed TLS certificates on navigation.
matched_capabilities . set ( " acceptInsecureCerts " sv , false ) ;
// "strictFileInteractability"
// Boolean initially set to false, indicating that interactability checks will be applied to <input type=file>.
2025-02-05 16:05:25 -05:00
// FIXME: Spec issue: This item likely should have been removed in lieu of step 2.
// https://github.com/w3c/webdriver/issues/1879
2022-11-18 08:54:27 -05:00
// "setWindowRect"
// Boolean indicating whether the remote end supports all of the resizing and positioning commands.
matched_capabilities . set ( " setWindowRect " sv , true ) ;
2025-02-05 16:05:25 -05:00
// "userAgent"
// String containing the default User-Agent value.
matched_capabilities . set ( " userAgent " sv , Web : : default_user_agent ) ;
// 2. If flags contains "http", add the following entries to matched capabilities:
2025-02-07 11:09:28 -05:00
if ( has_flag ( flags , SessionFlags : : Http ) ) {
2025-02-05 16:05:25 -05:00
// "strictFileInteractability"
// Boolean initially set to false, indicating that interactabilty checks will be applied to <input type=file>.
matched_capabilities . set ( " strictFileInteractability " sv , false ) ;
}
2022-11-18 08:54:27 -05:00
2025-02-07 12:11:38 -05:00
// 3. Optionally add extension capabilities as entries to matched capabilities. The values of these may be elided,
// and there is no requirement that all extension capabilities be added.
set_default_ladybird_capabilities ( matched_capabilities ) ;
2022-11-18 08:54:27 -05:00
2025-02-05 16:05:25 -05:00
// 3. For each name and value corresponding to capabilities's own properties:
2022-11-18 08:54:27 -05:00
auto result = capabilities . try_for_each_member ( [ & ] ( auto const & name , auto const & value ) - > ErrorOr < void > {
// a. Let match value equal value.
// b. Run the substeps of the first matching name:
// -> "browserName"
if ( name = = " browserName " sv ) {
// If value is not a string equal to the "browserName" entry in matched capabilities, return success with data null.
2025-02-17 13:21:07 -05:00
if ( value . as_string ( ) ! = matched_capabilities . get_string ( name ) . value ( ) )
2024-09-05 15:06:15 +04:00
return AK : : Error : : from_string_literal ( " browserName " ) ;
2022-11-18 08:54:27 -05:00
}
// -> "browserVersion"
else if ( name = = " browserVersion " sv ) {
// Compare value to the "browserVersion" entry in matched capabilities using an implementation-defined comparison algorithm. The comparison is to accept a value that places constraints on the version using the "<", "<=", ">", and ">=" operators.
// If the two values do not match, return success with data null.
2025-02-17 13:58:21 -05:00
if ( ! matches_browser_version ( value . as_string ( ) , matched_capabilities . get_string ( name ) . value ( ) ) )
2024-09-05 15:06:15 +04:00
return AK : : Error : : from_string_literal ( " browserVersion " ) ;
2022-11-18 08:54:27 -05:00
}
// -> "platformName"
else if ( name = = " platformName " sv ) {
// If value is not a string equal to the "platformName" entry in matched capabilities, return success with data null.
2025-02-17 13:58:21 -05:00
if ( ! matches_platform_name ( value . as_string ( ) , matched_capabilities . get_string ( name ) . value ( ) ) )
2024-09-05 15:06:15 +04:00
return AK : : Error : : from_string_literal ( " platformName " ) ;
2022-11-18 08:54:27 -05:00
}
// -> "acceptInsecureCerts"
else if ( name = = " acceptInsecureCerts " sv ) {
2025-02-05 16:05:25 -05:00
// If accept insecure TLS flag is set and not equal to value, return success with data null.
2022-11-18 08:54:27 -05:00
if ( value . as_bool ( ) )
2024-09-05 15:06:15 +04:00
return AK : : Error : : from_string_literal ( " acceptInsecureCerts " ) ;
2022-11-18 08:54:27 -05:00
}
// -> "proxy"
else if ( name = = " proxy " sv ) {
2025-02-07 13:52:41 -05:00
// If the has proxy configuration flag is set, or if the proxy configuration defined in value is not one that
// passes the endpoint node's implementation-specific validity checks, return success with data null.
if ( has_proxy_configuration ( ) )
return AK : : Error : : from_string_literal ( " proxy " ) ;
2025-02-05 16:05:25 -05:00
}
// -> "unhandledPromptBehavior"
else if ( name = = " unhandledPromptBehavior " sv ) {
// If check user prompt handler matches with value is false, return success with data null.
if ( ! check_user_prompt_handler_matches ( value . as_object ( ) ) )
return AK : : Error : : from_string_literal ( " unhandledPromptBehavior " ) ;
2022-11-18 08:54:27 -05:00
}
// -> Otherwise
else {
2025-02-05 16:05:25 -05:00
// FIXME: If name is the name of an additional WebDriver capability which defines a matched capability serialization algorithm, let match value be the result of running the matched capability serialization algorithm for capability name with arguments value, and flags.
2022-11-18 08:54:27 -05:00
// FIXME: Otherwise, if name is the key of an extension capability, let match value be the result of trying implementation-specific steps to match on name with value. If the match is not successful, return success with data null.
2024-05-30 14:44:35 +02:00
// https://w3c.github.io/webdriver-bidi/#type-session-CapabilityRequest
if ( name = = " webSocketUrl " sv ) {
// 1. If value is false, return success with data null.
if ( ! value . as_bool ( ) )
2024-09-05 15:06:15 +04:00
return AK : : Error : : from_string_literal ( " webSocketUrl " ) ;
2024-05-30 14:44:35 +02:00
// 2. Return success with data value.
// FIXME: Remove this when we support BIDI communication.
2024-09-05 15:06:15 +04:00
return AK : : Error : : from_string_literal ( " webSocketUrl " ) ;
2024-05-30 14:44:35 +02:00
}
2022-11-18 08:54:27 -05:00
}
2025-02-05 16:05:25 -05:00
// c. If match value is not null, set a property on matched capabilities with name name and value match value.
if ( ! value . is_null ( ) )
matched_capabilities . set ( name , value ) ;
2022-11-18 08:54:27 -05:00
return { } ;
} ) ;
if ( result . is_error ( ) ) {
dbgln_if ( WEBDRIVER_DEBUG , " Failed to match capability: {} " , result . error ( ) ) ;
return JsonValue { } ;
}
// 4. Return success with data matched capabilities.
return matched_capabilities ;
}
2022-11-17 12:57:14 -05:00
// https://w3c.github.io/webdriver/#dfn-capabilities-processing
2025-02-07 11:09:28 -05:00
Response process_capabilities ( JsonValue const & parameters , SessionFlags flags )
2022-11-17 12:57:14 -05:00
{
if ( ! parameters . is_object ( ) )
return Error : : from_code ( ErrorCode : : InvalidArgument , " Session parameters is not an object " sv ) ;
// 1. Let capabilities request be the result of getting the property "capabilities" from parameters.
// a. If capabilities request is not a JSON Object, return error with error code invalid argument.
2022-12-21 17:28:20 +00:00
auto maybe_capabilities_request = parameters . as_object ( ) . get_object ( " capabilities " sv ) ;
if ( ! maybe_capabilities_request . has_value ( ) )
2022-11-17 12:57:14 -05:00
return Error : : from_code ( ErrorCode : : InvalidArgument , " Capabilities is not an object " sv ) ;
2022-12-21 17:28:20 +00:00
auto const & capabilities_request = maybe_capabilities_request . value ( ) ;
2022-11-17 12:57:14 -05:00
// 2. Let required capabilities be the result of getting the property "alwaysMatch" from capabilities request.
// a. If required capabilities is undefined, set the value to an empty JSON Object.
JsonObject required_capabilities ;
2022-12-21 17:28:20 +00:00
if ( auto capability = capabilities_request . get ( " alwaysMatch " sv ) ; capability . has_value ( ) ) {
2025-02-05 16:05:25 -05:00
// b. Let required capabilities be the result of trying to validate capabilities with arguments required capabilities and flag.
// FIXME: Spec issue: The "flags" parameter should not be provided to validate_capabilities.
// https://github.com/w3c/webdriver/issues/1879
2022-11-17 12:57:14 -05:00
required_capabilities = TRY ( validate_capabilities ( * capability ) ) ;
}
// 3. Let all first match capabilities be the result of getting the property "firstMatch" from capabilities request.
JsonArray all_first_match_capabilities ;
2022-12-21 17:28:20 +00:00
if ( auto capabilities = capabilities_request . get ( " firstMatch " sv ) ; capabilities . has_value ( ) ) {
2025-02-05 16:05:25 -05:00
// b. If all first match capabilities is not a List with one or more entries, return error with error code invalid argument.
2022-11-17 12:57:14 -05:00
if ( ! capabilities - > is_array ( ) | | capabilities - > as_array ( ) . is_empty ( ) )
return Error : : from_code ( ErrorCode : : InvalidArgument , " Capability firstMatch must be an array with at least one entry " sv ) ;
all_first_match_capabilities = capabilities - > as_array ( ) ;
} else {
2025-02-05 16:05:25 -05:00
// a. If all first match capabilities is undefined, set the value to a List with a single entry of an empty JSON Object.
2023-04-17 15:38:27 +10:00
all_first_match_capabilities . must_append ( JsonObject { } ) ;
2022-11-17 12:57:14 -05:00
}
2025-02-05 16:05:25 -05:00
// 4. Let validated first match capabilities be an empty List.
2022-11-17 12:57:14 -05:00
JsonArray validated_first_match_capabilities ;
validated_first_match_capabilities . ensure_capacity ( all_first_match_capabilities . size ( ) ) ;
// 5. For each first match capabilities corresponding to an indexed property in all first match capabilities:
TRY ( all_first_match_capabilities . try_for_each ( [ & ] ( auto const & first_match_capabilities ) - > ErrorOr < void , Error > {
2025-02-05 16:05:25 -05:00
// a. Let validated capabilities be the result of trying to validate capabilities with arguments first match capabilities and flags.
// FIXME: Spec issue: The "flags" parameter should not be provided to validate_capabilities.
// https://github.com/w3c/webdriver/issues/1879
2022-11-17 12:57:14 -05:00
auto validated_capabilities = TRY ( validate_capabilities ( first_match_capabilities ) ) ;
// b. Append validated capabilities to validated first match capabilities.
2023-04-17 15:38:27 +10:00
validated_first_match_capabilities . must_append ( move ( validated_capabilities ) ) ;
2022-11-17 12:57:14 -05:00
return { } ;
} ) ) ;
// 6. Let merged capabilities be an empty List.
JsonArray merged_capabilities ;
merged_capabilities . ensure_capacity ( validated_first_match_capabilities . size ( ) ) ;
// 7. For each first match capabilities corresponding to an indexed property in validated first match capabilities:
TRY ( validated_first_match_capabilities . try_for_each ( [ & ] ( auto const & first_match_capabilities ) - > ErrorOr < void , Error > {
// a. Let merged be the result of trying to merge capabilities with required capabilities and first match capabilities as arguments.
auto merged = TRY ( merge_capabilities ( required_capabilities , first_match_capabilities . as_object ( ) ) ) ;
// b. Append merged to merged capabilities.
2023-04-17 15:38:27 +10:00
merged_capabilities . must_append ( move ( merged ) ) ;
2022-11-17 12:57:14 -05:00
return { } ;
} ) ) ;
2022-11-18 08:54:27 -05:00
// 8. For each capabilities corresponding to an indexed property in merged capabilities:
for ( auto const & capabilities : merged_capabilities . values ( ) ) {
// a. Let matched capabilities be the result of trying to match capabilities with capabilities as an argument.
2025-02-05 16:05:25 -05:00
// FIXME: Spec issue: The "flags" parameter *should* be provided to match_capabilities.
// https://github.com/w3c/webdriver/issues/1879
auto matched_capabilities = match_capabilities ( capabilities . as_object ( ) , flags ) ;
2022-11-17 12:57:14 -05:00
2022-11-18 08:54:27 -05:00
// b. If matched capabilities is not null, return success with data matched capabilities.
if ( ! matched_capabilities . is_null ( ) )
return matched_capabilities ;
}
2022-11-17 12:57:14 -05:00
// 9. Return success with data null.
2022-11-18 08:54:27 -05:00
return JsonValue { } ;
2022-11-17 12:57:14 -05:00
}
2022-11-22 07:38:07 -05:00
LadybirdOptions : : LadybirdOptions ( JsonObject const & capabilities )
{
2025-02-07 12:11:38 -05:00
if ( auto headless = capabilities . get_bool ( " ladybird:headless " sv ) ; headless . has_value ( ) )
this - > headless = * headless ;
2022-11-22 07:38:07 -05:00
}
2022-11-17 12:57:14 -05:00
}