2020-12-06 19:51:55 +01:00
/*
* Copyright ( c ) 2020 , the SerenityOS developers .
2022-01-30 23:35:51 +00:00
* Copyright ( c ) 2022 , Luke Wilde < lukew @ serenityos . org >
2020-12-06 19:51:55 +01:00
*
2021-04-22 01:24:48 -07:00
* SPDX - License - Identifier : BSD - 2 - Clause
2020-12-06 19:51:55 +01:00
*/
# include <LibWeb/DOM/Document.h>
2022-01-31 18:05:54 +00:00
# include <LibWeb/DOM/DocumentType.h>
2020-12-06 19:51:55 +01:00
# include <LibWeb/DOM/Node.h>
# include <LibWeb/DOM/Range.h>
2020-12-06 22:09:24 +01:00
# include <LibWeb/DOM/Window.h>
2020-12-06 19:51:55 +01:00
namespace Web : : DOM {
2021-02-21 23:41:54 +01:00
NonnullRefPtr < Range > Range : : create ( Window & window )
{
2021-09-09 13:55:31 +02:00
return Range : : create ( window . associated_document ( ) ) ;
2021-02-21 23:41:54 +01:00
}
NonnullRefPtr < Range > Range : : create ( Document & document )
{
2021-04-23 16:46:57 +02:00
return adopt_ref ( * new Range ( document ) ) ;
2021-02-21 23:41:54 +01:00
}
2022-01-30 23:35:51 +00:00
NonnullRefPtr < Range > Range : : create ( Node & start_container , u32 start_offset , Node & end_container , u32 end_offset )
2021-02-21 23:41:54 +01:00
{
2021-04-23 16:46:57 +02:00
return adopt_ref ( * new Range ( start_container , start_offset , end_container , end_offset ) ) ;
2021-02-21 23:41:54 +01:00
}
2022-01-30 23:35:51 +00:00
2021-02-21 23:41:54 +01:00
NonnullRefPtr < Range > Range : : create_with_global_object ( Bindings : : WindowObject & window )
{
return Range : : create ( window . impl ( ) ) ;
}
Range : : Range ( Document & document )
: Range ( document , 0 , document , 0 )
2020-12-06 19:51:55 +01:00
{
}
2022-01-30 23:35:51 +00:00
Range : : Range ( Node & start_container , u32 start_offset , Node & end_container , u32 end_offset )
: AbstractRange ( start_container , start_offset , end_container , end_offset )
{
}
Range : : ~ Range ( )
2020-12-06 19:51:55 +01:00
{
}
2022-01-31 18:05:54 +00:00
// https://dom.spec.whatwg.org/#concept-range-root
Node & Range : : root ( )
{
// The root of a live range is the root of its start node.
return m_start_container - > root ( ) ;
}
Node const & Range : : root ( ) const
{
return m_start_container - > root ( ) ;
}
enum class RelativeBoundaryPointPosition {
Equal ,
Before ,
After ,
} ;
// https://dom.spec.whatwg.org/#concept-range-bp-position
static RelativeBoundaryPointPosition position_of_boundary_point_relative_to_other_boundary_point ( Node const & node_a , u32 offset_a , Node const & node_b , u32 offset_b )
{
// 1. Assert: nodeA and nodeB have the same root.
VERIFY ( & node_a . root ( ) = = & node_b . 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 )
return RelativeBoundaryPointPosition : : Equal ;
if ( offset_a < offset_b )
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 ( relative_position = = RelativeBoundaryPointPosition : : Before )
return RelativeBoundaryPointPosition : : After ;
if ( relative_position = = RelativeBoundaryPointPosition : : After )
return RelativeBoundaryPointPosition : : Before ;
}
// 4. If nodeA is an ancestor of nodeB:
if ( node_a . is_ancestor_of ( node_b ) ) {
// 1. Let child be nodeB.
NonnullRefPtr < Node > child = node_b ;
// 2. While child is not a child of nodeA, set child to its parent.
while ( ! node_a . 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 )
return RelativeBoundaryPointPosition : : After ;
}
// 5. Return before.
return RelativeBoundaryPointPosition : : Before ;
}
ExceptionOr < void > Range : : set_start_or_end ( Node & node , u32 offset , StartOrEnd start_or_end )
{
// To set the start or end of a range to a boundary point (node, offset), run these steps:
// 1. If node is a doctype, then throw an "InvalidNodeTypeError" DOMException.
if ( is < DocumentType > ( node ) )
return InvalidNodeTypeError : : create ( " Node cannot be a DocumentType. " ) ;
// 2. If offset is greater than node’ s length, then throw an "IndexSizeError" DOMException.
if ( offset > node . length ( ) )
return IndexSizeError : : create ( String : : formatted ( " Node does not contain a child at offset {} " , offset ) ) ;
// 3. Let bp be the boundary point (node, offset).
if ( start_or_end = = StartOrEnd : : Start ) {
// -> 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 ( ) ! = & node . root ( ) | | position_of_boundary_point_relative_to_other_boundary_point ( node , offset , m_end_container , m_end_offset ) = = RelativeBoundaryPointPosition : : After ) {
m_end_container = node ;
m_end_offset = offset ;
}
// 2. Set range’ s start to bp.
m_start_container = node ;
m_start_offset = offset ;
} else {
// -> If these steps were invoked as "set the end"
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 ( ) ! = & node . root ( ) | | position_of_boundary_point_relative_to_other_boundary_point ( node , offset , m_start_container , m_start_offset ) = = RelativeBoundaryPointPosition : : Before ) {
m_start_container = node ;
m_start_offset = offset ;
}
// 2. Set range’ s end to bp.
m_end_container = node ;
m_end_offset = offset ;
}
return { } ;
}
// https://dom.spec.whatwg.org/#concept-range-bp-set
ExceptionOr < void > Range : : set_start ( Node & node , u32 offset )
{
// The setStart(node, offset) method steps are to set the start of this to boundary point (node, offset).
return set_start_or_end ( node , offset , StartOrEnd : : Start ) ;
}
ExceptionOr < void > Range : : set_end ( Node & node , u32 offset )
{
// The setEnd(node, offset) method steps are to set the end of this to boundary point (node, offset).
return set_start_or_end ( node , offset , StartOrEnd : : End ) ;
}
2022-01-31 17:35:03 +00:00
// https://dom.spec.whatwg.org/#dom-range-setstartbefore
ExceptionOr < void > Range : : set_start_before ( Node & node )
{
// 1. Let parent be node’ s parent.
auto * parent = node . parent ( ) ;
// 2. If parent is null, then throw an "InvalidNodeTypeError" DOMException.
if ( ! parent )
return InvalidNodeTypeError : : create ( " Given node has no parent. " ) ;
// 3. Set the start of this to boundary point (parent, node’ s index).
return set_start_or_end ( * parent , node . index ( ) , StartOrEnd : : Start ) ;
}
// https://dom.spec.whatwg.org/#dom-range-setstartafter
ExceptionOr < void > Range : : set_start_after ( Node & node )
{
// 1. Let parent be node’ s parent.
auto * parent = node . parent ( ) ;
// 2. If parent is null, then throw an "InvalidNodeTypeError" DOMException.
if ( ! parent )
return InvalidNodeTypeError : : create ( " Given node has no parent. " ) ;
// 3. Set the start of this to boundary point (parent, node’ s index plus 1).
return set_start_or_end ( * parent , node . index ( ) + 1 , StartOrEnd : : Start ) ;
}
// https://dom.spec.whatwg.org/#dom-range-setendbefore
ExceptionOr < void > Range : : set_end_before ( Node & node )
{
// 1. Let parent be node’ s parent.
auto * parent = node . parent ( ) ;
// 2. If parent is null, then throw an "InvalidNodeTypeError" DOMException.
if ( ! parent )
return InvalidNodeTypeError : : create ( " Given node has no parent. " ) ;
// 3. Set the end of this to boundary point (parent, node’ s index).
return set_start_or_end ( * parent , node . index ( ) , StartOrEnd : : End ) ;
}
// https://dom.spec.whatwg.org/#dom-range-setendafter
ExceptionOr < void > Range : : set_end_after ( Node & node )
{
// 1. Let parent be node’ s parent.
auto * parent = node . parent ( ) ;
// 2. If parent is null, then throw an "InvalidNodeTypeError" DOMException.
if ( ! parent )
return InvalidNodeTypeError : : create ( " Given node has no parent. " ) ;
// 3. Set the end of this to boundary point (parent, node’ s index plus 1).
return set_start_or_end ( * parent , node . index ( ) + 1 , StartOrEnd : : End ) ;
}
2020-12-06 19:51:55 +01:00
NonnullRefPtr < Range > Range : : clone_range ( ) const
{
2021-04-23 16:46:57 +02:00
return adopt_ref ( * new Range ( const_cast < Node & > ( * m_start_container ) , m_start_offset , const_cast < Node & > ( * m_end_container ) , m_end_offset ) ) ;
2020-12-06 19:51:55 +01:00
}
NonnullRefPtr < Range > Range : : inverted ( ) const
{
2021-04-23 16:46:57 +02:00
return adopt_ref ( * new Range ( const_cast < Node & > ( * m_end_container ) , m_end_offset , const_cast < Node & > ( * m_start_container ) , m_start_offset ) ) ;
2020-12-06 19:51:55 +01:00
}
NonnullRefPtr < Range > Range : : normalized ( ) const
{
if ( m_start_container . ptr ( ) = = m_end_container . ptr ( ) ) {
if ( m_start_offset < = m_end_offset )
return clone_range ( ) ;
return inverted ( ) ;
}
if ( m_start_container - > is_before ( m_end_container ) )
return clone_range ( ) ;
return inverted ( ) ;
}
2022-02-25 20:45:03 +01:00
// https://dom.spec.whatwg.org/#dom-range-commonancestorcontainer
NonnullRefPtr < Node > Range : : common_ancestor_container ( ) const
{
// 1. Let container be start node.
auto container = m_start_container ;
// 2. While container is not an inclusive ancestor of end node, let container be container’ s parent.
while ( ! container - > is_inclusive_ancestor_of ( m_end_container ) ) {
VERIFY ( container - > parent ( ) ) ;
container = * container - > parent ( ) ;
}
// 3. Return container.
return container ;
}
2020-12-06 19:51:55 +01:00
}