Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LibWeb: Update "compute the auto directionality" algorithm to match spec #1426

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 35 additions & 42 deletions Userland/Libraries/LibWeb/DOM/Element.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2575,31 +2575,6 @@ bool Element::is_auto_directionality_form_associated_element() const
// https://html.spec.whatwg.org/multipage/dom.html#auto-directionality
Optional<Element::Directionality> Element::auto_directionality() const
{
// https://html.spec.whatwg.org/multipage/dom.html#text-node-directionality
auto text_node_directionality = [](Text const& text_node) -> Optional<Directionality> {
// 1. If text's data does not contain a code point whose bidirectional character type is L, AL, or R, then return null.
// 2. Let codePoint be the first code point in text's data whose bidirectional character type is L, AL, or R.
Optional<Unicode::BidiClass> found_character_bidi_class;
for (auto code_point : Utf8View(text_node.data())) {
auto bidi_class = Unicode::bidirectional_class(code_point);
if (first_is_one_of(bidi_class, Unicode::BidiClass::LeftToRight, Unicode::BidiClass::RightToLeftArabic, Unicode::BidiClass::RightToLeft)) {
found_character_bidi_class = bidi_class;
break;
}
}
if (!found_character_bidi_class.has_value())
return {};

// 3. If codePoint is of bidirectional character type AL or R, then return 'rtl'.
if (first_is_one_of(*found_character_bidi_class, Unicode::BidiClass::RightToLeftArabic, Unicode::BidiClass::RightToLeft))
return Directionality::Rtl;

// 4. If codePoint is of bidirectional character type L, then return 'ltr'.
// NOTE: codePoint should always be of bidirectional character type L by this point, so we can just return 'ltr' here.
VERIFY(*found_character_bidi_class == Unicode::BidiClass::LeftToRight);
return Directionality::Ltr;
};

// 1. If element is an auto-directionality form-associated element:
if (is_auto_directionality_form_associated_element()) {
auto const* form_associated_element = dynamic_cast<HTML::FormAssociatedElement const*>(this);
Expand Down Expand Up @@ -2635,15 +2610,15 @@ Optional<Element::Directionality> Element::auto_directionality() const

// 2. If child is a Text node, then set childDirection to the text node directionality of child.
if (child->is_text())
child_direction = text_node_directionality(static_cast<Text const&>(*child));
child_direction = static_cast<Text const&>(*child).directionality();

// 3. Otherwise:
else {
// 1. Assert: child is an Element node.
VERIFY(child->is_element());

// 2. Set childDirection to the auto directionality of child.
child_direction = static_cast<HTML::HTMLElement const&>(*child).auto_directionality();
// 2. Set childDirection to the contained text auto directionality of child with canExcludeRoot set to true.
child_direction = static_cast<Element const&>(*child).contained_text_auto_directionality(true);
}

// 4. If childDirection is not null, then return childDirection.
Expand All @@ -2656,20 +2631,38 @@ Optional<Element::Directionality> Element::auto_directionality() const
}
}

// 3. For each node descendant of element's descendants, in tree order:
// 3. Return the contained text auto directionality of element with canExcludeRoot set to false.
return contained_text_auto_directionality(false);
}

// https://html.spec.whatwg.org/multipage/dom.html#contained-text-auto-directionality
Optional<Element::Directionality> Element::contained_text_auto_directionality(bool can_exclude_root) const
{
// To compute the contained text auto directionality of an element element with a boolean canExcludeRoot:

// 1. For each node descendant of element's descendants, in tree order:
Optional<Directionality> result;
for_each_in_subtree([&](auto& descendant) {
// 1. If descendant, or any of its ancestor elements that are descendants of element, is one of
// - FIXME: a bdi element
// - a script element
// - a style element
// - a textarea element
// - an element whose dir attribute is not in the undefined state
// then continue.
if (is<HTML::HTMLScriptElement>(descendant)
|| is<HTML::HTMLStyleElement>(descendant)
|| is<HTML::HTMLTextAreaElement>(descendant)
|| (is<Element>(descendant) && static_cast<Element const&>(descendant).dir().has_value())) {
// 1. If any of
// - descendant
// - any ancestor element of descendant that is a descendant of element
// - if canExcludeRoot is true, element
// is one of
// - FIXME: a bdi element
// - a script element
// - a style element
// - a textarea element
// - an element whose dir attribute is not in the undefined state
// then continue.
// NOTE: "any ancestor element of descendant that is a descendant of element" will be iterated already.
auto is_one_of_the_filtered_elements = [](auto& descendant) -> bool {
return is<HTML::HTMLScriptElement>(descendant)
|| is<HTML::HTMLStyleElement>(descendant)
|| is<HTML::HTMLTextAreaElement>(descendant)
|| (is<Element>(descendant) && static_cast<Element const&>(descendant).dir().has_value());
};
if (is_one_of_the_filtered_elements(descendant)
|| (can_exclude_root && is_one_of_the_filtered_elements(*this))) {
return TraversalDecision::SkipChildrenAndContinue;
}

Expand All @@ -2689,7 +2682,7 @@ Optional<Element::Directionality> Element::auto_directionality() const
return TraversalDecision::Continue;

// 4. Let result be the text node directionality of descendant.
result = text_node_directionality(static_cast<Text const&>(descendant));
result = static_cast<Text const&>(descendant).directionality();

// 5. If result is not null, then return result.
if (result.has_value())
Expand All @@ -2701,7 +2694,7 @@ Optional<Element::Directionality> Element::auto_directionality() const
if (result.has_value())
return result;

// 4. Return null.
// 2. Return null.
return {};
}

Expand Down
1 change: 1 addition & 0 deletions Userland/Libraries/LibWeb/DOM/Element.h
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ class Element
void enqueue_an_element_on_the_appropriate_element_queue();

Optional<Directionality> auto_directionality() const;
Optional<Directionality> contained_text_auto_directionality(bool can_exclude_root) const;
Directionality parent_directionality() const;
bool is_auto_directionality_form_associated_element() const;

Expand Down
27 changes: 27 additions & 0 deletions Userland/Libraries/LibWeb/DOM/Text.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* SPDX-License-Identifier: BSD-2-Clause
*/

#include <LibUnicode/CharacterTypes.h>
#include <LibWeb/Bindings/Intrinsics.h>
#include <LibWeb/Bindings/TextPrototype.h>
#include <LibWeb/DOM/Range.h>
Expand Down Expand Up @@ -149,4 +150,30 @@ String Text::whole_text()
return MUST(builder.to_string());
}

// https://html.spec.whatwg.org/multipage/dom.html#text-node-directionality
Optional<Element::Directionality> Text::directionality() const
{
// 1. If text's data does not contain a code point whose bidirectional character type is L, AL, or R, then return null.
// 2. Let codePoint be the first code point in text's data whose bidirectional character type is L, AL, or R.
Optional<Unicode::BidiClass> found_character_bidi_class;
for (auto code_point : Utf8View(data())) {
auto bidi_class = Unicode::bidirectional_class(code_point);
if (first_is_one_of(bidi_class, Unicode::BidiClass::LeftToRight, Unicode::BidiClass::RightToLeftArabic, Unicode::BidiClass::RightToLeft)) {
found_character_bidi_class = bidi_class;
break;
}
}
if (!found_character_bidi_class.has_value())
return {};

// 3. If codePoint is of bidirectional character type AL or R, then return 'rtl'.
if (first_is_one_of(*found_character_bidi_class, Unicode::BidiClass::RightToLeftArabic, Unicode::BidiClass::RightToLeft))
return Element::Directionality::Rtl;

// 4. If codePoint is of bidirectional character type L, then return 'ltr'.
// NOTE: codePoint should always be of bidirectional character type L by this point, so we can just return 'ltr' here.
VERIFY(*found_character_bidi_class == Unicode::BidiClass::LeftToRight);
return Element::Directionality::Ltr;
}

}
3 changes: 3 additions & 0 deletions Userland/Libraries/LibWeb/DOM/Text.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#pragma once

#include <LibWeb/DOM/CharacterData.h>
#include <LibWeb/DOM/Element.h>
#include <LibWeb/DOM/Slottable.h>

namespace Web::DOM {
Expand Down Expand Up @@ -48,6 +49,8 @@ class Text
bool is_password_input() const { return m_is_password_input; }
void set_is_password_input(Badge<HTML::HTMLInputElement>, bool b) { m_is_password_input = b; }

Optional<Element::Directionality> directionality() const;

protected:
Text(Document&, String const&);
Text(Document&, NodeType, String const&);
Expand Down
Loading