123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- /*
- * Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #include <LibJS/Runtime/GlobalObject.h>
- #include <LibWeb/Bindings/ExceptionOrUtils.h>
- #include <LibWeb/Bindings/LegacyPlatformObject.h>
- namespace Web::Bindings {
- LegacyPlatformObject::LegacyPlatformObject(JS::Object& prototype)
- : PlatformObject(prototype)
- {
- }
- LegacyPlatformObject::~LegacyPlatformObject() = default;
- JS::ThrowCompletionOr<bool> LegacyPlatformObject::is_named_property_exposed_on_object(JS::PropertyKey const& property_key) const
- {
- [[maybe_unused]] auto& vm = this->vm();
- // The spec doesn't say anything about the type of the property name here.
- // Numbers can be converted to a string, which is fine and what other engines do.
- // However, since a symbol cannot be converted to a string, it cannot be a supported property name. Return early if it's a symbol.
- if (property_key.is_symbol())
- return false;
- // 1. If P is not a supported property name of O, then return false.
- // NOTE: This is in it's own variable to enforce the type.
- // FIXME: Can this throw?
- Vector<String> supported_property_names = this->supported_property_names();
- auto property_key_string = property_key.to_string();
- if (!supported_property_names.contains_slow(property_key_string))
- return false;
- // 2. If O has an own property named P, then return false.
- // NOTE: This has to be done manually instead of using Object::has_own_property, as that would use the overridden internal_get_own_property.
- auto own_property_named_p = MUST(Object::internal_get_own_property(property_key));
- if (own_property_named_p.has_value())
- return false;
- // NOTE: Step 3 is not here as the interface doesn't have the LegacyOverrideBuiltIns extended attribute.
- // 4. Let prototype be O.[[GetPrototypeOf]]().
- auto* prototype = TRY(internal_get_prototype_of());
- // 5. While prototype is not null:
- while (prototype) {
- // FIXME: 1. If prototype is not a named properties object, and prototype has an own property named P, then return false.
- // (It currently does not check for named property objects)
- bool prototype_has_own_property_named_p = TRY(prototype->has_own_property(property_key));
- if (prototype_has_own_property_named_p)
- return false;
- // 2. Set prototype to prototype.[[GetPrototypeOf]]().
- prototype = TRY(prototype->internal_get_prototype_of());
- }
- // 6. Return true.
- return true;
- }
- JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> LegacyPlatformObject::legacy_platform_object_get_own_property_for_get_own_property_slot(JS::PropertyKey const& property_name) const
- {
- auto& vm = this->vm();
- if (property_name.is_number()) {
- // 1. Let index be the result of calling ToUint32(P).
- u32 index = property_name.as_number();
- // 2. If index is a supported property index, then:
- // FIXME: Can this throw?
- if (is_supported_property_index(index)) {
- auto value = TRY(throw_dom_exception_if_needed(vm, [&] { return item_value(index); }));
- // 5. Let desc be a newly created Property Descriptor with no fields.
- JS::PropertyDescriptor descriptor;
- // 6. Set desc.[[Value]] to the result of converting value to an ECMAScript value.
- descriptor.value = value;
- descriptor.writable = false;
- // 8. Set desc.[[Enumerable]] and desc.[[Configurable]] to true.
- descriptor.enumerable = true;
- descriptor.configurable = true;
- // 9. Return desc.
- return descriptor;
- }
- // 3. Set ignoreNamedProps to true.
- return TRY(Object::internal_get_own_property(property_name));
- }
- // 1. If the result of running the named property visibility algorithm with property name P and object O is true, then:
- if (TRY(is_named_property_exposed_on_object(property_name))) {
- // FIXME: It's unfortunate that this is done twice, once in is_named_property_exposed_on_object and here.
- auto property_name_string = property_name.to_string();
- auto value = TRY(throw_dom_exception_if_needed(vm, [&] { return named_item_value(property_name_string); }));
- // 5. Let desc be a newly created Property Descriptor with no fields.
- JS::PropertyDescriptor descriptor;
- // 6. Set desc.[[Value]] to the result of converting value to an ECMAScript value.
- descriptor.value = value;
- descriptor.writable = false;
- descriptor.enumerable = false;
- // 9. Set desc.[[Configurable]] to true.
- descriptor.configurable = true;
- // 10. Return desc.
- return descriptor;
- }
- return TRY(Object::internal_get_own_property(property_name));
- }
- JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> LegacyPlatformObject::legacy_platform_object_get_own_property_for_set_slot(JS::PropertyKey const& property_name) const
- {
- if (!property_name.is_number())
- return TRY(Object::internal_get_own_property(property_name));
- // 1. Let index be the result of calling ToUint32(P).
- u32 index = property_name.as_number();
- // 2. If index is a supported property index, then:
- // FIXME: Can this throw?
- if (is_supported_property_index(index)) {
- auto value = TRY(throw_dom_exception_if_needed(vm(), [&] { return item_value(index); }));
- // 5. Let desc be a newly created Property Descriptor with no fields.
- JS::PropertyDescriptor descriptor;
- // 6. Set desc.[[Value]] to the result of converting value to an ECMAScript value.
- descriptor.value = value;
- descriptor.writable = false;
- // 8. Set desc.[[Enumerable]] and desc.[[Configurable]] to true.
- descriptor.enumerable = true;
- descriptor.configurable = true;
- // 9. Return desc.
- return descriptor;
- }
- // 3. Set ignoreNamedProps to true.
- return TRY(Object::internal_get_own_property(property_name));
- }
- JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> LegacyPlatformObject::internal_get_own_property(JS::PropertyKey const& property_name) const
- {
- // 1. Return LegacyPlatformObjectGetOwnProperty(O, P, false).
- return TRY(legacy_platform_object_get_own_property_for_get_own_property_slot(property_name));
- }
- JS::ThrowCompletionOr<bool> LegacyPlatformObject::internal_set(JS::PropertyKey const& property_name, JS::Value value, JS::Value receiver)
- {
- [[maybe_unused]] auto& global_object = this->global_object();
- // 2. Let ownDesc be LegacyPlatformObjectGetOwnProperty(O, P, true).
- auto own_descriptor = TRY(legacy_platform_object_get_own_property_for_set_slot(property_name));
- // 3. Perform ? OrdinarySetWithOwnDescriptor(O, P, V, Receiver, ownDesc).
- // NOTE: The spec says "perform" instead of "return", meaning nothing will be returned on this path according to the spec, which isn't possible to do.
- // Let's treat it as though it says "return" instead of "perform".
- return ordinary_set_with_own_descriptor(property_name, value, receiver, own_descriptor);
- }
- JS::ThrowCompletionOr<bool> LegacyPlatformObject::internal_define_own_property(JS::PropertyKey const& property_name, JS::PropertyDescriptor const& property_descriptor)
- {
- [[maybe_unused]] auto& vm = this->vm();
- [[maybe_unused]] auto& global_object = this->global_object();
- if (property_name.is_number()) {
- // 1. If the result of calling IsDataDescriptor(Desc) is false, then return false.
- if (!property_descriptor.is_data_descriptor())
- return false;
- return false;
- }
- if (property_name.is_string()) {
- auto& property_name_as_string = property_name.as_string();
- // 1. Let creating be true if P is not a supported property name, and false otherwise.
- // NOTE: This is in it's own variable to enforce the type.
- // FIXME: Can this throw?
- Vector<String> supported_property_names = this->supported_property_names();
- [[maybe_unused]] bool creating = !supported_property_names.contains_slow(property_name_as_string);
- // NOTE: This has to be done manually instead of using Object::has_own_property, as that would use the overridden internal_get_own_property.
- auto own_property_named_p = TRY(Object::internal_get_own_property(property_name));
- if (!own_property_named_p.has_value()) {
- if (!creating)
- return false;
- }
- }
- // property_descriptor is a const&, thus we need to create a copy here to set [[Configurable]]
- JS::PropertyDescriptor descriptor_copy(property_descriptor);
- descriptor_copy.configurable = true;
- // 4. Return OrdinaryDefineOwnProperty(O, P, Desc).
- return Object::internal_define_own_property(property_name, descriptor_copy);
- }
- JS::ThrowCompletionOr<bool> LegacyPlatformObject::internal_delete(JS::PropertyKey const& property_name)
- {
- [[maybe_unused]] auto& global_object = this->global_object();
- if (property_name.is_number()) {
- // 1. Let index be the result of calling ToUint32(P).
- u32 index = property_name.as_number();
- // 2. If index is not a supported property index, then return true.
- // FIXME: Can this throw?
- if (!is_supported_property_index(index))
- return true;
- // 3. Return false.
- return false;
- }
- if (TRY(is_named_property_exposed_on_object(property_name))) {
- return false;
- }
- // 3. If O has an own property with name P, then:
- auto own_property_named_p_descriptor = TRY(Object::internal_get_own_property(property_name));
- if (own_property_named_p_descriptor.has_value()) {
- // 1. If the property is not configurable, then return false.
- // 2. Otherwise, remove the property from O.
- if (*own_property_named_p_descriptor->configurable)
- storage_delete(property_name);
- else
- return false;
- }
- // 4. Return true.
- return true;
- }
- JS::ThrowCompletionOr<bool> LegacyPlatformObject::internal_prevent_extensions()
- {
- // 1. Return false.
- return false;
- }
- JS::ThrowCompletionOr<JS::MarkedVector<JS::Value>> LegacyPlatformObject::internal_own_property_keys() const
- {
- auto& vm = this->vm();
- // 1. Let keys be a new empty list of ECMAScript String and Symbol values.
- JS::MarkedVector<JS::Value> keys { heap() };
- for (u64 index = 0; index <= NumericLimits<u32>::max(); ++index) {
- if (is_supported_property_index(index))
- keys.append(js_string(vm, String::number(index)));
- else
- break;
- }
- for (auto& named_property : supported_property_names()) {
- if (TRY(is_named_property_exposed_on_object(named_property)))
- keys.append(js_string(vm, named_property));
- }
- // 4. For each P of O’s own property keys that is a String, in ascending chronological order of property creation, append P to keys.
- for (auto& it : shape().property_table_ordered()) {
- if (it.key.is_string())
- keys.append(it.key.to_value(vm));
- }
- // 5. For each P of O’s own property keys that is a Symbol, in ascending chronological order of property creation, append P to keys.
- for (auto& it : shape().property_table_ordered()) {
- if (it.key.is_symbol())
- keys.append(it.key.to_value(vm));
- }
- // FIXME: 6. Assert: keys has no duplicate items.
- // 7. Return keys.
- return { move(keys) };
- }
- JS::Value LegacyPlatformObject::item_value(size_t) const
- {
- return JS::js_undefined();
- }
- JS::Value LegacyPlatformObject::named_item_value(FlyString const&) const
- {
- return JS::js_undefined();
- }
- Vector<String> LegacyPlatformObject::supported_property_names() const
- {
- return {};
- }
- bool LegacyPlatformObject::is_supported_property_index(u32) const
- {
- return false;
- }
- }
|