Skip to content

Commit

Permalink
LibWeb: Add concept of boundary point to DOM::AbstractRange
Browse files Browse the repository at this point in the history
This makes comparing the relative position of boundary points a bit
nicer when one of the boundary points is the range's start or end.
  • Loading branch information
gmta committed Dec 19, 2024
1 parent ea553e0 commit 1cda583
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 102 deletions.
8 changes: 8 additions & 0 deletions Libraries/LibWeb/DOM/AbstractRange.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,24 @@

namespace Web::DOM {

// https://dom.spec.whatwg.org/#concept-range-bp
struct BoundaryPoint {
GC::Ref<Node> node;
WebIDL::UnsignedLong offset;
};

// https://dom.spec.whatwg.org/#abstractrange
class AbstractRange : public Bindings::PlatformObject {
WEB_PLATFORM_OBJECT(AbstractRange, Bindings::PlatformObject);

public:
virtual ~AbstractRange() override;

BoundaryPoint start() const { return { m_start_container, m_start_offset }; }
GC::Ref<Node> start_container() const { return m_start_container; }
WebIDL::UnsignedLong start_offset() const { return m_start_offset; }

BoundaryPoint end() const { return { m_end_container, m_end_offset }; }
GC::Ref<Node> end_container() const { return m_end_container; }
WebIDL::UnsignedLong end_offset() const { return m_end_offset; }

Expand Down
46 changes: 23 additions & 23 deletions Libraries/LibWeb/DOM/Range.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,27 +117,27 @@ GC::Ref<Node> Range::root() const
}

// https://dom.spec.whatwg.org/#concept-range-bp-position
RelativeBoundaryPointPosition position_of_boundary_point_relative_to_other_boundary_point(GC::Ref<Node> node_a, u32 offset_a, GC::Ref<Node> node_b, u32 offset_b)
RelativeBoundaryPointPosition position_of_boundary_point_relative_to_other_boundary_point(BoundaryPoint a, BoundaryPoint b)
{
// 1. Assert: nodeA and nodeB have the same root.
// NOTE: Nodes may not share the same root if they belong to different shadow trees,
// so we assert that they share the same shadow-including root instead.
VERIFY(&node_a->shadow_including_root() == &node_b->shadow_including_root());
VERIFY(&a.node->shadow_including_root() == &b.node->shadow_including_root());

// 2. If nodeA is nodeB, then return equal if offsetA is offsetB, before if offsetA is less than offsetB, and after if offsetA is greater than offsetB.
if (node_a == node_b) {
if (offset_a == offset_b)
if (a.node == b.node) {
if (a.offset == b.offset)
return RelativeBoundaryPointPosition::Equal;

if (offset_a < offset_b)
if (a.offset < b.offset)
return RelativeBoundaryPointPosition::Before;

return RelativeBoundaryPointPosition::After;
}

// 3. If nodeA is following nodeB, then if the position of (nodeB, offsetB) relative to (nodeA, offsetA) is before, return after, and if it is after, return before.
if (node_a->is_following(node_b)) {
auto relative_position = position_of_boundary_point_relative_to_other_boundary_point(node_b, offset_b, node_a, offset_a);
if (a.node->is_following(b.node)) {
auto relative_position = position_of_boundary_point_relative_to_other_boundary_point(b, a);

if (relative_position == RelativeBoundaryPointPosition::Before)
return RelativeBoundaryPointPosition::After;
Expand All @@ -147,19 +147,19 @@ RelativeBoundaryPointPosition position_of_boundary_point_relative_to_other_bound
}

// 4. If nodeA is an ancestor of nodeB:
if (node_a->is_ancestor_of(node_b)) {
if (a.node->is_ancestor_of(b.node)) {
// 1. Let child be nodeB.
GC::Ref<Node const> child = node_b;
GC::Ref<Node const> child = b.node;

// 2. While child is not a child of nodeA, set child to its parent.
while (!node_a->is_parent_of(child)) {
while (!a.node->is_parent_of(child)) {
auto* parent = child->parent();
VERIFY(parent);
child = *parent;
}

// 3. If child’s index is less than offsetA, then return after.
if (child->index() < offset_a)
if (child->index() < a.offset)
return RelativeBoundaryPointPosition::After;
}

Expand All @@ -185,7 +185,7 @@ WebIDL::ExceptionOr<void> Range::set_start_or_end(GC::Ref<Node> node, u32 offset
// -> If these steps were invoked as "set the start"

// 1. If range’s root is not equal to node’s root, or if bp is after the range’s end, set range’s end to bp.
if (root().ptr() != &node->root() || position_of_boundary_point_relative_to_other_boundary_point(node, offset, m_end_container, m_end_offset) == RelativeBoundaryPointPosition::After) {
if (root().ptr() != &node->root() || position_of_boundary_point_relative_to_other_boundary_point({ node, offset }, end()) == RelativeBoundaryPointPosition::After) {
m_end_container = node;
m_end_offset = offset;
}
Expand All @@ -198,7 +198,7 @@ WebIDL::ExceptionOr<void> Range::set_start_or_end(GC::Ref<Node> node, u32 offset
VERIFY(start_or_end == StartOrEnd::End);

// 1. If range’s root is not equal to node’s root, or if bp is before the range’s start, set range’s start to bp.
if (root().ptr() != &node->root() || position_of_boundary_point_relative_to_other_boundary_point(node, offset, m_start_container, m_start_offset) == RelativeBoundaryPointPosition::Before) {
if (root().ptr() != &node->root() || position_of_boundary_point_relative_to_other_boundary_point({ node, offset }, start()) == RelativeBoundaryPointPosition::Before) {
m_start_container = node;
m_start_offset = offset;
}
Expand Down Expand Up @@ -349,7 +349,7 @@ WebIDL::ExceptionOr<WebIDL::Short> Range::compare_boundary_points(WebIDL::Unsign
VERIFY(other_point_node);

// 4. If the position of this point relative to other point is
auto relative_position = position_of_boundary_point_relative_to_other_boundary_point(*this_point_node, this_point_offset, *other_point_node, other_point_offset);
auto relative_position = position_of_boundary_point_relative_to_other_boundary_point({ *this_point_node, this_point_offset }, { *other_point_node, other_point_offset });
switch (relative_position) {
case RelativeBoundaryPointPosition::Before:
// -> before
Expand Down Expand Up @@ -492,11 +492,11 @@ bool Range::intersects_node(GC::Ref<Node> node) const
return true;

// 4. Let offset be node’s index.
auto offset = node->index();
WebIDL::UnsignedLong offset = node->index();

// 5. If (parent, offset) is before end and (parent, offset plus 1) is after start, return true
auto relative_position_to_end = position_of_boundary_point_relative_to_other_boundary_point(*parent, offset, m_end_container, m_end_offset);
auto relative_position_to_start = position_of_boundary_point_relative_to_other_boundary_point(*parent, offset + 1, m_start_container, m_start_offset);
auto relative_position_to_end = position_of_boundary_point_relative_to_other_boundary_point({ *parent, offset }, end());
auto relative_position_to_start = position_of_boundary_point_relative_to_other_boundary_point({ *parent, offset + 1 }, start());
if (relative_position_to_end == RelativeBoundaryPointPosition::Before && relative_position_to_start == RelativeBoundaryPointPosition::After)
return true;

Expand All @@ -520,8 +520,8 @@ WebIDL::ExceptionOr<bool> Range::is_point_in_range(GC::Ref<Node> node, WebIDL::U
return WebIDL::IndexSizeError::create(realm(), MUST(String::formatted("Node does not contain a child at offset {}", offset)));

// 4. If (node, offset) is before start or after end, return false.
auto relative_position_to_start = position_of_boundary_point_relative_to_other_boundary_point(node, offset, m_start_container, m_start_offset);
auto relative_position_to_end = position_of_boundary_point_relative_to_other_boundary_point(node, offset, m_end_container, m_end_offset);
auto relative_position_to_start = position_of_boundary_point_relative_to_other_boundary_point({ node, offset }, start());
auto relative_position_to_end = position_of_boundary_point_relative_to_other_boundary_point({ node, offset }, end());
if (relative_position_to_start == RelativeBoundaryPointPosition::Before || relative_position_to_end == RelativeBoundaryPointPosition::After)
return false;

Expand All @@ -545,12 +545,12 @@ WebIDL::ExceptionOr<WebIDL::Short> Range::compare_point(GC::Ref<Node> node, WebI
return WebIDL::IndexSizeError::create(realm(), MUST(String::formatted("Node does not contain a child at offset {}", offset)));

// 4. If (node, offset) is before start, return −1.
auto relative_position_to_start = position_of_boundary_point_relative_to_other_boundary_point(node, offset, m_start_container, m_start_offset);
auto relative_position_to_start = position_of_boundary_point_relative_to_other_boundary_point({ node, offset }, start());
if (relative_position_to_start == RelativeBoundaryPointPosition::Before)
return -1;

// 5. If (node, offset) is after end, return 1.
auto relative_position_to_end = position_of_boundary_point_relative_to_other_boundary_point(node, offset, m_end_container, m_end_offset);
auto relative_position_to_end = position_of_boundary_point_relative_to_other_boundary_point({ node, offset }, end());
if (relative_position_to_end == RelativeBoundaryPointPosition::After)
return 1;

Expand Down Expand Up @@ -794,11 +794,11 @@ bool Range::contains_node(GC::Ref<Node> node) const
return false;

// and (node, 0) is after range’s start,
if (position_of_boundary_point_relative_to_other_boundary_point(node, 0, m_start_container, m_start_offset) != RelativeBoundaryPointPosition::After)
if (position_of_boundary_point_relative_to_other_boundary_point({ node, 0 }, start()) != RelativeBoundaryPointPosition::After)
return false;

// and (node, node’s length) is before range’s end.
if (position_of_boundary_point_relative_to_other_boundary_point(node, node->length(), m_end_container, m_end_offset) != RelativeBoundaryPointPosition::Before)
if (position_of_boundary_point_relative_to_other_boundary_point({ node, static_cast<WebIDL::UnsignedLong>(node->length()) }, end()) != RelativeBoundaryPointPosition::Before)
return false;

return true;
Expand Down
2 changes: 1 addition & 1 deletion Libraries/LibWeb/DOM/Range.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ enum class RelativeBoundaryPointPosition {
};

// https://dom.spec.whatwg.org/#concept-range-bp-position
RelativeBoundaryPointPosition position_of_boundary_point_relative_to_other_boundary_point(GC::Ref<Node> node_a, u32 offset_a, GC::Ref<Node> node_b, u32 offset_b);
RelativeBoundaryPointPosition position_of_boundary_point_relative_to_other_boundary_point(BoundaryPoint a, BoundaryPoint b);

class Range final : public AbstractRange {
WEB_PLATFORM_OBJECT(Range, AbstractRange);
Expand Down
2 changes: 1 addition & 1 deletion Libraries/LibWeb/Editing/Commands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ bool command_delete_action(DOM::Document& document, String const&)
}

// 2. Canonicalize whitespace at the active range's start.
canonicalize_whitespace(active_range.start_container(), active_range.start_offset());
canonicalize_whitespace(active_range.start());

// 3. Let node and offset be the active range's start node and offset.
GC::Ptr<DOM::Node> node = active_range.start_container();
Expand Down
Loading

0 comments on commit 1cda583

Please sign in to comment.