CrossOriginAbstractOperations.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. /*
  2. * Copyright (c) 2022, Linus Groh <linusg@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Variant.h>
  7. #include <AK/Vector.h>
  8. #include <LibJS/Runtime/AbstractOperations.h>
  9. #include <LibJS/Runtime/Completion.h>
  10. #include <LibJS/Runtime/GlobalObject.h>
  11. #include <LibJS/Runtime/NativeFunction.h>
  12. #include <LibJS/Runtime/Object.h>
  13. #include <LibJS/Runtime/PropertyDescriptor.h>
  14. #include <LibJS/Runtime/PropertyKey.h>
  15. #include <LibWeb/Bindings/CrossOriginAbstractOperations.h>
  16. #include <LibWeb/Bindings/DOMExceptionWrapper.h>
  17. #include <LibWeb/Bindings/LocationObject.h>
  18. #include <LibWeb/Bindings/WindowObject.h>
  19. #include <LibWeb/DOM/DOMException.h>
  20. #include <LibWeb/HTML/Scripting/Environments.h>
  21. namespace Web::Bindings {
  22. // 7.2.3.1 CrossOriginProperties ( O ), https://html.spec.whatwg.org/multipage/browsers.html#crossoriginproperties-(-o-)
  23. Vector<CrossOriginProperty> cross_origin_properties(Variant<LocationObject const*, WindowObject const*> const& object)
  24. {
  25. // 1. Assert: O is a Location or Window object.
  26. return object.visit(
  27. // 2. If O is a Location object, then return « { [[Property]]: "href", [[NeedsGet]]: false, [[NeedsSet]]: true }, { [[Property]]: "replace" } ».
  28. [](LocationObject const*) -> Vector<CrossOriginProperty> {
  29. return {
  30. { .property = "href"sv, .needs_get = false, .needs_set = true },
  31. { .property = "replace"sv },
  32. };
  33. },
  34. // 3. Return « { [[Property]]: "window", [[NeedsGet]]: true, [[NeedsSet]]: false }, { [[Property]]: "self", [[NeedsGet]]: true, [[NeedsSet]]: false }, { [[Property]]: "location", [[NeedsGet]]: true, [[NeedsSet]]: true }, { [[Property]]: "close" }, { [[Property]]: "closed", [[NeedsGet]]: true, [[NeedsSet]]: false }, { [[Property]]: "focus" }, { [[Property]]: "blur" }, { [[Property]]: "frames", [[NeedsGet]]: true, [[NeedsSet]]: false }, { [[Property]]: "length", [[NeedsGet]]: true, [[NeedsSet]]: false }, { [[Property]]: "top", [[NeedsGet]]: true, [[NeedsSet]]: false }, { [[Property]]: "opener", [[NeedsGet]]: true, [[NeedsSet]]: false }, { [[Property]]: "parent", [[NeedsGet]]: true, [[NeedsSet]]: false }, { [[Property]]: "postMessage" } ».
  35. [](WindowObject const*) -> Vector<CrossOriginProperty> {
  36. return {
  37. { .property = "window"sv, .needs_get = true, .needs_set = false },
  38. { .property = "self"sv, .needs_get = true, .needs_set = false },
  39. { .property = "location"sv, .needs_get = true, .needs_set = true },
  40. { .property = "close"sv },
  41. { .property = "closed"sv, .needs_get = true, .needs_set = false },
  42. { .property = "focus"sv },
  43. { .property = "blur"sv },
  44. { .property = "frames"sv, .needs_get = true, .needs_set = false },
  45. { .property = "length"sv, .needs_get = true, .needs_set = false },
  46. { .property = "top"sv, .needs_get = true, .needs_set = false },
  47. { .property = "opener"sv, .needs_get = true, .needs_set = false },
  48. { .property = "parent"sv, .needs_get = true, .needs_set = false },
  49. { .property = "postMessage"sv },
  50. };
  51. });
  52. }
  53. // 7.2.3.2 CrossOriginPropertyFallback ( P ), https://html.spec.whatwg.org/multipage/browsers.html#crossoriginpropertyfallback-(-p-)
  54. JS::ThrowCompletionOr<JS::PropertyDescriptor> cross_origin_property_fallback(JS::GlobalObject& global_object, JS::PropertyKey const& property_key)
  55. {
  56. auto& vm = global_object.vm();
  57. // 1. If P is "then", @@toStringTag, @@hasInstance, or @@isConcatSpreadable, then return PropertyDescriptor{ [[Value]]: undefined, [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.
  58. auto property_key_is_then = property_key.is_string() && property_key.as_string() == vm.names.then.as_string();
  59. auto property_key_is_allowed_symbol = property_key.is_symbol()
  60. && (property_key.as_symbol() == vm.well_known_symbol_to_string_tag()
  61. || property_key.as_symbol() == vm.well_known_symbol_has_instance()
  62. || property_key.as_symbol() == vm.well_known_symbol_is_concat_spreadable());
  63. if (property_key_is_then || property_key_is_allowed_symbol)
  64. return JS::PropertyDescriptor { .value = JS::js_undefined(), .writable = false, .enumerable = false, .configurable = true };
  65. // 2. Throw a "SecurityError" DOMException.
  66. return vm.throw_completion<DOMExceptionWrapper>(global_object, DOM::SecurityError::create(String::formatted("Can't access property '{}' on cross-origin object", property_key)));
  67. }
  68. // 7.2.3.3 IsPlatformObjectSameOrigin ( O ), https://html.spec.whatwg.org/multipage/browsers.html#isplatformobjectsameorigin-(-o-)
  69. bool is_platform_object_same_origin(JS::Object const& object)
  70. {
  71. // 1. Return true if the current settings object's origin is same origin-domain with O's relevant settings object's origin, and false otherwise.
  72. return HTML::current_settings_object().origin().is_same_origin_domain(HTML::relevant_settings_object(object).origin());
  73. }
  74. // 7.2.3.4 CrossOriginGetOwnPropertyHelper ( O, P ), https://html.spec.whatwg.org/multipage/browsers.html#crossorigingetownpropertyhelper-(-o,-p-)
  75. Optional<JS::PropertyDescriptor> cross_origin_get_own_property_helper(Variant<LocationObject*, WindowObject*> const& object, JS::PropertyKey const& property_key)
  76. {
  77. auto const* object_ptr = object.visit([](auto* o) { return static_cast<JS::Object const*>(o); });
  78. auto const object_const_variant = object.visit([](auto* o) { return Variant<LocationObject const*, WindowObject const*> { o }; });
  79. // 1. Let crossOriginKey be a tuple consisting of the current settings object, O's relevant settings object, and P.
  80. auto cross_origin_key = CrossOriginKey {
  81. .current_settings_object = (FlatPtr)&HTML::current_settings_object(),
  82. .relevant_settings_object = (FlatPtr)&HTML::relevant_settings_object(*object_ptr),
  83. .property_key = property_key,
  84. };
  85. // 2. For each e of CrossOriginProperties(O):
  86. for (auto const& entry : cross_origin_properties(object_const_variant)) {
  87. auto& cross_origin_property_descriptor_map = object.visit([](auto* o) -> CrossOriginPropertyDescriptorMap& { return o->cross_origin_property_descriptor_map(); });
  88. // 1. If the value of the [[CrossOriginPropertyDescriptorMap]] internal slot of O contains an entry whose key is crossOriginKey, then return that entry's value.
  89. auto it = cross_origin_property_descriptor_map.find(cross_origin_key);
  90. if (it != cross_origin_property_descriptor_map.end())
  91. return it->value;
  92. // 2. Let originalDesc be OrdinaryGetOwnProperty(O, P).
  93. auto original_descriptor = MUST((object_ptr->JS::Object::internal_get_own_property)(property_key));
  94. // 3. Let crossOriginDesc be undefined.
  95. auto cross_origin_descriptor = JS::PropertyDescriptor {};
  96. // 4. If e.[[NeedsGet]] and e.[[NeedsSet]] are absent, then:
  97. if (!entry.needs_get.has_value() && !entry.needs_set.has_value()) {
  98. // 1. Let value be originalDesc.[[Value]].
  99. auto value = original_descriptor->value;
  100. // 2. If IsCallable(value) is true, then set value to an anonymous built-in function, created in the current Realm Record, that performs the same steps as the IDL operation P on object O.
  101. if (value->is_function()) {
  102. value = JS::NativeFunction::create(
  103. HTML::current_global_object(), [function = JS::make_handle(*value)](auto&, auto& global_object) {
  104. return JS::call(global_object, function.value(), JS::js_undefined());
  105. },
  106. 0, "");
  107. }
  108. // 3. Set crossOriginDesc to PropertyDescriptor{ [[Value]]: value, [[Enumerable]]: false, [[Writable]]: false, [[Configurable]]: true }.
  109. cross_origin_descriptor = JS::PropertyDescriptor { .value = value, .writable = false, .enumerable = false, .configurable = true };
  110. }
  111. // 5. Otherwise:
  112. else {
  113. // 1. Let crossOriginGet be undefined.
  114. Optional<JS::FunctionObject*> cross_origin_get;
  115. // 2. If e.[[NeedsGet]] is true, then set crossOriginGet to an anonymous built-in function, created in the current Realm Record, that performs the same steps as the getter of the IDL attribute P on object O.
  116. if (*entry.needs_get) {
  117. cross_origin_get = JS::NativeFunction::create(
  118. HTML::current_global_object(), [object_ptr, getter = JS::make_handle(*original_descriptor->get)](auto&, auto& global_object) {
  119. return JS::call(global_object, getter.cell(), object_ptr);
  120. },
  121. 0, "");
  122. }
  123. // 3. Let crossOriginSet be undefined.
  124. Optional<JS::FunctionObject*> cross_origin_set;
  125. // If e.[[NeedsSet]] is true, then set crossOriginSet to an anonymous built-in function, created in the current Realm Record, that performs the same steps as the setter of the IDL attribute P on object O.
  126. if (*entry.needs_set) {
  127. cross_origin_set = JS::NativeFunction::create(
  128. HTML::current_global_object(), [object_ptr, setter = JS::make_handle(*original_descriptor->set)](auto&, auto& global_object) {
  129. return JS::call(global_object, setter.cell(), object_ptr);
  130. },
  131. 0, "");
  132. }
  133. // 5. Set crossOriginDesc to PropertyDescriptor{ [[Get]]: crossOriginGet, [[Set]]: crossOriginSet, [[Enumerable]]: false, [[Configurable]]: true }.
  134. cross_origin_descriptor = JS::PropertyDescriptor { .get = cross_origin_get, .set = cross_origin_set, .enumerable = false, .configurable = true };
  135. }
  136. // 6. Create an entry in the value of the [[CrossOriginPropertyDescriptorMap]] internal slot of O with key crossOriginKey and value crossOriginDesc.
  137. cross_origin_property_descriptor_map.set(cross_origin_key, cross_origin_descriptor);
  138. // 7. Return crossOriginDesc.
  139. return cross_origin_descriptor;
  140. }
  141. // 3. Return undefined.
  142. return {};
  143. }
  144. }