From 57a6d577ad8081b1ebeaa2692fbe474a21326d16 Mon Sep 17 00:00:00 2001 From: Jonah Date: Sat, 25 Feb 2023 15:50:34 -0600 Subject: [PATCH] LibWeb: Add AriaData object to parse ARIAMixin data This provides an easily serializeable interface for us to use in IPC. --- Userland/Libraries/LibWeb/ARIA/ARIAMixin.cpp | 18 + Userland/Libraries/LibWeb/ARIA/ARIAMixin.h | 52 +- Userland/Libraries/LibWeb/ARIA/AriaData.cpp | 529 +++++++++++++++++++ Userland/Libraries/LibWeb/ARIA/AriaData.h | 285 ++++++++++ Userland/Libraries/LibWeb/CMakeLists.txt | 1 + Userland/Libraries/LibWeb/DOM/Element.cpp | 5 + Userland/Libraries/LibWeb/DOM/Element.h | 2 + Userland/Libraries/LibWeb/Forward.h | 6 + 8 files changed, 893 insertions(+), 5 deletions(-) create mode 100644 Userland/Libraries/LibWeb/ARIA/AriaData.cpp create mode 100644 Userland/Libraries/LibWeb/ARIA/AriaData.h diff --git a/Userland/Libraries/LibWeb/ARIA/ARIAMixin.cpp b/Userland/Libraries/LibWeb/ARIA/ARIAMixin.cpp index 448855eefd..20989c091b 100644 --- a/Userland/Libraries/LibWeb/ARIA/ARIAMixin.cpp +++ b/Userland/Libraries/LibWeb/ARIA/ARIAMixin.cpp @@ -61,4 +61,22 @@ bool ARIAMixin::has_global_aria_attribute() const || !aria_role_description().is_null(); } +Optional ARIAMixin::parse_id_reference(DeprecatedString const& id_reference) const +{ + if (id_reference_exists(id_reference)) + return id_reference; + return {}; +} + +Vector ARIAMixin::parse_id_reference_list(DeprecatedString const& id_list) const +{ + Vector result; + auto id_references = id_list.split_view(Infra::is_ascii_whitespace); + for (auto const id_reference : id_references) { + if (id_reference_exists(id_reference)) + result.append(id_reference); + } + return result; +} + } diff --git a/Userland/Libraries/LibWeb/ARIA/ARIAMixin.h b/Userland/Libraries/LibWeb/ARIA/ARIAMixin.h index 84ba7eeee9..9a4cd76598 100644 --- a/Userland/Libraries/LibWeb/ARIA/ARIAMixin.h +++ b/Userland/Libraries/LibWeb/ARIA/ARIAMixin.h @@ -6,7 +6,9 @@ #pragma once -#include +#include +#include +#include #include #include @@ -22,16 +24,22 @@ public: virtual DeprecatedString aria_active_descendant() const = 0; virtual WebIDL::ExceptionOr set_aria_active_descendant(DeprecatedString const&) = 0; + virtual DeprecatedString aria_atomic() const = 0; virtual WebIDL::ExceptionOr set_aria_atomic(DeprecatedString const&) = 0; + virtual DeprecatedString aria_auto_complete() const = 0; virtual WebIDL::ExceptionOr set_aria_auto_complete(DeprecatedString const&) = 0; + virtual DeprecatedString aria_busy() const = 0; virtual WebIDL::ExceptionOr set_aria_busy(DeprecatedString const&) = 0; + virtual DeprecatedString aria_checked() const = 0; virtual WebIDL::ExceptionOr set_aria_checked(DeprecatedString const&) = 0; + virtual DeprecatedString aria_col_count() const = 0; virtual WebIDL::ExceptionOr set_aria_col_count(DeprecatedString const&) = 0; + virtual DeprecatedString aria_col_index() const = 0; virtual WebIDL::ExceptionOr set_aria_col_index(DeprecatedString const&) = 0; @@ -46,32 +54,40 @@ public: virtual DeprecatedString aria_described_by() const = 0; virtual WebIDL::ExceptionOr set_aria_described_by(DeprecatedString const&) = 0; + virtual DeprecatedString aria_details() const = 0; virtual WebIDL::ExceptionOr set_aria_details(DeprecatedString const&) = 0; - virtual DeprecatedString aria_drop_effect() const = 0; - virtual WebIDL::ExceptionOr set_aria_drop_effect(DeprecatedString const&) = 0; - virtual DeprecatedString aria_error_message() const = 0; - virtual WebIDL::ExceptionOr set_aria_error_message(DeprecatedString const&) = 0; virtual DeprecatedString aria_disabled() const = 0; virtual WebIDL::ExceptionOr set_aria_disabled(DeprecatedString const&) = 0; + virtual DeprecatedString aria_drop_effect() const = 0; + virtual WebIDL::ExceptionOr set_aria_drop_effect(DeprecatedString const&) = 0; + + virtual DeprecatedString aria_error_message() const = 0; + virtual WebIDL::ExceptionOr set_aria_error_message(DeprecatedString const&) = 0; + virtual DeprecatedString aria_expanded() const = 0; virtual WebIDL::ExceptionOr set_aria_expanded(DeprecatedString const&) = 0; virtual DeprecatedString aria_flow_to() const = 0; virtual WebIDL::ExceptionOr set_aria_flow_to(DeprecatedString const&) = 0; + virtual DeprecatedString aria_grabbed() const = 0; virtual WebIDL::ExceptionOr set_aria_grabbed(DeprecatedString const&) = 0; virtual DeprecatedString aria_has_popup() const = 0; virtual WebIDL::ExceptionOr set_aria_has_popup(DeprecatedString const&) = 0; + virtual DeprecatedString aria_hidden() const = 0; virtual WebIDL::ExceptionOr set_aria_hidden(DeprecatedString const&) = 0; + virtual DeprecatedString aria_invalid() const = 0; virtual WebIDL::ExceptionOr set_aria_invalid(DeprecatedString const&) = 0; + virtual DeprecatedString aria_key_shortcuts() const = 0; virtual WebIDL::ExceptionOr set_aria_key_shortcuts(DeprecatedString const&) = 0; + virtual DeprecatedString aria_label() const = 0; virtual WebIDL::ExceptionOr set_aria_label(DeprecatedString const&) = 0; @@ -80,14 +96,19 @@ public: virtual DeprecatedString aria_level() const = 0; virtual WebIDL::ExceptionOr set_aria_level(DeprecatedString const&) = 0; + virtual DeprecatedString aria_live() const = 0; virtual WebIDL::ExceptionOr set_aria_live(DeprecatedString const&) = 0; + virtual DeprecatedString aria_modal() const = 0; virtual WebIDL::ExceptionOr set_aria_modal(DeprecatedString const&) = 0; + virtual DeprecatedString aria_multi_line() const = 0; virtual WebIDL::ExceptionOr set_aria_multi_line(DeprecatedString const&) = 0; + virtual DeprecatedString aria_multi_selectable() const = 0; virtual WebIDL::ExceptionOr set_aria_multi_selectable(DeprecatedString const&) = 0; + virtual DeprecatedString aria_orientation() const = 0; virtual WebIDL::ExceptionOr set_aria_orientation(DeprecatedString const&) = 0; @@ -96,10 +117,13 @@ public: virtual DeprecatedString aria_placeholder() const = 0; virtual WebIDL::ExceptionOr set_aria_placeholder(DeprecatedString const&) = 0; + virtual DeprecatedString aria_pos_in_set() const = 0; virtual WebIDL::ExceptionOr set_aria_pos_in_set(DeprecatedString const&) = 0; + virtual DeprecatedString aria_pressed() const = 0; virtual WebIDL::ExceptionOr set_aria_pressed(DeprecatedString const&) = 0; + virtual DeprecatedString aria_read_only() const = 0; virtual WebIDL::ExceptionOr set_aria_read_only(DeprecatedString const&) = 0; @@ -108,27 +132,37 @@ public: virtual DeprecatedString aria_required() const = 0; virtual WebIDL::ExceptionOr set_aria_required(DeprecatedString const&) = 0; + virtual DeprecatedString aria_role_description() const = 0; virtual WebIDL::ExceptionOr set_aria_role_description(DeprecatedString const&) = 0; + virtual DeprecatedString aria_row_count() const = 0; virtual WebIDL::ExceptionOr set_aria_row_count(DeprecatedString const&) = 0; + virtual DeprecatedString aria_row_index() const = 0; virtual WebIDL::ExceptionOr set_aria_row_index(DeprecatedString const&) = 0; virtual DeprecatedString aria_row_span() const = 0; virtual WebIDL::ExceptionOr set_aria_row_span(DeprecatedString const&) = 0; + virtual DeprecatedString aria_selected() const = 0; virtual WebIDL::ExceptionOr set_aria_selected(DeprecatedString const&) = 0; + virtual DeprecatedString aria_set_size() const = 0; virtual WebIDL::ExceptionOr set_aria_set_size(DeprecatedString const&) = 0; + virtual DeprecatedString aria_sort() const = 0; virtual WebIDL::ExceptionOr set_aria_sort(DeprecatedString const&) = 0; + virtual DeprecatedString aria_value_max() const = 0; virtual WebIDL::ExceptionOr set_aria_value_max(DeprecatedString const&) = 0; + virtual DeprecatedString aria_value_min() const = 0; virtual WebIDL::ExceptionOr set_aria_value_min(DeprecatedString const&) = 0; + virtual DeprecatedString aria_value_now() const = 0; virtual WebIDL::ExceptionOr set_aria_value_now(DeprecatedString const&) = 0; + virtual DeprecatedString aria_value_text() const = 0; virtual WebIDL::ExceptionOr set_aria_value_text(DeprecatedString const&) = 0; @@ -145,8 +179,16 @@ public: bool has_global_aria_attribute() const; + // https://www.w3.org/TR/wai-aria-1.2/#valuetype_idref + Optional parse_id_reference(DeprecatedString const&) const; + + // https://www.w3.org/TR/wai-aria-1.2/#valuetype_idref_list + Vector parse_id_reference_list(DeprecatedString const&) const; + protected: ARIAMixin() = default; + + virtual bool id_reference_exists(DeprecatedString const&) const = 0; }; } diff --git a/Userland/Libraries/LibWeb/ARIA/AriaData.cpp b/Userland/Libraries/LibWeb/ARIA/AriaData.cpp new file mode 100644 index 0000000000..5baccad4ef --- /dev/null +++ b/Userland/Libraries/LibWeb/ARIA/AriaData.cpp @@ -0,0 +1,529 @@ +/* + * Copyright (c) 2023, Jonah Shafran + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace Web::ARIA { + +AriaData::AriaData(Web::ARIA::ARIAMixin const& source) +{ + m_aria_active_descendant = source.aria_active_descendant(); + m_aria_atomic = AriaData::parse_optional_true_false(source.aria_atomic()); + m_aria_auto_complete = AriaData::parse_aria_autocomplete(source.aria_auto_complete()); + m_aria_busy = AriaData::parse_true_false(source.aria_busy()); + m_aria_checked = AriaData::parse_tristate(source.aria_checked()); + m_aria_col_count = AriaData::parse_integer(source.aria_col_count()); + m_aria_col_index = AriaData::parse_integer(source.aria_col_index()); + m_aria_col_span = AriaData::parse_integer(source.aria_col_span()); + m_aria_controls = source.parse_id_reference_list(source.aria_controls()); + m_aria_current = AriaData::parse_aria_current(source.aria_current()); + m_aria_described_by = source.parse_id_reference_list(source.aria_described_by()); + m_aria_details = source.parse_id_reference(source.aria_details()); + m_aria_disabled = AriaData::parse_true_false(source.aria_disabled()); + m_aria_drop_effect = AriaData::parse_aria_drop_effect(source.aria_drop_effect()); + m_aria_error_message = source.parse_id_reference(source.aria_error_message()); + m_aria_expanded = AriaData::parse_true_false_undefined(source.aria_expanded()); + m_aria_flow_to = source.parse_id_reference_list(source.aria_flow_to()); + m_aria_grabbed = AriaData::parse_true_false_undefined(source.aria_grabbed()); + m_aria_has_popup = AriaData::parse_aria_has_popup(source.aria_has_popup()); + m_aria_hidden = AriaData::parse_true_false_undefined(source.aria_hidden()); + m_aria_invalid = AriaData::parse_aria_invalid(source.aria_invalid()); + m_aria_key_shortcuts = source.aria_key_shortcuts(); + m_aria_label = source.aria_label(); + m_aria_labelled_by = source.parse_id_reference_list(source.aria_labelled_by()); + m_aria_level = AriaData::parse_integer(source.aria_level()); + m_aria_live = AriaData::parse_aria_live(source.aria_live()); + m_aria_modal = AriaData::parse_true_false(source.aria_modal()); + m_aria_multi_line = AriaData::parse_true_false(source.aria_multi_line()); + m_aria_multi_selectable = AriaData::parse_true_false(source.aria_multi_selectable()); + m_aria_orientation = AriaData::parse_aria_orientation(source.aria_orientation()); + m_aria_owns = source.parse_id_reference_list(source.aria_owns()); + m_aria_placeholder = source.aria_placeholder(); + m_aria_pos_in_set = AriaData::parse_integer(source.aria_pos_in_set()); + m_aria_pressed = AriaData::parse_tristate(source.aria_pressed()); + m_aria_read_only = AriaData::parse_true_false(source.aria_read_only()); + m_aria_relevant = AriaData::parse_aria_relevant(source.aria_relevant()); + m_aria_required = AriaData::parse_true_false(source.aria_required()); + m_aria_role_description = source.aria_role_description(); + m_aria_row_count = AriaData::parse_integer(source.aria_row_count()); + m_aria_row_index = AriaData::parse_integer(source.aria_row_index()); + m_aria_row_span = AriaData::parse_integer(source.aria_row_span()); + m_aria_selected = AriaData::parse_true_false_undefined(source.aria_selected()); + m_aria_set_size = AriaData::parse_integer(source.aria_set_size()); + m_aria_sort = AriaData::parse_aria_sort(source.aria_sort()); + m_aria_value_max = AriaData::parse_number(source.aria_value_max()); + m_aria_value_min = AriaData::parse_number(source.aria_value_min()); + m_aria_value_now = AriaData::parse_number(source.aria_value_now()); + m_aria_value_text = source.aria_value_text(); +} + +bool AriaData::parse_true_false(StringView value) +{ + if (value == "true"sv) + return true; + if (value == "false"sv) + return false; + return false; +} + +Tristate AriaData::parse_tristate(StringView value) +{ + if (value == "true"sv) + return Tristate::True; + if (value == "false"sv) + return Tristate::False; + if (value == "mixed"sv) + return Tristate::Mixed; + if (value == "undefined"sv) + return Tristate::Undefined; + return Tristate::Undefined; +} + +Optional AriaData::parse_true_false_undefined(StringView value) +{ + if (value == "true"sv) + return true; + if (value == "false"sv) + return false; + if (value == "undefined"sv) + return {}; + return {}; +} + +Optional AriaData::parse_integer(StringView value) +{ + return value.to_int(); +} + +Optional AriaData::parse_number(StringView value) +{ + return value.to_double(TrimWhitespace::Yes); +} + +Optional AriaData::aria_active_descendant_or_default() const +{ + return m_aria_active_descendant; +} + +bool AriaData::aria_atomic_or_default(bool default_value) const +{ + auto value = m_aria_atomic; + if (!value.has_value()) + return default_value; + return value.value(); +} + +AriaAutocomplete AriaData::aria_auto_complete_or_default() const +{ + return m_aria_auto_complete; +} + +bool AriaData::aria_busy_or_default() const +{ + return m_aria_busy; +} + +Tristate AriaData::aria_checked_or_default() const +{ + return m_aria_checked; +} + +Optional AriaData::aria_col_count_or_default() const +{ + return m_aria_col_count; +} + +Optional AriaData::aria_col_index_or_default() const +{ + return m_aria_col_index; +} + +Optional AriaData::aria_col_span_or_default() const +{ + return m_aria_col_span; +} + +Vector AriaData::aria_controls_or_default() const +{ + return m_aria_controls; +} + +AriaCurrent AriaData::aria_current_or_default() const +{ + return m_aria_current; +} + +Vector AriaData::aria_described_by_or_default() const +{ + return m_aria_described_by; +} + +Optional AriaData::aria_details_or_default() const +{ + return m_aria_details; +} + +bool AriaData::aria_disabled_or_default() const +{ + return m_aria_disabled; +} + +Vector AriaData::aria_drop_effect_or_default() const +{ + return m_aria_drop_effect; +} + +Optional AriaData::aria_error_message_or_default() const +{ + return m_aria_error_message; +} + +Optional AriaData::aria_expanded_or_default() const +{ + return m_aria_expanded; +} + +Vector AriaData::aria_flow_to_or_default() const +{ + return m_aria_flow_to; +} + +Optional AriaData::aria_grabbed_or_default() const +{ + return m_aria_grabbed; +} + +AriaHasPopup AriaData::aria_has_popup_or_default() const +{ + return m_aria_has_popup; +} + +Optional AriaData::aria_hidden_or_default() const +{ + return m_aria_hidden; +} + +AriaInvalid AriaData::aria_invalid_or_default() const +{ + return m_aria_invalid; +} + +DeprecatedString AriaData::aria_key_shortcuts_or_default() const +{ + return m_aria_key_shortcuts; +} + +DeprecatedString AriaData::aria_label_or_default() const +{ + return m_aria_label; +} + +Vector AriaData::aria_labelled_by_or_default() const +{ + return m_aria_labelled_by; +} + +Optional AriaData::aria_level_or_default() const +{ + return m_aria_level; +} + +AriaLive AriaData::aria_live_or_default(AriaLive default_value) const +{ + auto value = m_aria_live; + if (!value.has_value()) + return default_value; + + return value.value(); +} + +bool AriaData::aria_modal_or_default() const +{ + return m_aria_modal; +} + +bool AriaData::aria_multi_line_or_default() const +{ + return m_aria_multi_line; +} + +bool AriaData::aria_multi_selectable_or_default() const +{ + return m_aria_multi_selectable; +} + +AriaOrientation AriaData::aria_orientation_or_default(AriaOrientation default_value) const +{ + auto value = m_aria_orientation; + if (!value.has_value()) + return default_value; + + return value.value(); +} + +Vector AriaData::aria_owns_or_default() const +{ + return m_aria_owns; +} + +DeprecatedString AriaData::aria_placeholder_or_default() const +{ + return m_aria_placeholder; +} + +Optional AriaData::aria_pos_in_set_or_default() const +{ + return m_aria_pos_in_set; +} + +Tristate AriaData::aria_pressed_or_default() const +{ + return m_aria_pressed; +} + +bool AriaData::aria_read_only_or_default() const +{ + return m_aria_read_only; +} + +Vector AriaData::aria_relevant_or_default() const +{ + return m_aria_relevant; +} + +bool AriaData::aria_required_or_default() const +{ + return m_aria_required; +} + +DeprecatedString AriaData::aria_role_description_or_default() const +{ + return m_aria_role_description; +} + +Optional AriaData::aria_row_count_or_default() const +{ + return m_aria_row_count; +} + +Optional AriaData::aria_row_index_or_default() const +{ + return m_aria_row_index; +} + +Optional AriaData::aria_row_span_or_default() const +{ + return m_aria_row_span; +} + +Optional AriaData::aria_selected_or_default() const +{ + return m_aria_selected; +} + +Optional AriaData::aria_set_size_or_default() const +{ + return m_aria_set_size; +} + +AriaSort AriaData::aria_sort_or_default() const +{ + return m_aria_sort; +} + +Optional AriaData::aria_value_max_or_default(Optional default_value) const +{ + auto value = m_aria_value_max; + if (!value.has_value()) + return default_value; + return value; +} + +Optional AriaData::aria_value_min_or_default(Optional default_value) const +{ + auto value = m_aria_value_min; + if (!value.has_value()) + return default_value; + return value; +} + +Optional AriaData::aria_value_now_or_default() const +{ + return m_aria_value_now; +} + +DeprecatedString AriaData::aria_value_text_or_default() const +{ + return m_aria_value_text; +} + +AriaAutocomplete AriaData::parse_aria_autocomplete(StringView value) +{ + if (value == "inline"sv) + return AriaAutocomplete::Inline; + if (value == "list"sv) + return AriaAutocomplete::List; + if (value == "both"sv) + return AriaAutocomplete::Both; + if (value == "none"sv) + return AriaAutocomplete::None; + return AriaAutocomplete::None; +} + +AriaCurrent AriaData::parse_aria_current(StringView value) +{ + if (value == "page"sv) + return AriaCurrent::Page; + if (value == "step"sv) + return AriaCurrent::Step; + if (value == "location"sv) + return AriaCurrent::Location; + if (value == "date"sv) + return AriaCurrent::Date; + if (value == "time"sv) + return AriaCurrent::Time; + if (value == "true"sv) + return AriaCurrent::True; + if (value == "false"sv) + return AriaCurrent::False; + return AriaCurrent::False; +} + +Vector AriaData::parse_aria_drop_effect(StringView value) +{ + Vector result; + + for (auto token : value.split_view_if(Infra::is_ascii_whitespace)) { + if (token == "copy"sv) + result.append(AriaDropEffect::Copy); + else if (token == "execute"sv) + result.append(AriaDropEffect::Execute); + else if (token == "link"sv) + result.append(AriaDropEffect::Link); + else if (token == "move"sv) + result.append(AriaDropEffect::Move); + else if (token == "popup"sv) + result.append(AriaDropEffect::Popup); + } + + // None combined with any other token value is ignored + if (result.is_empty()) + result.append(AriaDropEffect::None); + + return result; +} + +AriaHasPopup AriaData::parse_aria_has_popup(StringView value) +{ + if (value == "false"sv) + return AriaHasPopup::False; + if (value == "true"sv) + return AriaHasPopup::True; + if (value == "menu"sv) + return AriaHasPopup::Menu; + if (value == "listbox"sv) + return AriaHasPopup::Listbox; + if (value == "tree"sv) + return AriaHasPopup::Tree; + if (value == "grid"sv) + return AriaHasPopup::Grid; + if (value == "dialog"sv) + return AriaHasPopup::Dialog; + return AriaHasPopup::False; +} + +AriaInvalid AriaData::parse_aria_invalid(StringView value) +{ + if (value == "grammar"sv) + return AriaInvalid::Grammar; + if (value == "false"sv) + return AriaInvalid::False; + if (value == "spelling"sv) + return AriaInvalid::Spelling; + if (value == "true"sv) + return AriaInvalid::True; + return AriaInvalid::False; +} + +Optional AriaData::parse_aria_live(StringView value) +{ + if (value == "assertive"sv) + return AriaLive::Assertive; + if (value == "off"sv) + return AriaLive::Off; + if (value == "polite"sv) + return AriaLive::Polite; + return {}; +} + +Optional AriaData::parse_aria_orientation(StringView value) +{ + if (value == "horizontal"sv) + return AriaOrientation::Horizontal; + if (value == "undefined"sv) + return AriaOrientation::Undefined; + if (value == "vertical"sv) + return AriaOrientation::Vertical; + return {}; +} + +Vector AriaData::parse_aria_relevant(StringView value) +{ + Vector result; + auto tokens = value.split_view_if(Infra::is_ascii_whitespace); + for (size_t i = 0; i < tokens.size(); i++) { + if (tokens[i] == "additions"sv) { + if (i + 1 < tokens.size()) { + if (tokens[i + 1] == "text"sv) { + result.append(AriaRelevant::AdditionsText); + ++i; + continue; + } + if (tokens[i + 1] == "removals"sv && i + 2 < tokens.size() && tokens[i + 2] == "text"sv) { + result.append(AriaRelevant::All); + i += 2; + continue; + } + } + result.append(AriaRelevant::Additions); + } else if (tokens[i] == "all"sv) + result.append(AriaRelevant::All); + else if (tokens[i] == "removals"sv) + result.append(AriaRelevant::Removals); + else if (tokens[i] == "text"sv) + result.append(AriaRelevant::Text); + } + + if (result.is_empty()) + result.append(AriaRelevant::AdditionsText); + + return result; +} + +AriaSort AriaData::parse_aria_sort(StringView value) +{ + if (value == "ascending"sv) + return AriaSort::Ascending; + if (value == "descending"sv) + return AriaSort::Descending; + if (value == "none"sv) + return AriaSort::None; + if (value == "other"sv) + return AriaSort::Other; + return AriaSort::None; +} + +Optional AriaData::parse_optional_true_false(StringView value) +{ + if (value == "true"sv) + return true; + if (value == "false"sv) + return false; + return {}; +} + +} diff --git a/Userland/Libraries/LibWeb/ARIA/AriaData.h b/Userland/Libraries/LibWeb/ARIA/AriaData.h new file mode 100644 index 0000000000..76962c75ac --- /dev/null +++ b/Userland/Libraries/LibWeb/ARIA/AriaData.h @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2023, Jonah Shafran + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +namespace Web::ARIA { + +// https://www.w3.org/TR/wai-aria-1.2/#valuetype_tristate +enum class Tristate { + True, + False, + Mixed, + Undefined +}; + +// https://www.w3.org/TR/wai-aria-1.2/#aria-autocomplete +enum class AriaAutocomplete { + // When a user is providing input, text suggesting one way to complete the provided input may be dynamically inserted after the caret. + Inline, + // When a user is providing input, an element containing a collection of values that could complete the provided input may be displayed. + List, + // When a user is providing input, an element containing a collection of values that could complete the provided input may be displayed. + // If displayed, one value in the collection is automatically selected, and the text needed to complete the automatically selected value appears after the caret in the input + Both, + // When a user is providing input, an automatic suggestion that attempts to predict how the user intends to complete the input is not displayed. + None +}; + +// https://www.w3.org/TR/wai-aria-1.2/#aria-current +enum class AriaCurrent { + // Represents the current page within a set of pages. + Page, + // Represents the current step within a process. + Step, + // Represents the current location within an environment or context. + Location, + // Represents the current date within a collection of dates. + Date, + // Represents the current time within a set of times. + Time, + // Represents the current item within a set. + True, + // Does not represent the current item within a set. + False +}; + +// https://www.w3.org/TR/wai-aria-1.2/#aria-dropeffect +enum class AriaDropEffect { + // A duplicate of the source object will be dropped into the target. + Copy, + // A function supported by the drop target is executed, using the drag source as an input. + Execute, + // A reference or shortcut to the dragged object will be created in the target object. + Link, + // The source object will be removed from its current location and dropped into the target. + Move, + // No operation can be performed; effectively cancels the drag operation if an attempt is made to drop on this object. + // Ignored if combined with any other token value. e.g., 'none copy' is equivalent to a 'copy' value. + None, + // There is a popup menu or dialog that allows the user to choose one of the drag operations (copy, move, link, execute) and any other drag functionality, such as cancel. + Popup +}; + +// https://www.w3.org/TR/wai-aria-1.2/#aria-haspopup +enum class AriaHasPopup { + // Indicates the element does not have a popup. + False, + // Indicates the popup is a menu. + True, + // Indicates the popup is a menu. + Menu, + // Indicates the popup is a listbox. + Listbox, + // Indicates the popup is a tree. + Tree, + // Indicates the popup is a grid. + Grid, + // Indicates the popup is a dialog. + Dialog +}; + +// https://www.w3.org/TR/wai-aria-1.2/#aria-invalid +enum class AriaInvalid { + // A grammatical error was detected. + Grammar, + // There are no detected errors in the value. + False, + // A spelling error was detected. + Spelling, + // The value entered by the user has failed validation. + True +}; + +// https://www.w3.org/TR/wai-aria-1.2/#aria-live +enum class AriaLive { + // Indicates that updates to the region have the highest priority and should be presented the user immediately. + Assertive, + // Indicates that updates to the region should not be presented to the user unless the user is currently focused on that region. + Off, + // Indicates that updates to the region should be presented at the next graceful opportunity, such as at the end of speaking the current sentence or when the user pauses typing. + Polite +}; + +// https://www.w3.org/TR/wai-aria-1.2/#aria-orientation +enum class AriaOrientation { + // The element is oriented horizontally. + Horizontal, + // The element's orientation is unknown/ambiguous. + Undefined, + // The element is oriented vertically. + Vertical +}; + +// https://www.w3.org/TR/wai-aria-1.2/#aria-relevant +enum class AriaRelevant { + // Element nodes are added to the accessibility tree within the live region. + Additions, + // Equivalent to the combination of values, "additions text". + AdditionsText, + // Equivalent to the combination of all values, "additions removals text". + All, + // Text content, a text alternative, or an element node within the live region is removed from the accessibility tree. + Removals, + // Text content or a text alternative is added to any descendant in the accessibility tree of the live region. + Text +}; + +// https://www.w3.org/TR/wai-aria-1.2/#aria-sort +enum class AriaSort { + // Items are sorted in ascending order by this column. + Ascending, + // Items are sorted in descending order by this column. + Descending, + // There is no defined sort applied to the column. + None, + // A sort algorithm other than ascending or descending has been applied. + Other +}; + +class AriaData { +public: + AriaData() { } + + static ErrorOr> build_data(ARIAMixin const& mixin) { return adopt_nonnull_own_or_enomem(new (nothrow) AriaData(mixin)); } + + Optional aria_active_descendant_or_default() const; + bool aria_atomic_or_default(bool default_value = false) const; + AriaAutocomplete aria_auto_complete_or_default() const; + bool aria_busy_or_default() const; + Tristate aria_checked_or_default() const; + Optional aria_col_count_or_default() const; + Optional aria_col_index_or_default() const; + Optional aria_col_span_or_default() const; + Vector aria_controls_or_default() const; + AriaCurrent aria_current_or_default() const; + Vector aria_described_by_or_default() const; + Optional aria_details_or_default() const; + bool aria_disabled_or_default() const; + Vector aria_drop_effect_or_default() const; + Optional aria_error_message_or_default() const; + Optional aria_expanded_or_default() const; + Vector aria_flow_to_or_default() const; + Optional aria_grabbed_or_default() const; + AriaHasPopup aria_has_popup_or_default() const; + Optional aria_hidden_or_default() const; + AriaInvalid aria_invalid_or_default() const; + DeprecatedString aria_key_shortcuts_or_default() const; + DeprecatedString aria_label_or_default() const; + Vector aria_labelled_by_or_default() const; + Optional aria_level_or_default() const; + AriaLive aria_live_or_default(AriaLive default_value = AriaLive::Off) const; + bool aria_modal_or_default() const; + bool aria_multi_line_or_default() const; + bool aria_multi_selectable_or_default() const; + AriaOrientation aria_orientation_or_default(AriaOrientation default_value = AriaOrientation::Undefined) const; + Vector aria_owns_or_default() const; + DeprecatedString aria_placeholder_or_default() const; + Optional aria_pos_in_set_or_default() const; + Tristate aria_pressed_or_default() const; + bool aria_read_only_or_default() const; + Vector aria_relevant_or_default() const; + bool aria_required_or_default() const; + DeprecatedString aria_role_description_or_default() const; + Optional aria_row_count_or_default() const; + Optional aria_row_index_or_default() const; + Optional aria_row_span_or_default() const; + Optional aria_selected_or_default() const; + Optional aria_set_size_or_default() const; + AriaSort aria_sort_or_default() const; + Optional aria_value_max_or_default(Optional default_value = {}) const; + Optional aria_value_min_or_default(Optional default_value = {}) const; + Optional aria_value_now_or_default() const; + DeprecatedString aria_value_text_or_default() const; + +private: + explicit AriaData(ARIAMixin const&); + + // https://www.w3.org/TR/wai-aria-1.2/#valuetype_true-false + // The default value for this value type is false unless otherwise specified. + static bool parse_true_false(StringView); + + // https://www.w3.org/TR/wai-aria-1.2/#valuetype_tristate + // The default value for this value type is undefined unless otherwise specified. + static Tristate parse_tristate(StringView); + + // https://www.w3.org/TR/wai-aria-1.2/#valuetype_true-false-undefined + // The default value for this value type is undefined unless otherwise specified. + static Optional parse_true_false_undefined(StringView); + + // https://www.w3.org/TR/wai-aria-1.2/#valuetype_integer + static Optional parse_integer(StringView); + + // https://www.w3.org/TR/wai-aria-1.2/#valuetype_number + static Optional parse_number(StringView); + + static AriaAutocomplete parse_aria_autocomplete(StringView); + static AriaCurrent parse_aria_current(StringView); + static Vector parse_aria_drop_effect(StringView); + static AriaHasPopup parse_aria_has_popup(StringView); + static AriaInvalid parse_aria_invalid(StringView); + static Optional parse_aria_live(StringView); + static Optional parse_aria_orientation(StringView); + static Vector parse_aria_relevant(StringView); + static AriaSort parse_aria_sort(StringView); + static Optional parse_optional_true_false(StringView); + + Optional m_aria_active_descendant; + Optional m_aria_atomic; + AriaAutocomplete m_aria_auto_complete; + bool m_aria_busy; + Tristate m_aria_checked; + Optional m_aria_col_count; + Optional m_aria_col_index; + Optional m_aria_col_span; + Vector m_aria_controls; + AriaCurrent m_aria_current; + Vector m_aria_described_by; + Optional m_aria_details; + bool m_aria_disabled; + Vector m_aria_drop_effect; + Optional m_aria_error_message; + Optional m_aria_expanded; + Vector m_aria_flow_to; + Optional m_aria_grabbed; + AriaHasPopup m_aria_has_popup; + Optional m_aria_hidden; + AriaInvalid m_aria_invalid; + DeprecatedString m_aria_key_shortcuts; + DeprecatedString m_aria_label; + Vector m_aria_labelled_by; + Optional m_aria_level; + Optional m_aria_live; + bool m_aria_modal; + bool m_aria_multi_line; + bool m_aria_multi_selectable; + Optional m_aria_orientation; + Vector m_aria_owns; + DeprecatedString m_aria_placeholder; + Optional m_aria_pos_in_set; + Tristate m_aria_pressed; + bool m_aria_read_only; + Vector m_aria_relevant; + bool m_aria_required; + DeprecatedString m_aria_role_description; + Optional m_aria_row_count; + Optional m_aria_row_index; + Optional m_aria_row_span; + Optional m_aria_selected; + Optional m_aria_set_size; + AriaSort m_aria_sort; + Optional m_aria_value_max; + Optional m_aria_value_min; + Optional m_aria_value_now; + DeprecatedString m_aria_value_text; +}; + +} diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index 84cba59122..1424770239 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -1,6 +1,7 @@ include(libweb_generators) set(SOURCES + ARIA/AriaData.cpp ARIA/ARIAMixin.cpp ARIA/ARIAMixin.idl ARIA/Roles.cpp diff --git a/Userland/Libraries/LibWeb/DOM/Element.cpp b/Userland/Libraries/LibWeb/DOM/Element.cpp index d0ed35d371..34bca0ed5a 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.cpp +++ b/Userland/Libraries/LibWeb/DOM/Element.cpp @@ -1779,4 +1779,9 @@ void Element::scroll(HTML::ScrollToOptions const&) dbgln("FIXME: Implement Element::scroll(ScrollToOptions)"); } +bool Element::id_reference_exists(DeprecatedString const& id_reference) const +{ + return document().get_element_by_id(id_reference); +} + } diff --git a/Userland/Libraries/LibWeb/DOM/Element.h b/Userland/Libraries/LibWeb/DOM/Element.h index 455eedfd69..70c1e6e20f 100644 --- a/Userland/Libraries/LibWeb/DOM/Element.h +++ b/Userland/Libraries/LibWeb/DOM/Element.h @@ -318,6 +318,8 @@ protected: virtual void visit_edges(Cell::Visitor&) override; + virtual bool id_reference_exists(DeprecatedString const&) const override; + private: void make_html_uppercased_qualified_name(); diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 319fdb56ba..fac541e70c 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -23,6 +23,12 @@ class ResourceLoader; class XMLDocumentBuilder; } +namespace Web::ARIA { +class AriaData; +class ARIAMixin; +enum class StateAndProperties; +} + namespace Web::Bindings { class Intrinsics; class OptionConstructor;