mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-29 12:49:05 +00:00
LibWeb: Implement element-referencing ARIA attributes
There are ARIA attributes, e.g. ariaControlsElements, which refer to a list of elements by their ID. For example: <div aria-controls="item1 item2"> The div.ariaControlsElements attribute would be a list of elements whose ID matches the values in the aria-controls attribute.
This commit is contained in:
parent
0289df9357
commit
f985ac8884
Notes:
github-actions[bot]
2025-04-25 00:21:23 +00:00
Author: https://github.com/trflynn89
Commit: f985ac8884
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4460
Reviewed-by: https://github.com/tcl3 ✅
9 changed files with 3354 additions and 28 deletions
|
@ -230,4 +230,17 @@ Vector<String> ARIAMixin::parse_id_reference_list(Optional<String> const& id_lis
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define __ENUMERATE_ARIA_ATTRIBUTE(attribute, referencing_attribute) \
|
||||||
|
Optional<Vector<WeakPtr<DOM::Element>>> const& ARIAMixin::attribute() const \
|
||||||
|
{ \
|
||||||
|
return m_##attribute; \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
void ARIAMixin::set_##attribute(Optional<Vector<WeakPtr<DOM::Element>>> value) \
|
||||||
|
{ \
|
||||||
|
m_##attribute = move(value); \
|
||||||
|
}
|
||||||
|
ENUMERATE_ARIA_ELEMENT_LIST_REFERENCING_ATTRIBUTES
|
||||||
|
#undef __ENUMERATE_ARIA_ATTRIBUTE
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,15 @@
|
||||||
|
|
||||||
namespace Web::ARIA {
|
namespace Web::ARIA {
|
||||||
|
|
||||||
|
#define ENUMERATE_ARIA_ELEMENT_LIST_REFERENCING_ATTRIBUTES \
|
||||||
|
__ENUMERATE_ARIA_ATTRIBUTE(aria_controls_elements, aria_controls) \
|
||||||
|
__ENUMERATE_ARIA_ATTRIBUTE(aria_described_by_elements, aria_described_by) \
|
||||||
|
__ENUMERATE_ARIA_ATTRIBUTE(aria_details_elements, aria_details) \
|
||||||
|
__ENUMERATE_ARIA_ATTRIBUTE(aria_error_message_elements, aria_error_message) \
|
||||||
|
__ENUMERATE_ARIA_ATTRIBUTE(aria_flow_to_elements, aria_flow_to) \
|
||||||
|
__ENUMERATE_ARIA_ATTRIBUTE(aria_labelled_by_elements, aria_labelled_by) \
|
||||||
|
__ENUMERATE_ARIA_ATTRIBUTE(aria_owns_elements, aria_owns)
|
||||||
|
|
||||||
class ARIAMixin {
|
class ARIAMixin {
|
||||||
public:
|
public:
|
||||||
virtual ~ARIAMixin();
|
virtual ~ARIAMixin();
|
||||||
|
@ -51,6 +60,12 @@ public:
|
||||||
GC::Ptr<DOM::Element> aria_active_descendant_element() { return m_aria_active_descendant_element; }
|
GC::Ptr<DOM::Element> aria_active_descendant_element() { return m_aria_active_descendant_element; }
|
||||||
void set_aria_active_descendant_element(GC::Ptr<DOM::Element> value) { m_aria_active_descendant_element = value; }
|
void set_aria_active_descendant_element(GC::Ptr<DOM::Element> value) { m_aria_active_descendant_element = value; }
|
||||||
|
|
||||||
|
#define __ENUMERATE_ARIA_ATTRIBUTE(attribute, referencing_attribute) \
|
||||||
|
Optional<Vector<WeakPtr<DOM::Element>>> const& attribute() const; \
|
||||||
|
void set_##attribute(Optional<Vector<WeakPtr<DOM::Element>>> value);
|
||||||
|
ENUMERATE_ARIA_ELEMENT_LIST_REFERENCING_ATTRIBUTES
|
||||||
|
#undef __ENUMERATE_ARIA_ATTRIBUTE
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
ARIAMixin();
|
ARIAMixin();
|
||||||
|
|
||||||
|
@ -60,6 +75,11 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
GC::Ptr<DOM::Element> m_aria_active_descendant_element;
|
GC::Ptr<DOM::Element> m_aria_active_descendant_element;
|
||||||
|
|
||||||
|
#define __ENUMERATE_ARIA_ATTRIBUTE(attribute, referencing_attribute) \
|
||||||
|
Optional<Vector<WeakPtr<DOM::Element>>> m_##attribute;
|
||||||
|
ENUMERATE_ARIA_ELEMENT_LIST_REFERENCING_ATTRIBUTES
|
||||||
|
#undef __ENUMERATE_ARIA_ATTRIBUTE
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,21 +12,28 @@ interface mixin ARIAMixin {
|
||||||
[CEReactions] attribute DOMString? ariaColIndex;
|
[CEReactions] attribute DOMString? ariaColIndex;
|
||||||
[CEReactions] attribute DOMString? ariaColIndexText;
|
[CEReactions] attribute DOMString? ariaColIndexText;
|
||||||
[CEReactions] attribute DOMString? ariaColSpan;
|
[CEReactions] attribute DOMString? ariaColSpan;
|
||||||
|
[Reflect=aria-controls, CEReactions] attribute sequence<Element>? ariaControlsElements; // FIXME: Should `FrozenArray<Element>?`
|
||||||
[CEReactions] attribute DOMString? ariaCurrent;
|
[CEReactions] attribute DOMString? ariaCurrent;
|
||||||
|
[Reflect=aria-describedby, CEReactions] attribute sequence<Element>? ariaDescribedByElements; // FIXME: Should `FrozenArray<Element>?`
|
||||||
[CEReactions] attribute DOMString? ariaDescription;
|
[CEReactions] attribute DOMString? ariaDescription;
|
||||||
|
[Reflect=aria-details, CEReactions] attribute sequence<Element>? ariaDetailsElements; // FIXME: Should `FrozenArray<Element>?`
|
||||||
[CEReactions] attribute DOMString? ariaDisabled;
|
[CEReactions] attribute DOMString? ariaDisabled;
|
||||||
|
[Reflect=aria-errormessage, CEReactions] attribute sequence<Element>? ariaErrorMessageElements; // FIXME: Should `FrozenArray<Element>?`
|
||||||
[CEReactions] attribute DOMString? ariaExpanded;
|
[CEReactions] attribute DOMString? ariaExpanded;
|
||||||
|
[Reflect=aria-flowto, CEReactions] attribute sequence<Element>? ariaFlowToElements; // FIXME: Should `FrozenArray<Element>?`
|
||||||
[CEReactions] attribute DOMString? ariaHasPopup;
|
[CEReactions] attribute DOMString? ariaHasPopup;
|
||||||
[CEReactions] attribute DOMString? ariaHidden;
|
[CEReactions] attribute DOMString? ariaHidden;
|
||||||
[CEReactions] attribute DOMString? ariaInvalid;
|
[CEReactions] attribute DOMString? ariaInvalid;
|
||||||
[CEReactions] attribute DOMString? ariaKeyShortcuts;
|
[CEReactions] attribute DOMString? ariaKeyShortcuts;
|
||||||
[CEReactions] attribute DOMString? ariaLabel;
|
[CEReactions] attribute DOMString? ariaLabel;
|
||||||
|
[Reflect=aria-labelledby, CEReactions] attribute sequence<Element>? ariaLabelledByElements; // FIXME: Should `FrozenArray<Element>?`
|
||||||
[CEReactions] attribute DOMString? ariaLevel;
|
[CEReactions] attribute DOMString? ariaLevel;
|
||||||
[CEReactions] attribute DOMString? ariaLive;
|
[CEReactions] attribute DOMString? ariaLive;
|
||||||
[CEReactions] attribute DOMString? ariaModal;
|
[CEReactions] attribute DOMString? ariaModal;
|
||||||
[CEReactions] attribute DOMString? ariaMultiLine;
|
[CEReactions] attribute DOMString? ariaMultiLine;
|
||||||
[CEReactions] attribute DOMString? ariaMultiSelectable;
|
[CEReactions] attribute DOMString? ariaMultiSelectable;
|
||||||
[CEReactions] attribute DOMString? ariaOrientation;
|
[CEReactions] attribute DOMString? ariaOrientation;
|
||||||
|
[Reflect=aria-owns, CEReactions] attribute sequence<Element>? ariaOwnsElements; // FIXME: Should `FrozenArray<Element>?`
|
||||||
[CEReactions] attribute DOMString? ariaPlaceholder;
|
[CEReactions] attribute DOMString? ariaPlaceholder;
|
||||||
[CEReactions] attribute DOMString? ariaPosInSet;
|
[CEReactions] attribute DOMString? ariaPosInSet;
|
||||||
[CEReactions] attribute DOMString? ariaPressed;
|
[CEReactions] attribute DOMString? ariaPressed;
|
||||||
|
|
|
@ -430,6 +430,60 @@ Vector<String> Element::get_attribute_names() const
|
||||||
return names;
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#attr-associated-elements
|
||||||
|
Optional<GC::RootVector<GC::Ref<DOM::Element>>> Element::get_the_attribute_associated_elements(FlyString const& content_attribute, Optional<Vector<WeakPtr<DOM::Element>>> const& explicitly_set_attribute_elements) const
|
||||||
|
{
|
||||||
|
// 1. Let elements be an empty list.
|
||||||
|
GC::RootVector<GC::Ref<DOM::Element>> elements(heap());
|
||||||
|
|
||||||
|
// 2. Let element be the result of running reflectedTarget's get the element.
|
||||||
|
auto const& element = *this;
|
||||||
|
|
||||||
|
// 3. If reflectedTarget's explicitly set attr-elements is not null:
|
||||||
|
if (explicitly_set_attribute_elements.has_value()) {
|
||||||
|
// 1. For each attrElement in reflectedTarget's explicitly set attr-elements:
|
||||||
|
for (auto const& attribute_element : *explicitly_set_attribute_elements) {
|
||||||
|
// 1. If attrElement is not a descendant of any of element's shadow-including ancestors, then continue.
|
||||||
|
if (!attribute_element || &attribute_element->root() != &element.shadow_including_root())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// 2. Append attrElement to elements.
|
||||||
|
elements.append(*attribute_element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 4. Otherwise:
|
||||||
|
else {
|
||||||
|
// 1. Let contentAttributeValue be the result of running reflectedTarget's get the content attribute.
|
||||||
|
auto content_attribute_value = element.get_attribute(content_attribute);
|
||||||
|
|
||||||
|
// 2. If contentAttributeValue is null, then return null.
|
||||||
|
if (!content_attribute_value.has_value())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// 3. Let tokens be contentAttributeValue, split on ASCII whitespace.
|
||||||
|
auto tokens = content_attribute_value->bytes_as_string_view().split_view_if(Infra::is_ascii_whitespace);
|
||||||
|
|
||||||
|
// 4. For each id of tokens:
|
||||||
|
for (auto id : tokens) {
|
||||||
|
// 1. Let candidate be the first element, in tree order, that meets the following criteria:
|
||||||
|
// * candidate's root is the same as element's root;
|
||||||
|
// * candidate's ID is id; and
|
||||||
|
// * candidate implements T.
|
||||||
|
auto candidate = element.document().get_element_by_id(MUST(FlyString::from_utf8(id)));
|
||||||
|
|
||||||
|
// 2. If no such element exists, then continue.
|
||||||
|
if (!candidate)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// 3. Append candidate to elements.
|
||||||
|
elements.append(*candidate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Return elements.
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
GC::Ptr<Layout::Node> Element::create_layout_node(GC::Ref<CSS::ComputedProperties> style)
|
GC::Ptr<Layout::Node> Element::create_layout_node(GC::Ref<CSS::ComputedProperties> style)
|
||||||
{
|
{
|
||||||
if (local_name() == "noscript" && document().is_scripting_enabled())
|
if (local_name() == "noscript" && document().is_scripting_enabled())
|
||||||
|
@ -3609,6 +3663,17 @@ void Element::attribute_changed(FlyString const& local_name, Optional<String> co
|
||||||
// Set element's explicitly set attr-element to null.
|
// Set element's explicitly set attr-element to null.
|
||||||
set_aria_active_descendant_element({});
|
set_aria_active_descendant_element({});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/common-dom-interfaces.html#reflecting-content-attributes-in-idl-attributes:concept-element-attributes-change-ext-2
|
||||||
|
// 1. If localName is not attr or namespace is not null, then return.
|
||||||
|
// 2. Set element's explicitly set attr-elements to null.
|
||||||
|
#define __ENUMERATE_ARIA_ATTRIBUTE(attribute, referencing_attribute) \
|
||||||
|
else if (local_name == ARIA::AttributeNames::referencing_attribute && !namespace_.has_value()) \
|
||||||
|
{ \
|
||||||
|
set_##attribute({}); \
|
||||||
|
}
|
||||||
|
ENUMERATE_ARIA_ELEMENT_LIST_REFERENCING_ATTRIBUTES
|
||||||
|
#undef __ENUMERATE_ARIA_ATTRIBUTE
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Element::ensure_custom_element_reaction_queue() -> CustomElementReactionQueue&
|
auto Element::ensure_custom_element_reaction_queue() -> CustomElementReactionQueue&
|
||||||
|
|
|
@ -163,6 +163,8 @@ public:
|
||||||
GC::Ptr<Attr> get_attribute_node(FlyString const& name) const;
|
GC::Ptr<Attr> get_attribute_node(FlyString const& name) const;
|
||||||
GC::Ptr<Attr> get_attribute_node_ns(Optional<FlyString> const& namespace_, FlyString const& name) const;
|
GC::Ptr<Attr> get_attribute_node_ns(Optional<FlyString> const& namespace_, FlyString const& name) const;
|
||||||
|
|
||||||
|
Optional<GC::RootVector<GC::Ref<DOM::Element>>> get_the_attribute_associated_elements(FlyString const& content_attribute, Optional<Vector<WeakPtr<DOM::Element>>> const& explicitly_set_attribute_elements) const;
|
||||||
|
|
||||||
DOMTokenList* class_list();
|
DOMTokenList* class_list();
|
||||||
|
|
||||||
WebIDL::ExceptionOr<GC::Ref<ShadowRoot>> attach_shadow(ShadowRootInit init);
|
WebIDL::ExceptionOr<GC::Ref<ShadowRoot>> attach_shadow(ShadowRootInit init);
|
||||||
|
|
|
@ -172,6 +172,18 @@ static StringView sequence_storage_type_to_cpp_storage_type_name(SequenceStorage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_nullable_sequence_of_single_type(Type const& type, StringView type_name)
|
||||||
|
{
|
||||||
|
if (!type.is_nullable() || !type.is_sequence())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto const& parameters = type.as_parameterized().parameters();
|
||||||
|
if (parameters.size() != 1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return parameters.first()->name() == type_name;
|
||||||
|
}
|
||||||
|
|
||||||
CppType idl_type_name_to_cpp_type(Type const& type, Interface const& interface);
|
CppType idl_type_name_to_cpp_type(Type const& type, Interface const& interface);
|
||||||
|
|
||||||
static ByteString union_type_to_variant(UnionType const& union_type, Interface const& interface)
|
static ByteString union_type_to_variant(UnionType const& union_type, Interface const& interface)
|
||||||
|
@ -1024,21 +1036,15 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
|
||||||
// 3. If method is undefined, throw a TypeError.
|
// 3. If method is undefined, throw a TypeError.
|
||||||
// 4. Return the result of creating a sequence from V and method.
|
// 4. Return the result of creating a sequence from V and method.
|
||||||
|
|
||||||
if (optional) {
|
if (optional || parameter.type->is_nullable()) {
|
||||||
auto sequence_cpp_type = idl_type_name_to_cpp_type(parameterized_type.parameters().first(), interface);
|
auto sequence_cpp_type = idl_type_name_to_cpp_type(parameterized_type.parameters().first(), interface);
|
||||||
sequence_generator.set("sequence.type", sequence_cpp_type.name);
|
sequence_generator.set("sequence.type", sequence_cpp_type.name);
|
||||||
sequence_generator.set("sequence.storage_type", sequence_storage_type_to_cpp_storage_type_name(sequence_cpp_type.sequence_storage_type));
|
sequence_generator.set("sequence.storage_type", sequence_storage_type_to_cpp_storage_type_name(sequence_cpp_type.sequence_storage_type));
|
||||||
|
|
||||||
if (!optional_default_value.has_value()) {
|
if (!optional_default_value.has_value()) {
|
||||||
if (sequence_cpp_type.sequence_storage_type == IDL::SequenceStorageType::Vector) {
|
sequence_generator.append(R"~~~(
|
||||||
sequence_generator.append(R"~~~(
|
|
||||||
Optional<@sequence.storage_type@<@sequence.type@>> @cpp_name@;
|
Optional<@sequence.storage_type@<@sequence.type@>> @cpp_name@;
|
||||||
)~~~");
|
)~~~");
|
||||||
} else {
|
|
||||||
sequence_generator.append(R"~~~(
|
|
||||||
Optional<@sequence.storage_type@> @cpp_name@;
|
|
||||||
)~~~");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (optional_default_value != "[]")
|
if (optional_default_value != "[]")
|
||||||
TODO();
|
TODO();
|
||||||
|
@ -1054,9 +1060,15 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sequence_generator.append(R"~~~(
|
if (optional) {
|
||||||
|
sequence_generator.append(R"~~~(
|
||||||
if (!@js_name@@js_suffix@.is_undefined()) {
|
if (!@js_name@@js_suffix@.is_undefined()) {
|
||||||
)~~~");
|
)~~~");
|
||||||
|
} else {
|
||||||
|
sequence_generator.append(R"~~~(
|
||||||
|
if (!@js_name@@js_suffix@.is_nullish()) {
|
||||||
|
)~~~");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sequence_generator.append(R"~~~(
|
sequence_generator.append(R"~~~(
|
||||||
|
@ -1068,9 +1080,9 @@ static void generate_to_cpp(SourceGenerator& generator, ParameterType& parameter
|
||||||
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotIterable, @js_name@@js_suffix@.to_string_without_side_effects());
|
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotIterable, @js_name@@js_suffix@.to_string_without_side_effects());
|
||||||
)~~~");
|
)~~~");
|
||||||
|
|
||||||
parameterized_type.generate_sequence_from_iterable(sequence_generator, ByteString::formatted("{}{}", acceptable_cpp_name, optional ? "_non_optional" : ""), ByteString::formatted("{}{}", js_name, js_suffix), ByteString::formatted("{}{}_iterator_method{}", js_name, js_suffix, recursion_depth), interface, recursion_depth + 1);
|
parameterized_type.generate_sequence_from_iterable(sequence_generator, ByteString::formatted("{}{}", acceptable_cpp_name, optional || parameter.type->is_nullable() ? "_non_optional" : ""), ByteString::formatted("{}{}", js_name, js_suffix), ByteString::formatted("{}{}_iterator_method{}", js_name, js_suffix, recursion_depth), interface, recursion_depth + 1);
|
||||||
|
|
||||||
if (optional) {
|
if (optional || parameter.type->is_nullable()) {
|
||||||
sequence_generator.append(R"~~~(
|
sequence_generator.append(R"~~~(
|
||||||
@cpp_name@ = move(@cpp_name@_non_optional);
|
@cpp_name@ = move(@cpp_name@_non_optional);
|
||||||
}
|
}
|
||||||
|
@ -4060,6 +4072,24 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.getter_callback@)
|
||||||
}
|
}
|
||||||
)~~~");
|
)~~~");
|
||||||
|
|
||||||
|
}
|
||||||
|
// If a reflected IDL attribute has the type FrozenArray<T>?, where T is either Element or an interface that
|
||||||
|
// inherits from Element, then with attr being the reflected content attribute name:
|
||||||
|
// FIXME: Handle "an interface that inherits from Element".
|
||||||
|
// FIXME: This should handle "FrozenArray" rather than "sequence".
|
||||||
|
else if (is_nullable_sequence_of_single_type(attribute.type, "Element"sv)) {
|
||||||
|
// 1. Let elements be the result of running this's get the attr-associated elements.
|
||||||
|
attribute_generator.append(R"~~~(
|
||||||
|
static auto content_attribute = "@attribute.reflect_name@"_fly_string;
|
||||||
|
|
||||||
|
auto retval = impl->get_the_attribute_associated_elements(content_attribute, TRY(throw_dom_exception_if_needed(vm, [&] { return impl->@attribute.cpp_name@(); })));
|
||||||
|
)~~~");
|
||||||
|
|
||||||
|
// FIXME: 2. If the contents of elements is equal to the contents of this's cached attr-associated elements, then return
|
||||||
|
// this's cached attr-associated elements object.
|
||||||
|
// FIXME: 3. Let elementsAsFrozenArray be elements, converted to a FrozenArray<T>?.
|
||||||
|
// FIXME: 4. Set this's cached attr-associated elements to elements.
|
||||||
|
// FIXME: 5. Set this's cached attr-associated elements object to elementsAsFrozenArray.
|
||||||
} else {
|
} else {
|
||||||
attribute_generator.append(R"~~~(
|
attribute_generator.append(R"~~~(
|
||||||
auto retval = impl->get_attribute_value("@attribute.reflect_name@"_fly_string);
|
auto retval = impl->get_attribute_value("@attribute.reflect_name@"_fly_string);
|
||||||
|
@ -4202,6 +4232,45 @@ JS_DEFINE_NATIVE_FUNCTION(@class_name@::@attribute.setter_callback@)
|
||||||
// 3. Set this's explicitly set attr-element to a weak reference to the given value.
|
// 3. Set this's explicitly set attr-element to a weak reference to the given value.
|
||||||
attribute_generator.append(R"~~~(
|
attribute_generator.append(R"~~~(
|
||||||
TRY(throw_dom_exception_if_needed(vm, [&] { return impl->set_@attribute.cpp_name@(cpp_value); }));
|
TRY(throw_dom_exception_if_needed(vm, [&] { return impl->set_@attribute.cpp_name@(cpp_value); }));
|
||||||
|
)~~~");
|
||||||
|
}
|
||||||
|
// If a reflected IDL attribute has the type FrozenArray<T>?, where T is either Element or an interface
|
||||||
|
// that inherits from Element, then with attr being the reflected content attribute name:
|
||||||
|
// FIXME: Handle "an interface that inherits from Element".
|
||||||
|
// FIXME: This should handle "FrozenArray" rather than "sequence".
|
||||||
|
else if (is_nullable_sequence_of_single_type(attribute.type, "Element"sv)) {
|
||||||
|
// 1. If the given value is null:
|
||||||
|
// 1. Set this's explicitly set attr-elements to null.
|
||||||
|
// 2. Run this's delete the content attribute.
|
||||||
|
// 3. Return.
|
||||||
|
attribute_generator.append(R"~~~(
|
||||||
|
static auto content_attribute = "@attribute.reflect_name@"_fly_string;
|
||||||
|
|
||||||
|
if (!cpp_value.has_value()) {
|
||||||
|
TRY(throw_dom_exception_if_needed(vm, [&] { return impl->set_@attribute.cpp_name@({}); }));
|
||||||
|
impl->remove_attribute(content_attribute);
|
||||||
|
return JS::js_undefined();
|
||||||
|
}
|
||||||
|
)~~~");
|
||||||
|
|
||||||
|
// 2. Run this's set the content attribute with the empty string.
|
||||||
|
attribute_generator.append(R"~~~(
|
||||||
|
MUST(impl->set_attribute(content_attribute, String {}));
|
||||||
|
)~~~");
|
||||||
|
|
||||||
|
// 3. Let elements be an empty list.
|
||||||
|
// 4. For each element in the given value:
|
||||||
|
// 1. Append a weak reference to element to elements.
|
||||||
|
// 5. Set this's explicitly set attr-elements to elements.
|
||||||
|
attribute_generator.append(R"~~~(
|
||||||
|
Vector<WeakPtr<DOM::Element>> elements;
|
||||||
|
elements.ensure_capacity(cpp_value->size());
|
||||||
|
|
||||||
|
for (auto const& element : *cpp_value) {
|
||||||
|
elements.unchecked_append(*element);
|
||||||
|
}
|
||||||
|
|
||||||
|
TRY(throw_dom_exception_if_needed(vm, [&] { return impl->set_@attribute.cpp_name@(move(elements)); }));
|
||||||
)~~~");
|
)~~~");
|
||||||
} else if (attribute.type->is_nullable()) {
|
} else if (attribute.type->is_nullable()) {
|
||||||
attribute_generator.append(R"~~~(
|
attribute_generator.append(R"~~~(
|
||||||
|
|
|
@ -2,32 +2,32 @@ Harness status: OK
|
||||||
|
|
||||||
Found 27 tests
|
Found 27 tests
|
||||||
|
|
||||||
16 Pass
|
22 Pass
|
||||||
11 Fail
|
5 Fail
|
||||||
Pass aria-activedescendant element reflection
|
Pass aria-activedescendant element reflection
|
||||||
Pass aria-activedescendant If the content attribute is set directly, the IDL attribute getter always returns the first element whose ID matches the content attribute.
|
Pass aria-activedescendant If the content attribute is set directly, the IDL attribute getter always returns the first element whose ID matches the content attribute.
|
||||||
Pass aria-activedescendant Setting the IDL attribute to an element which is not the first element in DOM order with its ID causes the content attribute to be an empty string
|
Pass aria-activedescendant Setting the IDL attribute to an element which is not the first element in DOM order with its ID causes the content attribute to be an empty string
|
||||||
Pass aria-activedescendant Setting an element reference that crosses into a shadow tree is disallowed, but setting one that is in a shadow inclusive ancestor is allowed.
|
Pass aria-activedescendant Setting an element reference that crosses into a shadow tree is disallowed, but setting one that is in a shadow inclusive ancestor is allowed.
|
||||||
Fail aria-errormessage
|
Pass aria-errormessage
|
||||||
Pass ariaErrorMessageElement is not defined
|
Pass ariaErrorMessageElement is not defined
|
||||||
Fail aria-details
|
Pass aria-details
|
||||||
Pass aria-activedescendant Deleting a reflected element should return null for the IDL attribute and the content attribute will be empty.
|
Pass aria-activedescendant Deleting a reflected element should return null for the IDL attribute and the content attribute will be empty.
|
||||||
Pass aria-activedescendant Changing the ID of an element doesn't lose the reference.
|
Pass aria-activedescendant Changing the ID of an element doesn't lose the reference.
|
||||||
Pass aria-activedescendant Reparenting an element into a descendant shadow scope hides the element reference.
|
Pass aria-activedescendant Reparenting an element into a descendant shadow scope hides the element reference.
|
||||||
Pass aria-activedescendant Reparenting referenced element cannot cause retargeting of reference.
|
Pass aria-activedescendant Reparenting referenced element cannot cause retargeting of reference.
|
||||||
Pass aria-activedescendant Element reference set in invalid scope remains intact throughout move to valid scope.
|
Pass aria-activedescendant Element reference set in invalid scope remains intact throughout move to valid scope.
|
||||||
Fail aria-labelledby.
|
Fail aria-labelledby.
|
||||||
Fail aria-controls.
|
Pass aria-controls.
|
||||||
Fail aria-describedby.
|
Pass aria-describedby.
|
||||||
Fail aria-flowto.
|
Pass aria-flowto.
|
||||||
Fail aria-owns.
|
Pass aria-owns.
|
||||||
Fail shadow DOM behaviour for FrozenArray element reflection.
|
Fail shadow DOM behaviour for FrozenArray element reflection.
|
||||||
Fail Moving explicitly set elements across shadow DOM boundaries.
|
Fail Moving explicitly set elements across shadow DOM boundaries.
|
||||||
Fail Moving explicitly set elements around within the same scope, and removing from the DOM.
|
Pass Moving explicitly set elements around within the same scope, and removing from the DOM.
|
||||||
Pass aria-activedescendant Reparenting.
|
Pass aria-activedescendant Reparenting.
|
||||||
Pass aria-activedescendant Attaching element reference before it's inserted into the DOM.
|
Pass aria-activedescendant Attaching element reference before it's inserted into the DOM.
|
||||||
Pass aria-activedescendant Cross-document references and moves.
|
Pass aria-activedescendant Cross-document references and moves.
|
||||||
Pass aria-activedescendant Adopting element keeps references.
|
Pass aria-activedescendant Adopting element keeps references.
|
||||||
Pass Caching invariant different attributes.
|
Fail Caching invariant different attributes.
|
||||||
Pass Caching invariant different elements.
|
Fail Caching invariant different elements.
|
||||||
Fail Passing values of the wrong type should throw a TypeError
|
Pass Passing values of the wrong type should throw a TypeError
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue