diff --git a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp index 862fe0971a..5fe40c7c8d 100644 --- a/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp +++ b/Libraries/LibWeb/HTML/WindowOrWorkerGlobalScope.cpp @@ -2,7 +2,7 @@ * Copyright (c) 2022, Andrew Kaster * Copyright (c) 2023, Linus Groh * Copyright (c) 2023, Luke Wilde - * Copyright (c) 2025, Shannon Booth + * Copyright (c) 2025-2026, Shannon Booth * * SPDX-License-Identifier: BSD-2-Clause */ diff --git a/Libraries/LibWeb/Internals/Internals.cpp b/Libraries/LibWeb/Internals/Internals.cpp index 11e7123295..2328b09630 100644 --- a/Libraries/LibWeb/Internals/Internals.cpp +++ b/Libraries/LibWeb/Internals/Internals.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -532,4 +533,10 @@ void Internals::clear_element(HTML::HTMLElement& element) form_associated_element.clear_algorithm(); } +void Internals::set_environments_top_level_url(StringView url) +{ + auto& realm = *vm().current_realm(); + HTML::principal_realm_settings_object(realm).top_level_creation_url = URL::Parser::basic_parse(url); +} + } diff --git a/Libraries/LibWeb/Internals/Internals.h b/Libraries/LibWeb/Internals/Internals.h index a299738b14..74d81f45e5 100644 --- a/Libraries/LibWeb/Internals/Internals.h +++ b/Libraries/LibWeb/Internals/Internals.h @@ -105,6 +105,7 @@ public: void set_highlighted_node(GC::Ptr node); void clear_element(HTML::HTMLElement&); + void set_environments_top_level_url(StringView url); private: explicit Internals(JS::Realm&); diff --git a/Libraries/LibWeb/Internals/Internals.idl b/Libraries/LibWeb/Internals/Internals.idl index 5ff01ad3ac..86115cc0ca 100644 --- a/Libraries/LibWeb/Internals/Internals.idl +++ b/Libraries/LibWeb/Internals/Internals.idl @@ -79,6 +79,8 @@ interface Internals { undefined handleSDLInputEvents(); + undefined setEnvironmentsTopLevelURL(USVString url); + InternalGamepad connectVirtualGamepad(); undefined setHighlightedNode(Node? node); diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp index da7e523ddd..485e691c93 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/BindingsGenerator/IDLGenerators.cpp @@ -3731,11 +3731,21 @@ void @class_name@::initialize(JS::Realm& realm) continue; auto attribute_generator = generator_for_member(attribute.name, attribute.extended_attributes); + + if (attribute.extended_attributes.contains("SecureContext")) { + attribute_generator.append(R"~~~( + if (HTML::is_secure_context(Bindings::principal_host_defined_environment_settings_object(realm))) {)~~~"); + } + if (attribute.extended_attributes.contains("FIXME")) { attribute_generator.set("attribute.name", attribute.name); attribute_generator.append(R"~~~( @define_direct_property@("@attribute.name@"_utf16_fly_string, JS::js_undefined(), default_attributes | JS::Attribute::Unimplemented); )~~~"); + if (attribute.extended_attributes.contains("SecureContext")) { + attribute_generator.append(R"~~~( + })~~~"); + } continue; } @@ -3778,6 +3788,11 @@ void @class_name@::initialize(JS::Realm& realm) attribute_generator.append(R"~~~( @define_direct_accessor@("@attribute.name@"_utf16_fly_string, native_@attribute.getter_callback@, native_@attribute.setter_callback@, default_attributes); )~~~"); + + if (attribute.extended_attributes.contains("SecureContext")) { + attribute_generator.append(R"~~~( + })~~~"); + } } for (auto& function : interface.functions) { @@ -3823,6 +3838,11 @@ void @class_name@::initialize(JS::Realm& realm) function_generator.set("function.name:snakecase", make_input_acceptable_cpp(overload_set.key.to_snakecase())); function_generator.set("function.length", ByteString::number(get_shortest_function_length(overload_set.value))); + if (function.extended_attributes.contains("SecureContext")) { + function_generator.append(R"~~~( + if (HTML::is_secure_context(Bindings::principal_host_defined_environment_settings_object(realm))) {)~~~"); + } + if (any_of(overload_set.value, [](auto const& function) { return function.extended_attributes.contains("Unscopable"); })) { VERIFY(all_of(overload_set.value, [](auto const& function) { return function.extended_attributes.contains("Unscopable"); })); function_generator.append(R"~~~( @@ -3833,6 +3853,11 @@ void @class_name@::initialize(JS::Realm& realm) function_generator.append(R"~~~( @define_native_function@(realm, "@function.name@"_utf16_fly_string, @function.name:snakecase@, @function.length@, default_attributes); )~~~"); + + if (function.extended_attributes.contains("SecureContext")) { + function_generator.append(R"~~~( + })~~~"); + } } bool should_generate_stringifier = true; @@ -5548,6 +5573,7 @@ void generate_prototype_implementation(IDL::Interface const& interface, StringBu #include #include #include +#include #include #include #include @@ -5969,6 +5995,7 @@ void generate_global_mixin_implementation(IDL::Interface const& interface, Strin #include #include #include +#include #include #include #include diff --git a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateWindowOrWorkerInterfaces.cpp b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateWindowOrWorkerInterfaces.cpp index 1542bcc084..6c982ab57a 100644 --- a/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateWindowOrWorkerInterfaces.cpp +++ b/Meta/Lagom/Tools/CodeGenerators/LibWeb/GenerateWindowOrWorkerInterfaces.cpp @@ -120,6 +120,7 @@ static ErrorOr generate_intrinsic_definitions_implementation(StringView ou #include #include #include +#include #include #include #include @@ -182,6 +183,27 @@ void Intrinsics::create_web_namespace<@namespace_class@>(JS::Realm& realm) )~~~"); }; + generator.append(R"~~~( +static bool is_secure_context_interface(InterfaceName name) +{ + switch (name) { +)~~~"); + for (auto const& interface : interface_sets.intrinsics) { + if (!interface.extended_attributes.contains("SecureContext")) + continue; + + generator.set("secure_context_interface_name", interface.name); + generator.append(R"~~~( + case InterfaceName::@secure_context_interface_name@:)~~~"); + } + generator.append(R"~~~( + return true; + default: + return false; + } +} +)~~~"); + auto generate_global_exposed = [&generator](StringView global_name, Vector const& interface_set) { generator.set("global_name", global_name); generator.append(R"~~~( @@ -235,8 +257,11 @@ bool is_exposed(InterfaceName name, JS::Realm& realm) TODO(); // FIXME: ServiceWorkerGlobalScope and WorkletGlobalScope. } - // FIXME: 2. If realm’s settings object is not a secure context, and construct is conditionally exposed on - // [SecureContext], then return false. + // 2. If realm’s settings object is not a secure context, and construct is conditionally exposed on + // [SecureContext], then return false. + if (is_secure_context_interface(name) && HTML::is_non_secure_context(principal_host_defined_environment_settings_object(realm))) + return false; + // FIXME: 3. If realm’s settings object’s cross-origin isolated capability is false, and construct is // conditionally exposed on [CrossOriginIsolated], then return false. @@ -352,6 +377,7 @@ static ErrorOr generate_exposed_interface_implementation(StringView class_ #include #include #include +#include )~~~"); for (auto& interface : exposed_interfaces) { auto gen = generator.fork(); @@ -382,6 +408,7 @@ namespace Web::Bindings { void add_@global_object_snake_name@_exposed_interfaces(JS::Object& global) { static constexpr u8 attr = JS::Attribute::Writable | JS::Attribute::Configurable; + [[maybe_unused]] bool is_secure_context = HTML::is_secure_context(HTML::relevant_principal_settings_object(global)); )~~~"); auto add_interface = [class_name](SourceGenerator& gen, IDL::Interface const& interface) { @@ -393,6 +420,11 @@ void add_@global_object_snake_name@_exposed_interfaces(JS::Object& global) gen.set("interface_name", interface.namespaced_name); gen.set("prototype_class", interface.prototype_class); + if (interface.extended_attributes.contains("SecureContext")) { + gen.append(R"~~~( + if (is_secure_context) {)~~~"); + } + gen.append(R"~~~( global.define_intrinsic_accessor("@interface_name@"_utf16_fly_string, attr, [](auto& realm) -> JS::Value { return &ensure_web_constructor<@prototype_class@>(realm, "@interface_name@"_fly_string); });)~~~"); @@ -417,6 +449,11 @@ void add_@global_object_snake_name@_exposed_interfaces(JS::Object& global) gen.append(R"~~~( global.define_intrinsic_accessor("@legacy_interface_name@"_utf16_fly_string, attr, [](auto& realm) -> JS::Value { return &ensure_web_constructor<@prototype_class@>(realm, "@legacy_interface_name@"_fly_string); });)~~~"); } + + if (interface.extended_attributes.contains("SecureContext")) { + gen.append(R"~~~( + })~~~"); + } }; auto add_namespace = [](SourceGenerator& gen, StringView name, StringView namespace_class) { diff --git a/Tests/LibWeb/Text/expected/secure-context.txt b/Tests/LibWeb/Text/expected/secure-context.txt new file mode 100644 index 0000000000..7d007925be --- /dev/null +++ b/Tests/LibWeb/Text/expected/secure-context.txt @@ -0,0 +1,5 @@ +{ + "SubtleCrypto": false, + "Clipboard": false, + "CookieChangeEvent": true +} diff --git a/Tests/LibWeb/Text/input/secure-context.html b/Tests/LibWeb/Text/input/secure-context.html new file mode 100644 index 0000000000..60a5da9213 --- /dev/null +++ b/Tests/LibWeb/Text/input/secure-context.html @@ -0,0 +1,35 @@ + + +