2022-03-08 19:37:41 +01:00
/*
* Copyright ( c ) 2022 , Linus Groh < linusg @ serenityos . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <AK/Optional.h>
2024-12-26 14:32:52 +01:00
# include <LibGC/RootVector.h>
2022-03-08 19:37:41 +01:00
# include <LibJS/Runtime/Completion.h>
# include <LibJS/Runtime/GlobalObject.h>
# include <LibJS/Runtime/PropertyDescriptor.h>
# include <LibJS/Runtime/PropertyKey.h>
2023-04-20 17:41:32 +01:00
# include <LibWeb/DOM/Document.h>
2022-09-24 14:02:47 +01:00
# include <LibWeb/HTML/CrossOrigin/AbstractOperations.h>
2022-03-08 19:37:41 +01:00
# include <LibWeb/HTML/CrossOrigin/Reporting.h>
# include <LibWeb/HTML/Scripting/Environments.h>
# include <LibWeb/HTML/Window.h>
2022-09-24 16:24:41 +01:00
# include <LibWeb/HTML/WindowProxy.h>
2022-09-25 17:28:46 +01:00
# include <LibWeb/WebIDL/DOMException.h>
2022-03-08 19:37:41 +01:00
2022-09-24 16:24:41 +01:00
namespace Web : : HTML {
2022-03-08 19:37:41 +01:00
2024-11-15 04:01:23 +13:00
GC_DEFINE_ALLOCATOR ( WindowProxy ) ;
2023-11-19 19:47:52 +01:00
2022-03-08 19:37:41 +01:00
// 7.4 The WindowProxy exotic object, https://html.spec.whatwg.org/multipage/window-object.html#the-windowproxy-exotic-object
2022-10-15 23:10:56 +02:00
WindowProxy : : WindowProxy ( JS : : Realm & realm )
2023-11-09 07:29:52 +00:00
: JS : : Object ( realm , nullptr , MayInterfereWithIndexedPropertyAccess : : Yes )
2022-03-08 19:37:41 +01:00
{
}
// 7.4.1 [[GetPrototypeOf]] ( ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getprototypeof
JS : : ThrowCompletionOr < JS : : Object * > WindowProxy : : internal_get_prototype_of ( ) const
{
// 1. Let W be the value of the [[Window]] internal slot of this.
// 2. If IsPlatformObjectSameOrigin(W) is true, then return ! OrdinaryGetPrototypeOf(W).
if ( is_platform_object_same_origin ( * m_window ) )
return MUST ( m_window - > internal_get_prototype_of ( ) ) ;
// 3. Return null.
return nullptr ;
}
// 7.4.2 [[SetPrototypeOf]] ( V ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-setprototypeof
JS : : ThrowCompletionOr < bool > WindowProxy : : internal_set_prototype_of ( Object * prototype )
{
// 1. Return ! SetImmutablePrototype(this, V).
return MUST ( set_immutable_prototype ( prototype ) ) ;
}
// 7.4.3 [[IsExtensible]] ( ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-isextensible
JS : : ThrowCompletionOr < bool > WindowProxy : : internal_is_extensible ( ) const
{
// 1. Return true.
return true ;
}
// 7.4.4 [[PreventExtensions]] ( ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-preventextensions
JS : : ThrowCompletionOr < bool > WindowProxy : : internal_prevent_extensions ( )
{
// 1. Return false.
return false ;
}
// 7.4.5 [[GetOwnProperty]] ( P ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getownproperty
JS : : ThrowCompletionOr < Optional < JS : : PropertyDescriptor > > WindowProxy : : internal_get_own_property ( JS : : PropertyKey const & property_key ) const
{
2022-08-21 21:21:52 +01:00
auto & vm = this - > vm ( ) ;
2022-03-08 19:37:41 +01:00
// 1. Let W be the value of the [[Window]] internal slot of this.
// 2. If P is an array index property name, then:
if ( property_key . is_number ( ) ) {
// 1. Let index be ! ToUint32(P).
auto index = property_key . as_number ( ) ;
2023-08-23 02:15:29 +02:00
// 2. Let children be the document-tree child navigables of W's associated Document.
auto children = m_window - > associated_document ( ) . document_tree_child_navigables ( ) ;
2022-03-08 19:37:41 +01:00
// 3. Let value be undefined.
Optional < JS : : Value > value ;
2023-08-23 02:15:29 +02:00
// 4. If index is less than children's size, then:
if ( index < children . size ( ) ) {
// 1. Sort children in ascending order, with navigableA being less than navigableB if navigableA's container was inserted into W's associated Document earlier than navigableB's container was.
// NOTE: children are coming sorted in required order from document_tree_child_navigables()
// 2. Set value to children[index]'s active WindowProxy.
value = children [ index ] - > active_window_proxy ( ) ;
2022-03-08 19:37:41 +01:00
}
// 5. If value is undefined, then:
if ( ! value . has_value ( ) ) {
// 1. If IsPlatformObjectSameOrigin(W) is true, then return undefined.
if ( is_platform_object_same_origin ( * m_window ) )
return Optional < JS : : PropertyDescriptor > { } ;
// 2. Throw a "SecurityError" DOMException.
2025-08-07 19:31:52 -04:00
return throw_completion ( WebIDL : : SecurityError : : create ( m_window - > realm ( ) , Utf16String : : formatted ( " Can't access property ' { } ' on cross - origin object " , property_key)));
2022-03-08 19:37:41 +01:00
}
2025-06-24 11:18:14 +01:00
// 6. Return PropertyDescriptor { [[Value]]: value, [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: true }.
2022-03-08 19:37:41 +01:00
return JS : : PropertyDescriptor { . value = move ( value ) , . writable = false , . enumerable = true , . configurable = true } ;
}
// 3. If IsPlatformObjectSameOrigin(W) is true, then return ! OrdinaryGetOwnProperty(W, P).
// NOTE: This is a willful violation of the JavaScript specification's invariants of the essential internal methods to maintain compatibility with existing web content. See tc39/ecma262 issue #672 for more information.
if ( is_platform_object_same_origin ( * m_window ) )
return m_window - > internal_get_own_property ( property_key ) ;
// 4. Let property be CrossOriginGetOwnPropertyHelper(W, P).
2022-09-24 16:24:41 +01:00
auto property = cross_origin_get_own_property_helper ( const_cast < Window * > ( m_window . ptr ( ) ) , property_key ) ;
2022-03-08 19:37:41 +01:00
// 5. If property is not undefined, then return property.
if ( property . has_value ( ) )
return property ;
2023-09-18 21:56:11 -06:00
// 6. If property is undefined and P is in W's document-tree child navigable target name property set, then:
auto navigable_property_set = m_window - > document_tree_child_navigable_target_name_property_set ( ) ;
2025-08-02 19:27:29 -04:00
auto property_key_string = property_key . to_string ( ) . to_utf8_but_should_be_ported_to_utf16 ( ) ;
2024-04-03 21:51:34 -04:00
2025-03-18 18:08:02 -05:00
if ( auto navigable = navigable_property_set . get ( property_key_string ) ; navigable . has_value ( ) ) {
2023-09-18 21:56:11 -06:00
// 1. Let value be the active WindowProxy of the named object of W with the name P.
auto value = navigable . value ( ) - > active_window_proxy ( ) ;
2022-03-08 19:37:41 +01:00
2025-06-24 11:18:14 +01:00
// 2. Return PropertyDescriptor { [[Value]]: value, [[Enumerable]]: false, [[Writable]]: false, [[Configurable]]: true }.
2022-03-08 19:37:41 +01:00
// NOTE: The reason the property descriptors are non-enumerable, despite this mismatching the same-origin behavior, is for compatibility with existing web content. See issue #3183 for details.
return JS : : PropertyDescriptor { . value = value , . writable = false , . enumerable = false , . configurable = true } ;
}
// 7. Return ? CrossOriginPropertyFallback(P).
2022-09-24 16:24:41 +01:00
return TRY ( cross_origin_property_fallback ( vm , property_key ) ) ;
2022-03-08 19:37:41 +01:00
}
// 7.4.6 [[DefineOwnProperty]] ( P, Desc ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-defineownproperty
2025-09-15 16:43:27 +02:00
JS : : ThrowCompletionOr < bool > WindowProxy : : internal_define_own_property ( JS : : PropertyKey const & property_key , JS : : PropertyDescriptor & descriptor , Optional < JS : : PropertyDescriptor > * )
2022-03-08 19:37:41 +01:00
{
// 1. Let W be the value of the [[Window]] internal slot of this.
// 2. If IsPlatformObjectSameOrigin(W) is true, then:
if ( is_platform_object_same_origin ( * m_window ) ) {
// 1. If P is an array index property name, return false.
if ( property_key . is_number ( ) )
return false ;
// 2. Return ? OrdinaryDefineOwnProperty(W, P, Desc).
// NOTE: This is a willful violation of the JavaScript specification's invariants of the essential internal methods to maintain compatibility with existing web content. See tc39/ecma262 issue #672 for more information.
return m_window - > internal_define_own_property ( property_key , descriptor ) ;
}
// 3. Throw a "SecurityError" DOMException.
2025-08-07 19:31:52 -04:00
return throw_completion ( WebIDL : : SecurityError : : create ( m_window - > realm ( ) , Utf16String : : formatted ( " Can't define property ' { } ' on cross - origin object " , property_key)));
2022-03-08 19:37:41 +01:00
}
2024-10-21 13:35:24 +13:00
// 7.4.7 [[Get]] ( P, Receiver ), https://html.spec.whatwg.org/multipage/nav-history-apis.html#windowproxy-get
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#windowproxy-get
2025-09-15 17:23:39 +02:00
JS : : ThrowCompletionOr < JS : : Value > WindowProxy : : internal_get ( JS : : PropertyKey const & property_key , JS : : Value receiver , JS : : CacheableGetPropertyMetadata * , PropertyLookupPhase ) const
2022-03-08 19:37:41 +01:00
{
2022-08-21 21:21:52 +01:00
auto & vm = this - > vm ( ) ;
2022-03-08 19:37:41 +01:00
// 1. Let W be the value of the [[Window]] internal slot of this.
2024-10-21 13:48:44 +13:00
// 2. Check if an access between two browsing contexts should be reported, given the current principal global object's browsing context, W's browsing context, P, and the current principal settings object.
2025-05-30 00:34:50 +02:00
check_if_access_between_two_browsing_contexts_should_be_reported ( as < Window > ( current_principal_global_object ( ) ) . browsing_context ( ) , m_window - > browsing_context ( ) , property_key , current_principal_settings_object ( ) ) ;
2022-03-08 19:37:41 +01:00
// 3. If IsPlatformObjectSameOrigin(W) is true, then return ? OrdinaryGet(this, P, Receiver).
// NOTE: this is passed rather than W as OrdinaryGet and CrossOriginGet will invoke the [[GetOwnProperty]] internal method.
if ( is_platform_object_same_origin ( * m_window ) )
2023-07-10 20:50:24 +02:00
return JS : : Object : : internal_get ( property_key , receiver ) ;
2022-03-08 19:37:41 +01:00
// 4. Return ? CrossOriginGet(this, P, Receiver).
// NOTE: this is passed rather than W as OrdinaryGet and CrossOriginGet will invoke the [[GetOwnProperty]] internal method.
2022-09-24 16:24:41 +01:00
return cross_origin_get ( vm , * this , property_key , receiver ) ;
2022-03-08 19:37:41 +01:00
}
2024-10-21 13:35:24 +13:00
// 7.4.8 [[Set]] ( P, V, Receiver ), https://html.spec.whatwg.org/multipage/nav-history-apis.html#windowproxy-set
// https://html.spec.whatwg.org/multipage/nav-history-apis.html#windowproxy-set
2025-09-15 17:23:39 +02:00
JS : : ThrowCompletionOr < bool > WindowProxy : : internal_set ( JS : : PropertyKey const & property_key , JS : : Value value , JS : : Value receiver , JS : : CacheableSetPropertyMetadata * , PropertyLookupPhase )
2022-03-08 19:37:41 +01:00
{
2022-08-21 21:21:52 +01:00
auto & vm = this - > vm ( ) ;
2022-03-08 19:37:41 +01:00
// 1. Let W be the value of the [[Window]] internal slot of this.
2024-10-21 13:48:44 +13:00
// 2. Check if an access between two browsing contexts should be reported, given the current principal global object's browsing context, W's browsing context, P, and the current principal settings object.
2025-05-30 00:34:50 +02:00
check_if_access_between_two_browsing_contexts_should_be_reported ( as < Window > ( current_principal_global_object ( ) ) . browsing_context ( ) , m_window - > browsing_context ( ) , property_key , current_principal_settings_object ( ) ) ;
2022-03-08 19:37:41 +01:00
// 3. If IsPlatformObjectSameOrigin(W) is true, then:
if ( is_platform_object_same_origin ( * m_window ) ) {
// 1. If P is an array index property name, then return false.
if ( property_key . is_number ( ) )
return false ;
// 2. Return ? OrdinarySet(W, P, V, Receiver).
return m_window - > internal_set ( property_key , value , receiver ) ;
}
// 4. Return ? CrossOriginSet(this, P, V, Receiver).
// NOTE: this is passed rather than W as CrossOriginSet will invoke the [[GetOwnProperty]] internal method.
2022-09-24 16:24:41 +01:00
return cross_origin_set ( vm , * this , property_key , value , receiver ) ;
2022-03-08 19:37:41 +01:00
}
// 7.4.9 [[Delete]] ( P ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-delete
JS : : ThrowCompletionOr < bool > WindowProxy : : internal_delete ( JS : : PropertyKey const & property_key )
{
// 1. Let W be the value of the [[Window]] internal slot of this.
// 2. If IsPlatformObjectSameOrigin(W) is true, then:
if ( is_platform_object_same_origin ( * m_window ) ) {
// 1. If P is an array index property name, then:
if ( property_key . is_number ( ) ) {
// 2. Let desc be ! this.[[GetOwnProperty]](P).
auto descriptor = MUST ( internal_get_own_property ( property_key ) ) ;
// 2. If desc is undefined, then return true.
if ( ! descriptor . has_value ( ) )
return true ;
// 3. Return false.
return false ;
}
// 2. Return ? OrdinaryDelete(W, P).
return m_window - > internal_delete ( property_key ) ;
}
// 3. Throw a "SecurityError" DOMException.
2025-08-07 19:31:52 -04:00
return throw_completion ( WebIDL : : SecurityError : : create ( m_window - > realm ( ) , Utf16String : : formatted ( " Can't delete property ' { } ' on cross - origin object " , property_key)));
2022-03-08 19:37:41 +01:00
}
// 7.4.10 [[OwnPropertyKeys]] ( ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-ownpropertykeys
2025-05-16 04:14:12 +01:00
JS : : ThrowCompletionOr < GC : : RootVector < JS : : Value > > WindowProxy : : internal_own_property_keys ( ) const
2022-03-08 19:37:41 +01:00
{
2025-05-16 04:14:12 +01:00
auto & event_loop = main_thread_event_loop ( ) ;
auto & vm = event_loop . vm ( ) ;
2022-03-08 19:37:41 +01:00
// 1. Let W be the value of the [[Window]] internal slot of this.
// 2. Let keys be a new empty List.
2025-05-16 04:14:12 +01:00
auto keys = GC : : RootVector < JS : : Value > { vm . heap ( ) } ;
2022-03-08 19:37:41 +01:00
2023-09-03 22:53:36 +02:00
// 3. Let maxProperties be W's associated Document's document-tree child navigables's size.
auto max_properties = m_window - > associated_document ( ) . document_tree_child_navigables ( ) . size ( ) ;
2022-03-08 19:37:41 +01:00
// 4. Let index be 0.
// 5. Repeat while index < maxProperties,
for ( size_t i = 0 ; i < max_properties ; + + i ) {
// 1. Add ! ToString(index) as the last element of keys.
2025-10-04 11:00:20 +02:00
keys . append ( JS : : PrimitiveString : : create_from_unsigned_integer ( vm , i ) ) ;
2022-03-08 19:37:41 +01:00
// 2. Increment index by 1.
}
// 6. If IsPlatformObjectSameOrigin(W) is true, then return the concatenation of keys and OrdinaryOwnPropertyKeys(W).
if ( is_platform_object_same_origin ( * m_window ) ) {
keys . extend ( MUST ( m_window - > internal_own_property_keys ( ) ) ) ;
return keys ;
}
// 7. Return the concatenation of keys and ! CrossOriginOwnPropertyKeys(W).
2022-08-28 13:42:07 +02:00
keys . extend ( cross_origin_own_property_keys ( m_window . ptr ( ) ) ) ;
2022-03-08 19:37:41 +01:00
return keys ;
}
void WindowProxy : : visit_edges ( JS : : Cell : : Visitor & visitor )
{
2022-08-28 13:42:07 +02:00
Base : : visit_edges ( visitor ) ;
2023-11-19 16:18:00 +13:00
visitor . visit ( m_window ) ;
2022-03-08 19:37:41 +01:00
}
2024-11-15 04:01:23 +13:00
void WindowProxy : : set_window ( GC : : Ref < Window > window )
2022-10-15 23:10:56 +02:00
{
2023-04-23 19:39:12 +03:00
m_window = move ( window ) ;
2022-10-15 23:10:56 +02:00
}
2024-11-15 04:01:23 +13:00
GC : : Ref < BrowsingContext > WindowProxy : : associated_browsing_context ( ) const
2023-04-20 17:41:32 +01:00
{
return * m_window - > associated_document ( ) . browsing_context ( ) ;
}
2022-03-08 19:37:41 +01:00
}