mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 09:17:35 +00:00
LibWeb: Insert WindowProperties object into Window's prototype chain
And implement WindowProperties, the "named properties object" for Window according to the spec. This involves moving an AO out of LegacyPlatformObject and into a common place that the WindowProperties class can access. This doesn't implement the AOs on Window that actually name lookup for the unenumerable named properties on the window yet, just the scaffolding.
This commit is contained in:
parent
64899dba44
commit
247f12d7b0
12 changed files with 313 additions and 57 deletions
|
@ -18,52 +18,6 @@ LegacyPlatformObject::LegacyPlatformObject(JS::Realm& realm)
|
|||
|
||||
LegacyPlatformObject::~LegacyPlatformObject() = default;
|
||||
|
||||
// https://webidl.spec.whatwg.org/#dfn-named-property-visibility
|
||||
JS::ThrowCompletionOr<bool> LegacyPlatformObject::is_named_property_exposed_on_object(JS::PropertyKey const& property_key) const
|
||||
{
|
||||
// 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.
|
||||
Vector<DeprecatedString> 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;
|
||||
|
||||
// 3. If O implements an interface that has the [LegacyOverrideBuiltIns] extended attribute, then return true.
|
||||
if (has_legacy_override_built_ins_interface_extended_attribute())
|
||||
return true;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// https://webidl.spec.whatwg.org/#LegacyPlatformObjectGetOwnProperty
|
||||
JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> LegacyPlatformObject::legacy_platform_object_get_own_property(JS::PropertyKey const& property_name, IgnoreNamedProps ignore_named_props) const
|
||||
{
|
||||
|
@ -106,7 +60,7 @@ JS::ThrowCompletionOr<Optional<JS::PropertyDescriptor>> LegacyPlatformObject::le
|
|||
// 2. If O supports named properties and ignoreNamedProps is false, then:
|
||||
if (supports_named_properties() && ignore_named_props == IgnoreNamedProps::No) {
|
||||
// 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))) {
|
||||
if (TRY(WebIDL::is_named_property_exposed_on_object({ this }, 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();
|
||||
|
||||
|
@ -322,7 +276,7 @@ JS::ThrowCompletionOr<bool> LegacyPlatformObject::internal_delete(JS::PropertyKe
|
|||
|
||||
// 2. If O supports named properties, O does not implement an interface with the [Global] extended attribute and
|
||||
// the result of calling the named property visibility algorithm with property name P and object O is true, then:
|
||||
if (supports_named_properties() && !has_global_interface_extended_attribute() && TRY(is_named_property_exposed_on_object(property_name))) {
|
||||
if (supports_named_properties() && !has_global_interface_extended_attribute() && TRY(WebIDL::is_named_property_exposed_on_object({ this }, property_name))) {
|
||||
// 1. If O does not implement an interface with a named property deleter, then return false.
|
||||
if (!has_named_property_deleter())
|
||||
return false;
|
||||
|
@ -394,7 +348,7 @@ JS::ThrowCompletionOr<JS::MarkedVector<JS::Value>> LegacyPlatformObject::interna
|
|||
// 3. If O supports named properties, then for each P of O’s supported property names that is visible according to the named property visibility algorithm, append P to keys.
|
||||
if (supports_named_properties()) {
|
||||
for (auto& named_property : supported_property_names()) {
|
||||
if (TRY(is_named_property_exposed_on_object(named_property)))
|
||||
if (TRY(WebIDL::is_named_property_exposed_on_object({ this }, named_property)))
|
||||
keys.append(JS::PrimitiveString::create(vm, named_property));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <LibWeb/Bindings/PlatformObject.h>
|
||||
#include <LibWeb/WebIDL/AbstractOperations.h>
|
||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||
|
||||
namespace Web::Bindings {
|
||||
|
@ -16,6 +17,8 @@ namespace Web::Bindings {
|
|||
class LegacyPlatformObject : public PlatformObject {
|
||||
WEB_PLATFORM_OBJECT(LegacyPlatformObject, PlatformObject);
|
||||
|
||||
virtual bool has_legacy_override_built_ins_interface_extended_attribute() const = 0;
|
||||
|
||||
public:
|
||||
virtual ~LegacyPlatformObject() override;
|
||||
|
||||
|
@ -26,8 +29,6 @@ public:
|
|||
virtual JS::ThrowCompletionOr<bool> internal_prevent_extensions() override;
|
||||
virtual JS::ThrowCompletionOr<JS::MarkedVector<JS::Value>> internal_own_property_keys() const override;
|
||||
|
||||
JS::ThrowCompletionOr<bool> is_named_property_exposed_on_object(JS::PropertyKey const&) const;
|
||||
|
||||
enum class IgnoreNamedProps {
|
||||
No,
|
||||
Yes,
|
||||
|
@ -80,7 +81,6 @@ protected:
|
|||
|
||||
virtual bool has_named_property_deleter() const = 0;
|
||||
|
||||
virtual bool has_legacy_override_built_ins_interface_extended_attribute() const = 0;
|
||||
virtual bool has_legacy_unenumerable_named_properties_interface_extended_attribute() const = 0;
|
||||
virtual bool has_global_interface_extended_attribute() const = 0;
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ enum class StateAndProperties;
|
|||
|
||||
namespace Web::Bindings {
|
||||
class Intrinsics;
|
||||
class LegacyPlatformObject;
|
||||
class OptionConstructor;
|
||||
|
||||
enum class AudioContextLatencyCategory;
|
||||
|
|
|
@ -1437,4 +1437,16 @@ OrderedHashMap<String, JS::NonnullGCPtr<Navigable>> Window::document_tree_child_
|
|||
return names;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/#named-access-on-the-window-object
|
||||
Vector<DeprecatedString> Window::supported_property_names()
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/#named-access-on-the-window-object
|
||||
WebIDL::ExceptionOr<JS::Value> Window::named_item_value(DeprecatedFlyString const& name)
|
||||
{
|
||||
return JS::js_undefined();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -193,6 +193,9 @@ public:
|
|||
|
||||
[[nodiscard]] OrderedHashMap<String, JS::NonnullGCPtr<Navigable>> document_tree_child_navigable_target_name_property_set();
|
||||
|
||||
[[nodiscard]] Vector<DeprecatedString> supported_property_names();
|
||||
[[nodiscard]] WebIDL::ExceptionOr<JS::Value> named_item_value(DeprecatedFlyString const&);
|
||||
|
||||
private:
|
||||
explicit Window(JS::Realm&);
|
||||
|
||||
|
|
|
@ -37,6 +37,11 @@ interface Window : EventTarget {
|
|||
readonly attribute Element? frameElement;
|
||||
WindowProxy? open(optional USVString url = "", optional DOMString target = "_blank", optional [LegacyNullToEmptyString] DOMString features = "");
|
||||
|
||||
// Since this is the global object, the IDL named getter adds a NamedPropertiesObject exotic
|
||||
// object on the prototype chain. Indeed, this does not make the global object an exotic object.
|
||||
// Indexed access is taken care of by the WindowProxy exotic object.
|
||||
getter object (DOMString name);
|
||||
|
||||
// the user agent
|
||||
readonly attribute Navigator navigator;
|
||||
[ImplementedAs=navigator] readonly attribute Navigator clientInformation; // legacy alias of .navigator
|
||||
|
|
|
@ -12,6 +12,9 @@
|
|||
#include <LibJS/Runtime/DataView.h>
|
||||
#include <LibJS/Runtime/PropertyKey.h>
|
||||
#include <LibJS/Runtime/TypedArray.h>
|
||||
#include <LibWeb/Bindings/LegacyPlatformObject.h>
|
||||
#include <LibWeb/Bindings/WindowPrototype.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
#include <LibWeb/WebIDL/AbstractOperations.h>
|
||||
#include <LibWeb/WebIDL/Promise.h>
|
||||
|
||||
|
@ -301,4 +304,54 @@ JS::Completion construct(WebIDL::CallbackType& callback, JS::MarkedVector<JS::Va
|
|||
return completion;
|
||||
}
|
||||
|
||||
// https://webidl.spec.whatwg.org/#dfn-named-property-visibility
|
||||
JS::ThrowCompletionOr<bool> is_named_property_exposed_on_object(Variant<Bindings::LegacyPlatformObject const*, HTML::Window*> const& variant, JS::PropertyKey const& property_key)
|
||||
{
|
||||
auto const& object = *variant.visit([](auto& o) { return static_cast<JS::Object const*>(o); });
|
||||
|
||||
// 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.
|
||||
Vector<DeprecatedString> supported_property_names = variant.visit([](auto* o) { return o->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.JS::Object::internal_get_own_property(property_key));
|
||||
|
||||
if (own_property_named_p.has_value())
|
||||
return false;
|
||||
|
||||
// 3. If O implements an interface that has the [LegacyOverrideBuiltIns] extended attribute, then return true.
|
||||
if (variant.has<Bindings::LegacyPlatformObject const*>() && variant.get<Bindings::LegacyPlatformObject const*>()->has_legacy_override_built_ins_interface_extended_attribute())
|
||||
return true;
|
||||
|
||||
// 4. Let prototype be O.[[GetPrototypeOf]]().
|
||||
auto* prototype = TRY(object.internal_get_prototype_of());
|
||||
|
||||
// 5. While prototype is not null:
|
||||
while (prototype) {
|
||||
// 1. If prototype is not a named properties object, and prototype has an own property named P, then return false.
|
||||
// FIXME: Are there other named properties objects?
|
||||
if (!is<Bindings::WindowProperties>(prototype)) {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -61,4 +61,6 @@ JS::Completion construct(WebIDL::CallbackType& callback, Args&&... args)
|
|||
return construct(callback, move(arguments_list));
|
||||
}
|
||||
|
||||
JS::ThrowCompletionOr<bool> is_named_property_exposed_on_object(Variant<Bindings::LegacyPlatformObject const*, HTML::Window*> const& variant, JS::PropertyKey const& property_key);
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue