Skip to content

Commit 22e546a

Browse files
committed
servo: Merge #6625 - Implement Range#cloneContents (from dzbarsky:range-clonecontents); r=Ms2ger
Source-Repo: https://github.com/servo/servo Source-Revision: a0cf597946446c427c54da363fe989ff68db4270 UltraBlame original commit: 8b4c5111a66d6d0fa67b73323ab124215a3986a4
1 parent 75bbe3a commit 22e546a

File tree

2 files changed

+164
-3
lines changed

2 files changed

+164
-3
lines changed

servo/components/script/dom/range.rs

Lines changed: 162 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,25 @@
22

33

44

5+
use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
56
use dom::bindings::codegen::Bindings::NodeBinding::NodeConstants;
67
use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
78
use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
89
use dom::bindings::codegen::Bindings::RangeBinding::{self, RangeConstants};
910
use dom::bindings::codegen::Bindings::RangeBinding::RangeMethods;
1011
use dom::bindings::codegen::Bindings::TextBinding::TextMethods;
1112
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
12-
use dom::bindings::codegen::InheritTypes::{NodeCast, TextCast};
13+
use dom::bindings::codegen::InheritTypes::{CharacterDataCast, NodeCast, TextCast};
1314
use dom::bindings::error::{Error, ErrorResult, Fallible};
1415
use dom::bindings::error::Error::HierarchyRequest;
1516
use dom::bindings::global::GlobalRef;
1617
use dom::bindings::js::{JS, Root, RootedReference};
1718
use dom::bindings::utils::{Reflector, reflect_dom_object};
1819
use dom::characterdata::CharacterDataTypeId;
1920
use dom::document::{Document, DocumentHelpers};
21+
use dom::documentfragment::DocumentFragment;
2022
use dom::node::{Node, NodeHelpers, NodeTypeId};
23+
2124
use std::cell::RefCell;
2225
use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
2326
use std::rc::Rc;
@@ -60,6 +63,25 @@ impl Range {
6063
let document = global.as_window().Document();
6164
Ok(Range::new_with_doc(document.r()))
6265
}
66+
67+
68+
fn contains(&self, node: &Node) -> bool {
69+
let inner = self.inner.borrow();
70+
let start = &inner.start;
71+
let end = &inner.end;
72+
match (bp_position(node, 0, start.node().r(), start.offset()),
73+
bp_position(node, node.len(), end.node().r(), end.offset())) {
74+
(Some(Ordering::Greater), Some(Ordering::Less)) => true,
75+
_ => false
76+
}
77+
}
78+
79+
80+
fn partially_contains(&self, node: &Node) -> bool {
81+
let inner = self.inner.borrow();
82+
inner.start.node().inclusive_ancestors().any(|n| n.r() == node) !=
83+
inner.end.node().inclusive_ancestors().any(|n| n.r() == node)
84+
}
6385
}
6486

6587
pub trait RangeHelpers<'a> {
@@ -288,6 +310,145 @@ impl<'a> RangeMethods for &'a Range {
288310
}
289311

290312

313+
314+
fn CloneContents(self) -> Fallible<Root<DocumentFragment>> {
315+
let inner = self.inner.borrow();
316+
let start = &inner.start;
317+
let end = &inner.end;
318+
319+
320+
let start_node = start.node();
321+
let start_offset = start.offset();
322+
let end_node = end.node();
323+
let end_offset = end.offset();
324+
325+
326+
let fragment = DocumentFragment::new(start_node.owner_doc().r());
327+
328+
329+
if start == end {
330+
return Ok(fragment);
331+
}
332+
333+
if end_node == start_node {
334+
if let Some(text) = CharacterDataCast::to_ref(start_node.r()) {
335+
336+
let clone = start_node.CloneNode(true);
337+
338+
let text = text.SubstringData(start_offset, end_offset - start_offset);
339+
CharacterDataCast::to_ref(clone.r()).unwrap().SetData(text.unwrap());
340+
341+
try!(NodeCast::from_ref(fragment.r()).AppendChild(clone.r()));
342+
343+
return Ok(fragment);
344+
}
345+
}
346+
347+
348+
let common_ancestor = self.CommonAncestorContainer();
349+
350+
let first_contained_child =
351+
if start_node.is_inclusive_ancestor_of(end_node.r()) {
352+
353+
None
354+
} else {
355+
356+
common_ancestor.children()
357+
.find(|node| Range::partially_contains(self, node))
358+
};
359+
360+
let last_contained_child =
361+
if end_node.is_inclusive_ancestor_of(start_node.r()) {
362+
363+
None
364+
} else {
365+
366+
common_ancestor.rev_children()
367+
.find(|node| Range::partially_contains(self, node))
368+
};
369+
370+
371+
let contained_children =
372+
common_ancestor.children().filter(|n| Range::contains(self, n));
373+
374+
375+
if common_ancestor.children()
376+
.filter(|n| Range::contains(self, n))
377+
.any(|n| n.is_doctype()) {
378+
return Err(HierarchyRequest);
379+
}
380+
381+
if let Some(child) = first_contained_child {
382+
383+
if let Some(text) = CharacterDataCast::to_ref(child.r()) {
384+
assert!(child == start_node);
385+
386+
let clone = start_node.CloneNode(true);
387+
388+
let text = text.SubstringData(start_offset, start_node.len() - start_offset);
389+
CharacterDataCast::to_ref(clone.r()).unwrap().SetData(text.unwrap());
390+
391+
try!(NodeCast::from_ref(fragment.r()).AppendChild(clone.r()));
392+
} else {
393+
394+
let clone = child.CloneNode(false);
395+
396+
try!(NodeCast::from_ref(fragment.r()).AppendChild(clone.r()));
397+
398+
let subrange = Range::new(clone.owner_doc().r(),
399+
start_node.r(),
400+
start_offset,
401+
child.r(),
402+
child.len());
403+
404+
let subfragment = try!(subrange.CloneContents());
405+
406+
try!(clone.AppendChild(NodeCast::from_ref(subfragment.r())));
407+
}
408+
}
409+
410+
411+
for child in contained_children {
412+
413+
let clone = child.CloneNode(true);
414+
415+
try!(NodeCast::from_ref(fragment.r()).AppendChild(clone.r()));
416+
}
417+
418+
if let Some(child) = last_contained_child {
419+
420+
if let Some(text) = CharacterDataCast::to_ref(child.r()) {
421+
assert!(child == end_node);
422+
423+
let clone = end_node.CloneNode(true);
424+
425+
let text = text.SubstringData(0, end_offset);
426+
CharacterDataCast::to_ref(clone.r()).unwrap().SetData(text.unwrap());
427+
428+
try!(NodeCast::from_ref(fragment.r()).AppendChild(clone.r()));
429+
} else {
430+
431+
let clone = child.CloneNode(false);
432+
433+
try!(NodeCast::from_ref(fragment.r()).AppendChild(clone.r()));
434+
435+
let subrange = Range::new(clone.owner_doc().r(),
436+
child.r(),
437+
0,
438+
end_node.r(),
439+
end_offset);
440+
441+
let subfragment = try!(subrange.CloneContents());
442+
443+
try!(clone.AppendChild(NodeCast::from_ref(subfragment.r())));
444+
}
445+
}
446+
447+
448+
Ok(fragment)
449+
}
450+
451+
291452
fn Detach(self) {
292453

293454
}

servo/components/script/dom/webidls/Range.webidl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ interface Range {
4646
// void deleteContents();
4747
// [NewObject, Throws]
4848
// DocumentFragment extractContents();
49-
// [NewObject, Throws]
50-
// DocumentFragment cloneContents();
49+
[NewObject, Throws]
50+
DocumentFragment cloneContents();
5151
[Throws]
5252
void insertNode(Node node);
5353
// [Throws]

0 commit comments

Comments
 (0)