@@ -85,11 +85,10 @@ pub const Range = struct {
8585 }
8686
8787 pub fn _setStart (self : * Range , node : * parser.Node , offset_ : i32 ) ! void {
88- const relative = self ._comparePoint (node , offset_ ) catch | err | switch (err ) {
88+ try ensureValidOffset (node , offset_ );
89+ const offset : u32 = @intCast (offset_ );
90+ const position = compare (node , offset , self .proto .start_container , self .proto .start_offset ) catch | err | switch (err ) {
8991 error .WrongDocument = > blk : {
90- // comparePoint doesn't check this on WrongDocument.
91- try ensureValidOffset (node , offset_ );
92-
9392 // allow a node with a different root than the current, or
9493 // a disconnected one. Treat it as if it's "after", so that
9594 // we also update the end_offset and end_container.
@@ -98,8 +97,7 @@ pub const Range = struct {
9897 else = > return err ,
9998 };
10099
101- const offset : u32 = @intCast (offset_ );
102- if (relative == 1 ) {
100+ if (position == 1 ) {
103101 // if we're setting the node after the current start, the end must
104102 // be set too.
105103 self .proto .end_offset = offset ;
@@ -123,11 +121,11 @@ pub const Range = struct {
123121 }
124122
125123 pub fn _setEnd (self : * Range , node : * parser.Node , offset_ : i32 ) ! void {
126- const relative = self ._comparePoint (node , offset_ ) catch | err | switch (err ) {
127- error .WrongDocument = > blk : {
128- // comparePoint doesn't check this on WrongDocument.
129- try ensureValidOffset (node , offset_ );
124+ try ensureValidOffset (node , offset_ );
125+ const offset : u32 = @intCast (offset_ );
130126
127+ const position = compare (node , offset , self .proto .start_container , self .proto .start_offset ) catch | err | switch (err ) {
128+ error .WrongDocument = > blk : {
131129 // allow a node with a different root than the current, or
132130 // a disconnected one. Treat it as if it's "before", so that
133131 // we also update the end_offset and end_container.
@@ -136,10 +134,9 @@ pub const Range = struct {
136134 else = > return err ,
137135 };
138136
139- const offset : u32 = @intCast (offset_ );
140- if (relative == -1 ) {
137+ if (position == -1 ) {
141138 // if we're setting the node before the current start, the start
142- // must be
139+ // must be set too.
143140 self .proto .start_offset = offset ;
144141 self .proto .start_container = node ;
145142 }
@@ -207,101 +204,73 @@ pub const Range = struct {
207204 };
208205 }
209206
210- pub fn _comparePoint (self : * const Range , ref_node : * parser.Node , offset_ : i32 ) ! i32 {
207+ pub fn _comparePoint (self : * const Range , node : * parser.Node , offset_ : i32 ) ! i32 {
211208 const start = self .proto .start_container ;
212- if (try parser .nodeGetRootNode (start ) != try parser .nodeGetRootNode (ref_node )) {
209+ if (try parser .nodeGetRootNode (start ) != try parser .nodeGetRootNode (node )) {
213210 // WPT really wants this error to be first. Later, when we check
214211 // if the relative position is 'disconnected', it'll also catch this
215212 // case, but WPT will complain because it sometimes also sends
216213 // invalid offsets, and it wants WrongDocument to be raised.
217214 return error .WrongDocument ;
218215 }
219216
220- if (try parser .nodeType (ref_node ) == .document_type ) {
217+ if (try parser .nodeType (node ) == .document_type ) {
221218 return error .InvalidNodeType ;
222219 }
223220
224- try ensureValidOffset (ref_node , offset_ );
221+ try ensureValidOffset (node , offset_ );
225222
226223 const offset : u32 = @intCast (offset_ );
227- if (ref_node == start ) {
228- // This is a simple and common case, where the reference node and
229- // our start node are the same, so we just have to compare the offsets
230- const start_offset = self .proto .start_offset ;
231- if (offset == start_offset ) {
232- return 0 ;
233- }
234- return if (offset < start_offset ) -1 else 1 ;
235- }
236-
237- // We're probably comparing two different nodes. "Probably", because the
238- // above case on considered the offset if the two nodes were the same
239- // as-is. They could still be the same here, if we first consider the
240- // offset.
241- // Furthermore, as far as I can tell, if either or both nodes are textual,
242- // then we're doing a node comparison of their parents. This kind of
243- // makes sense, one/two text nodes which aren't the same, can only
244- // be positionally compared in relation to it/their parents.
245-
246- const adjusted_start = try getNodeForCompare (start , self .proto .start_offset );
247- const adjusted_ref_node = try getNodeForCompare (ref_node , offset );
248-
249- const relative = try Node ._compareDocumentPosition (adjusted_start , adjusted_ref_node );
250-
251- if (relative & @intFromEnum (parser .DocumentPosition .disconnected ) == @intFromEnum (parser .DocumentPosition .disconnected )) {
252- return error .WrongDocument ;
253- }
254-
255- if (relative & @intFromEnum (parser .DocumentPosition .preceding ) == @intFromEnum (parser .DocumentPosition .preceding )) {
224+ if (try compare (node , offset , start , self .proto .start_offset ) == -1 ) {
256225 return -1 ;
257226 }
258227
259- if (relative & @intFromEnum ( parser . DocumentPosition . following ) == @intFromEnum ( parser . DocumentPosition . following ) ) {
228+ if (try compare ( node , offset , self . proto . end_container , self . proto . end_offset ) == 1 ) {
260229 return 1 ;
261230 }
262231
263- // DUNNO
264- // unreachable??
265232 return 0 ;
266233 }
267234
268- pub fn _isPointInRange (self : * const Range , ref_node : * parser.Node , offset_ : i32 ) ! bool {
269- return self ._comparePoint (ref_node , offset_ ) catch | err | switch (err ) {
235+ pub fn _isPointInRange (self : * const Range , node : * parser.Node , offset_ : i32 ) ! bool {
236+ return self ._comparePoint (node , offset_ ) catch | err | switch (err ) {
270237 error .WrongDocument = > return false ,
271238 else = > return err ,
272239 } == 0 ;
273240 }
274241
242+ pub fn _intersectsNode (self : * const Range , node : * parser.Node ) ! bool {
243+ const start_root = try parser .nodeGetRootNode (self .proto .start_container );
244+ const node_root = try parser .nodeGetRootNode (node );
245+ if (start_root != node_root ) {
246+ return false ;
247+ }
248+
249+ const parent , const index = getParentAndIndex (node ) catch | err | switch (err ) {
250+ error .InvalidNodeType = > return true , // if node has no parent, we return true.
251+ else = > return err ,
252+ };
253+
254+ if (try compare (parent , index + 1 , self .proto .start_container , self .proto .start_offset ) != 1 ) {
255+ // node isn't after start, can't intersect
256+ return false ;
257+ }
258+
259+ if (try compare (parent , index , self .proto .end_container , self .proto .end_offset ) != -1 ) {
260+ // node isn't before end, can't intersect
261+ return false ;
262+ }
263+
264+ return true ;
265+ }
266+
275267 // The Range.detach() method does nothing. It used to disable the Range
276268 // object and enable the browser to release associated resources. The
277269 // method has been kept for compatibility.
278270 // https://developer.mozilla.org/en-US/docs/Web/API/Range/detach
279271 pub fn _detach (_ : * Range ) void {}
280272};
281273
282- fn getNodeForCompare (node : * parser.Node , offset : u32 ) ! * parser.Node {
283- if (try isTextual (node )) {
284- // when we're comparing a text node to another node which is not the same
285- // then we're really compare the position of the parent. It doesn't
286- // matter if the other node is a text node itself or not, all that matters
287- // is we're sure it isn't the same text node (because if they are the
288- // same text node, then we're comparing the offset (character position)
289- // of the text node)
290-
291- // not sure this is the correct error
292- return (try parser .nodeParentNode (node )) orelse return error .WrongDocument ;
293- }
294- if (offset == 0 ) {
295- return node ;
296- }
297-
298- const children = try parser .nodeGetChildNodes (node );
299-
300- // not sure about this error
301- // - 1 because, while the offset is 0 based, 0 seems to represent the parent
302- return (try parser .nodeListItem (children , offset - 1 )) orelse error .IndexSize ;
303- }
304-
305274fn ensureValidOffset (node : * parser.Node , offset : i32 ) ! void {
306275 if (offset < 0 ) {
307276 return error .IndexSize ;
@@ -347,6 +316,59 @@ fn getParentAndIndex(child: *parser.Node) !struct { *parser.Node, u32 } {
347316 return error .InvalidNodeType ;
348317}
349318
319+ // implementation is largely copied from the WPT helper called getPosition in
320+ // the common.js of the dom folder.
321+ fn compare (node_a : * parser.Node , offset_a : u32 , node_b : * parser.Node , offset_b : u32 ) ! i32 {
322+ if (node_a == node_b ) {
323+ // This is a simple and common case, where the two nodes are the same
324+ // We just need to compare their offsets
325+ if (offset_a == offset_b ) {
326+ return 0 ;
327+ }
328+ return if (offset_a < offset_b ) -1 else 1 ;
329+ }
330+
331+ // We're probably comparing two different nodes. "Probably", because the
332+ // above case on considered the offset if the two nodes were the same
333+ // as-is. They could still be the same here, if we first consider the
334+ // offset.
335+ const position = try Node ._compareDocumentPosition (node_b , node_a );
336+ if (position & @intFromEnum (parser .DocumentPosition .disconnected ) == @intFromEnum (parser .DocumentPosition .disconnected )) {
337+ return error .WrongDocument ;
338+ }
339+
340+ if (position & @intFromEnum (parser .DocumentPosition .following ) == @intFromEnum (parser .DocumentPosition .following )) {
341+ return switch (try compare (node_b , offset_b , node_a , offset_a )) {
342+ -1 = > 1 ,
343+ 1 = > -1 ,
344+ else = > unreachable ,
345+ };
346+ }
347+
348+ if (position & @intFromEnum (parser .DocumentPosition .contains ) == @intFromEnum (parser .DocumentPosition .contains )) {
349+ // node_a contains node_b
350+ var child = node_b ;
351+ while (try parser .nodeParentNode (child )) | parent | {
352+ if (parent == node_a ) {
353+ // child.parentNode == node_a
354+ break ;
355+ }
356+ child = parent ;
357+ } else {
358+ // this should not happen, because Node._compareDocumentPosition
359+ // has told us that node_a contains node_b, so one of node_b's
360+ // parent's MUST be node_a. But somehow we do end up here sometimes.
361+ return -1 ;
362+ }
363+
364+ const child_parent , const child_index = try getParentAndIndex (child );
365+ std .debug .assert (node_a == child_parent );
366+ return if (child_index < offset_a ) -1 else 1 ;
367+ }
368+
369+ return -1 ;
370+ }
371+
350372const testing = @import ("../../testing.zig" );
351373test "Browser.Range" {
352374 var runner = try testing .jsRunner (testing .tracking_allocator , .{});
0 commit comments