WindowProxy.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /*
  2. * Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Optional.h>
  7. #include <LibJS/Heap/MarkedVector.h>
  8. #include <LibJS/Runtime/Completion.h>
  9. #include <LibJS/Runtime/GlobalObject.h>
  10. #include <LibJS/Runtime/PropertyDescriptor.h>
  11. #include <LibJS/Runtime/PropertyKey.h>
  12. #include <LibWeb/DOM/Document.h>
  13. #include <LibWeb/HTML/CrossOrigin/AbstractOperations.h>
  14. #include <LibWeb/HTML/CrossOrigin/Reporting.h>
  15. #include <LibWeb/HTML/Scripting/Environments.h>
  16. #include <LibWeb/HTML/Window.h>
  17. #include <LibWeb/HTML/WindowProxy.h>
  18. #include <LibWeb/WebIDL/DOMException.h>
  19. namespace Web::HTML {
  20. // 7.4 The WindowProxy exotic object, https://html.spec.whatwg.org/multipage/window-object.html#the-windowproxy-exotic-object
  21. WindowProxy::WindowProxy(JS::Realm& realm)
  22. : JS::Object(realm, nullptr)
  23. {
  24. }
  25. // 7.4.1 [[GetPrototypeOf]] ( ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getprototypeof
  26. JS::ThrowCompletionOr<JS::Object*> WindowProxy::internal_get_prototype_of() const
  27. {
  28. // 1. Let W be the value of the [[Window]] internal slot of this.
  29. // 2. If IsPlatformObjectSameOrigin(W) is true, then return ! OrdinaryGetPrototypeOf(W).
  30. if (is_platform_object_same_origin(*m_window))
  31. return MUST(m_window->internal_get_prototype_of());
  32. // 3. Return null.
  33. return nullptr;
  34. }
  35. // 7.4.2 [[SetPrototypeOf]] ( V ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-setprototypeof
  36. JS::ThrowCompletionOr<bool> WindowProxy::internal_set_prototype_of(Object* prototype)
  37. {
  38. // 1. Return ! SetImmutablePrototype(this, V).
  39. return MUST(set_immutable_prototype(prototype));
  40. }
  41. // 7.4.3 [[IsExtensible]] ( ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-isextensible
  42. JS::ThrowCompletionOr<bool> WindowProxy::internal_is_extensible() const
  43. {
  44. // 1. Return true.
  45. return true;
  46. }
  47. // 7.4.4 [[PreventExtensions]] ( ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-preventextensions
  48. JS::ThrowCompletionOr<bool> WindowProxy::internal_prevent_extensions()
  49. {
  50. // 1. Return false.
  51. return false;
  52. }
  53. // 7.4.5 [[GetOwnProperty]] ( P ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-getownproperty
  54. JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> WindowProxy::internal_get_own_property(JS::PropertyKey const& property_key) const
  55. {
  56. auto& vm = this->vm();
  57. // 1. Let W be the value of the [[Window]] internal slot of this.
  58. // 2. If P is an array index property name, then:
  59. if (property_key.is_number()) {
  60. // 1. Let index be ! ToUint32(P).
  61. auto index = property_key.as_number();
  62. // 2. Let children be the document-tree child navigables of W's associated Document.
  63. auto children = m_window->associated_document().document_tree_child_navigables();
  64. // 3. Let value be undefined.
  65. Optional<JS::Value> value;
  66. // 4. If index is less than children's size, then:
  67. if (index < children.size()) {
  68. // 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.
  69. // NOTE: children are coming sorted in required order from document_tree_child_navigables()
  70. // 2. Set value to children[index]'s active WindowProxy.
  71. value = children[index]->active_window_proxy();
  72. }
  73. // 5. If value is undefined, then:
  74. if (!value.has_value()) {
  75. // 1. If IsPlatformObjectSameOrigin(W) is true, then return undefined.
  76. if (is_platform_object_same_origin(*m_window))
  77. return Optional<JS::PropertyDescriptor> {};
  78. // 2. Throw a "SecurityError" DOMException.
  79. return throw_completion(WebIDL::SecurityError::create(m_window->realm(), MUST(String::formatted("Can't access property '{}' on cross-origin object", property_key))));
  80. }
  81. // 6. Return PropertyDescriptor{ [[Value]]: value, [[Writable]]: false, [[Enumerable]]: true, [[Configurable]]: true }.
  82. return JS::PropertyDescriptor { .value = move(value), .writable = false, .enumerable = true, .configurable = true };
  83. }
  84. // 3. If IsPlatformObjectSameOrigin(W) is true, then return ! OrdinaryGetOwnProperty(W, P).
  85. // 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.
  86. if (is_platform_object_same_origin(*m_window))
  87. return m_window->internal_get_own_property(property_key);
  88. // 4. Let property be CrossOriginGetOwnPropertyHelper(W, P).
  89. auto property = cross_origin_get_own_property_helper(const_cast<Window*>(m_window.ptr()), property_key);
  90. // 5. If property is not undefined, then return property.
  91. if (property.has_value())
  92. return property;
  93. // FIXME: 6. If property is undefined and P is in W's document-tree child browsing context name property set, then:
  94. if (false) {
  95. // FIXME: 1. Let value be the WindowProxy object of the named object of W with the name P.
  96. auto value = JS::js_undefined();
  97. // 2. Return PropertyDescriptor{ [[Value]]: value, [[Enumerable]]: false, [[Writable]]: false, [[Configurable]]: true }.
  98. // 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.
  99. return JS::PropertyDescriptor { .value = value, .writable = false, .enumerable = false, .configurable = true };
  100. }
  101. // 7. Return ? CrossOriginPropertyFallback(P).
  102. return TRY(cross_origin_property_fallback(vm, property_key));
  103. }
  104. // 7.4.6 [[DefineOwnProperty]] ( P, Desc ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-defineownproperty
  105. JS::ThrowCompletionOr<bool> WindowProxy::internal_define_own_property(JS::PropertyKey const& property_key, JS::PropertyDescriptor const& descriptor)
  106. {
  107. // 1. Let W be the value of the [[Window]] internal slot of this.
  108. // 2. If IsPlatformObjectSameOrigin(W) is true, then:
  109. if (is_platform_object_same_origin(*m_window)) {
  110. // 1. If P is an array index property name, return false.
  111. if (property_key.is_number())
  112. return false;
  113. // 2. Return ? OrdinaryDefineOwnProperty(W, P, Desc).
  114. // 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.
  115. return m_window->internal_define_own_property(property_key, descriptor);
  116. }
  117. // 3. Throw a "SecurityError" DOMException.
  118. return throw_completion(WebIDL::SecurityError::create(m_window->realm(), MUST(String::formatted("Can't define property '{}' on cross-origin object", property_key))));
  119. }
  120. // 7.4.7 [[Get]] ( P, Receiver ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-get
  121. JS::ThrowCompletionOr<JS::Value> WindowProxy::internal_get(JS::PropertyKey const& property_key, JS::Value receiver, JS::CacheablePropertyMetadata*) const
  122. {
  123. auto& vm = this->vm();
  124. // 1. Let W be the value of the [[Window]] internal slot of this.
  125. // 2. Check if an access between two browsing contexts should be reported, given the current global object's browsing context, W's browsing context, P, and the current settings object.
  126. check_if_access_between_two_browsing_contexts_should_be_reported(*verify_cast<Window>(current_global_object()).browsing_context(), *m_window->browsing_context(), property_key, current_settings_object());
  127. // 3. If IsPlatformObjectSameOrigin(W) is true, then return ? OrdinaryGet(this, P, Receiver).
  128. // NOTE: this is passed rather than W as OrdinaryGet and CrossOriginGet will invoke the [[GetOwnProperty]] internal method.
  129. if (is_platform_object_same_origin(*m_window))
  130. return JS::Object::internal_get(property_key, receiver);
  131. // 4. Return ? CrossOriginGet(this, P, Receiver).
  132. // NOTE: this is passed rather than W as OrdinaryGet and CrossOriginGet will invoke the [[GetOwnProperty]] internal method.
  133. return cross_origin_get(vm, *this, property_key, receiver);
  134. }
  135. // 7.4.8 [[Set]] ( P, V, Receiver ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-set
  136. JS::ThrowCompletionOr<bool> WindowProxy::internal_set(JS::PropertyKey const& property_key, JS::Value value, JS::Value receiver)
  137. {
  138. auto& vm = this->vm();
  139. // 1. Let W be the value of the [[Window]] internal slot of this.
  140. // 2. Check if an access between two browsing contexts should be reported, given the current global object's browsing context, W's browsing context, P, and the current settings object.
  141. check_if_access_between_two_browsing_contexts_should_be_reported(*verify_cast<Window>(current_global_object()).browsing_context(), *m_window->browsing_context(), property_key, current_settings_object());
  142. // 3. If IsPlatformObjectSameOrigin(W) is true, then:
  143. if (is_platform_object_same_origin(*m_window)) {
  144. // 1. If P is an array index property name, then return false.
  145. if (property_key.is_number())
  146. return false;
  147. // 2. Return ? OrdinarySet(W, P, V, Receiver).
  148. return m_window->internal_set(property_key, value, receiver);
  149. }
  150. // 4. Return ? CrossOriginSet(this, P, V, Receiver).
  151. // NOTE: this is passed rather than W as CrossOriginSet will invoke the [[GetOwnProperty]] internal method.
  152. return cross_origin_set(vm, *this, property_key, value, receiver);
  153. }
  154. // 7.4.9 [[Delete]] ( P ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-delete
  155. JS::ThrowCompletionOr<bool> WindowProxy::internal_delete(JS::PropertyKey const& property_key)
  156. {
  157. // 1. Let W be the value of the [[Window]] internal slot of this.
  158. // 2. If IsPlatformObjectSameOrigin(W) is true, then:
  159. if (is_platform_object_same_origin(*m_window)) {
  160. // 1. If P is an array index property name, then:
  161. if (property_key.is_number()) {
  162. // 2. Let desc be ! this.[[GetOwnProperty]](P).
  163. auto descriptor = MUST(internal_get_own_property(property_key));
  164. // 2. If desc is undefined, then return true.
  165. if (!descriptor.has_value())
  166. return true;
  167. // 3. Return false.
  168. return false;
  169. }
  170. // 2. Return ? OrdinaryDelete(W, P).
  171. return m_window->internal_delete(property_key);
  172. }
  173. // 3. Throw a "SecurityError" DOMException.
  174. return throw_completion(WebIDL::SecurityError::create(m_window->realm(), MUST(String::formatted("Can't delete property '{}' on cross-origin object", property_key))));
  175. }
  176. // 7.4.10 [[OwnPropertyKeys]] ( ), https://html.spec.whatwg.org/multipage/window-object.html#windowproxy-ownpropertykeys
  177. JS::ThrowCompletionOr<JS::MarkedVector<JS::Value>> WindowProxy::internal_own_property_keys() const
  178. {
  179. auto& event_loop = main_thread_event_loop();
  180. auto& vm = event_loop.vm();
  181. // 1. Let W be the value of the [[Window]] internal slot of this.
  182. // 2. Let keys be a new empty List.
  183. auto keys = JS::MarkedVector<JS::Value> { vm.heap() };
  184. // 3. Let maxProperties be W's associated Document's document-tree child navigables's size.
  185. auto max_properties = m_window->associated_document().document_tree_child_navigables().size();
  186. // 4. Let index be 0.
  187. // 5. Repeat while index < maxProperties,
  188. for (size_t i = 0; i < max_properties; ++i) {
  189. // 1. Add ! ToString(index) as the last element of keys.
  190. keys.append(JS::PrimitiveString::create(vm, DeprecatedString::number(i)));
  191. // 2. Increment index by 1.
  192. }
  193. // 6. If IsPlatformObjectSameOrigin(W) is true, then return the concatenation of keys and OrdinaryOwnPropertyKeys(W).
  194. if (is_platform_object_same_origin(*m_window)) {
  195. keys.extend(MUST(m_window->internal_own_property_keys()));
  196. return keys;
  197. }
  198. // 7. Return the concatenation of keys and ! CrossOriginOwnPropertyKeys(W).
  199. keys.extend(cross_origin_own_property_keys(m_window.ptr()));
  200. return keys;
  201. }
  202. void WindowProxy::visit_edges(JS::Cell::Visitor& visitor)
  203. {
  204. Base::visit_edges(visitor);
  205. visitor.visit(m_window.ptr());
  206. }
  207. void WindowProxy::set_window(JS::NonnullGCPtr<Window> window)
  208. {
  209. m_window = move(window);
  210. }
  211. JS::NonnullGCPtr<BrowsingContext> WindowProxy::associated_browsing_context() const
  212. {
  213. return *m_window->associated_document().browsing_context();
  214. }
  215. }