/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use dom::bindings::codegen::Bindings::NodeBinding::NodeConstants; use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods; use dom::bindings::codegen::Bindings::RangeBinding::{self, RangeConstants}; use dom::bindings::codegen::Bindings::RangeBinding::RangeMethods; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::codegen::InheritTypes::NodeCast; use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::global::GlobalRef; use dom::bindings::js::{JS, JSRef, MutHeap, Rootable, Temporary}; use dom::bindings::utils::{Reflector, reflect_dom_object}; use dom::document::{Document, DocumentHelpers}; use dom::node::{Node, NodeHelpers}; use std::cell::RefCell; use std::cmp::{Ord, Ordering, PartialEq, PartialOrd}; use std::rc::Rc; #[dom_struct] pub struct Range { reflector_: Reflector, inner: Rc>, } impl Range { fn new_inherited(start_container: JSRef, start_offset: u32, end_container: JSRef, end_offset: u32) -> Range { Range { reflector_: Reflector::new(), inner: Rc::new(RefCell::new(RangeInner::new( BoundaryPoint::new(start_container, start_offset), BoundaryPoint::new(end_container, end_offset)))), } } pub fn new_with_doc(document: JSRef) -> Temporary { let root = NodeCast::from_ref(document); Range::new(document, root, 0, root, 0) } pub fn new(document: JSRef, start_container: JSRef, start_offset: u32, end_container: JSRef, end_offset: u32) -> Temporary { let window = document.window().root(); reflect_dom_object(box Range::new_inherited(start_container, start_offset, end_container, end_offset), GlobalRef::Window(window.r()), RangeBinding::Wrap) } // https://dom.spec.whatwg.org/#dom-range pub fn Constructor(global: GlobalRef) -> Fallible> { let document = global.as_window().Document().root(); Ok(Range::new_with_doc(document.r())) } } pub trait RangeHelpers<'a> { fn inner(self) -> &'a Rc>; } impl<'a> RangeHelpers<'a> for JSRef<'a, Range> { fn inner(self) -> &'a Rc> { &self.extended_deref().inner } } impl<'a> RangeMethods for JSRef<'a, Range> { // http://dom.spec.whatwg.org/#dom-range-startcontainer fn StartContainer(self) -> Temporary { self.inner().borrow().start.node() } /// http://dom.spec.whatwg.org/#dom-range-startoffset fn StartOffset(self) -> u32 { self.inner().borrow().start.offset } /// http://dom.spec.whatwg.org/#dom-range-endcontainer fn EndContainer(self) -> Temporary { self.inner().borrow().end.node() } /// http://dom.spec.whatwg.org/#dom-range-endoffset fn EndOffset(self) -> u32 { self.inner().borrow().end.offset } // https://dom.spec.whatwg.org/#dom-range-collapsed fn Collapsed(self) -> bool { let inner = self.inner().borrow(); inner.start == inner.end } // https://dom.spec.whatwg.org/#dom-range-commonancestorcontainer fn CommonAncestorContainer(self) -> Temporary { self.inner().borrow().common_ancestor_container() } // https://dom.spec.whatwg.org/#dom-range-setstartnode-offset fn SetStart(self, node: JSRef, offset: u32) -> ErrorResult { if node.is_doctype() { // Step 1. Err(Error::InvalidNodeType) } else if offset > node.len() { // Step 2. Err(Error::IndexSize) } else { // Step 3-4. self.inner().borrow_mut().set_start(node, offset); Ok(()) } } // https://dom.spec.whatwg.org/#dom-range-setendnode-offset fn SetEnd(self, node: JSRef, offset: u32) -> ErrorResult { if node.is_doctype() { // Step 1. Err(Error::InvalidNodeType) } else if offset > node.len() { // Step 2. Err(Error::IndexSize) } else { // Step 3-4. self.inner().borrow_mut().set_end(node, offset); Ok(()) } } // https://dom.spec.whatwg.org/#dom-range-setstartbeforenode fn SetStartBefore(self, node: JSRef) -> ErrorResult { let parent = try!(node.GetParentNode().ok_or(Error::InvalidNodeType)).root(); self.SetStart(parent.r(), node.index()) } // https://dom.spec.whatwg.org/#dom-range-setstartafternode fn SetStartAfter(self, node: JSRef) -> ErrorResult { let parent = try!(node.GetParentNode().ok_or(Error::InvalidNodeType)).root(); self.SetStart(parent.r(), node.index() + 1) } // https://dom.spec.whatwg.org/#dom-range-setendbeforenode fn SetEndBefore(self, node: JSRef) -> ErrorResult { let parent = try!(node.GetParentNode().ok_or(Error::InvalidNodeType)).root(); self.SetEnd(parent.r(), node.index()) } // https://dom.spec.whatwg.org/#dom-range-setendafternode fn SetEndAfter(self, node: JSRef) -> ErrorResult { let parent = try!(node.GetParentNode().ok_or(Error::InvalidNodeType)).root(); self.SetEnd(parent.r(), node.index() + 1) } // https://dom.spec.whatwg.org/#dom-range-collapsetostart fn Collapse(self, to_start: bool) { self.inner().borrow_mut().collapse(to_start); } // https://dom.spec.whatwg.org/#dom-range-selectnodenode fn SelectNode(self, node: JSRef) -> ErrorResult { self.inner().borrow_mut().select_node(node) } // https://dom.spec.whatwg.org/#dom-range-selectnodecontentsnode fn SelectNodeContents(self, node: JSRef) -> ErrorResult { self.inner().borrow_mut().select_node_contents(node) } // https://dom.spec.whatwg.org/#dom-range-compareboundarypointshow-sourcerange fn CompareBoundaryPoints(self, how: u16, source_range: JSRef) -> Fallible { if how > RangeConstants::END_TO_START { // Step 1. return Err(Error::NotSupported); } let this_inner = self.inner().borrow(); let other_inner = source_range.inner().borrow(); let this_start_node = this_inner.start.node().root(); let other_start_node = other_inner.start.node().root(); let this_root = this_start_node.r().inclusive_ancestors().last().unwrap(); let other_root = other_start_node.r().inclusive_ancestors().last().unwrap(); if this_root != other_root { // Step 2. return Err(Error::WrongDocument); } // Step 3. let (this_point, other_point) = match how { RangeConstants::START_TO_START => { (&this_inner.start, &other_inner.start) }, RangeConstants::START_TO_END => { (&this_inner.end, &other_inner.start) }, RangeConstants::END_TO_END => { (&this_inner.end, &other_inner.end) }, RangeConstants::END_TO_START => { (&this_inner.start, &other_inner.end) }, _ => unreachable!(), }; // step 4. match this_point.partial_cmp(other_point).unwrap() { Ordering::Less => Ok(-1), Ordering::Equal => Ok(0), Ordering::Greater => Ok(1), } } // https://dom.spec.whatwg.org/#dom-range-clonerange fn CloneRange(self) -> Temporary { let inner = self.inner().borrow(); let start = &inner.start; let end = &inner.end; let start_node = start.node().root(); let owner_doc = NodeCast::from_ref(start_node.r()).owner_doc().root(); Range::new(owner_doc.r(), start_node.r(), start.offset, end.node().root().r(), end.offset) } // https://dom.spec.whatwg.org/#dom-range-ispointinrangenode-offset fn IsPointInRange(self, node: JSRef, offset: u32) -> Fallible { match self.inner().borrow().compare_point(node, offset) { Ok(Ordering::Less) => Ok(false), Ok(Ordering::Equal) => Ok(true), Ok(Ordering::Greater) => Ok(false), Err(Error::WrongDocument) => { // Step 2. Ok(false) } Err(error) => Err(error), } } // https://dom.spec.whatwg.org/#dom-range-comparepointnode-offset fn ComparePoint(self, node: JSRef, offset: u32) -> Fallible { self.inner().borrow().compare_point(node, offset).map(|order| { match order { Ordering::Less => -1, Ordering::Equal => 0, Ordering::Greater => 1, } }) } // https://dom.spec.whatwg.org/#dom-range-intersectsnodenode fn IntersectsNode(self, node: JSRef) -> bool { let inner = self.inner().borrow(); let start = &inner.start; let start_node = start.node().root(); let start_offset = start.offset; let start_node_root = start_node.r().inclusive_ancestors().last().unwrap().root(); let node_root = node.inclusive_ancestors().last().unwrap().root(); if start_node_root.r() != node_root.r() { // Step 1. return false; } let parent = match node.GetParentNode() { Some(parent) => parent, None => { // Step 3. return true; }, }.root(); // Step 4. let offset = node.index(); let end = &inner.end; let end_node = end.node().root(); let end_offset = end.offset; match (bp_position(parent.r(), offset + 1, start_node.r(), start_offset).unwrap(), bp_position(parent.r(), offset, end_node.r(), end_offset).unwrap()) { (Ordering::Greater, Ordering::Less) => { // Step 5. true }, _ => { // Step 6. false } } } // http://dom.spec.whatwg.org/#dom-range-detach fn Detach(self) { // This method intentionally left blank. } } #[jstraceable] #[must_root] #[privatize] pub struct RangeInner { start: BoundaryPoint, end: BoundaryPoint, } impl RangeInner { fn new(start: BoundaryPoint, end: BoundaryPoint) -> RangeInner { RangeInner { start: start, end: end } } // https://dom.spec.whatwg.org/#dom-range-commonancestorcontainer fn common_ancestor_container(&self) -> Temporary { let start_container = self.start.node().root(); let end_container = self.end.node().root(); // Step 1. for container in start_container.r().inclusive_ancestors() { // Step 2. if container.root().r().is_inclusive_ancestor_of(end_container.r()) { // Step 3. return container; } } unreachable!(); } // https://dom.spec.whatwg.org/#concept-range-bp-set pub fn set_start(&mut self, bp_node: JSRef, bp_offset: u32) { // Steps 1-3 handled in Range caller. let end_node = self.end.node().root(); let end_offset = self.end.offset; match bp_position(bp_node, bp_offset, end_node.r(), end_offset) { None | Some(Ordering::Greater) => { // Step 4-1. self.end.set(bp_node, bp_offset); }, _ => {}, }; // Step 4-2. self.start.set(bp_node, bp_offset); } // https://dom.spec.whatwg.org/#concept-range-bp-set pub fn set_end(&mut self, bp_node: JSRef, bp_offset: u32) { // Steps 1-3 handled in Range caller. let start_node = self.start.node().root(); let start_offset = self.start.offset; match bp_position(bp_node, bp_offset, start_node.r(), start_offset) { None | Some(Ordering::Less) => { // Step 4-1. self.start.set(bp_node, bp_offset); }, _ => {}, }; // Step 4-2. self.end.set(bp_node, bp_offset); } // https://dom.spec.whatwg.org/#dom-range-collapsetostart fn collapse(&mut self, to_start: bool) { if to_start { let start_node = self.start.node().root(); self.end.set(start_node.r(), self.start.offset); } else { let end_node = self.end.node().root(); self.start.set(end_node.r(), self.end.offset); } } // https://dom.spec.whatwg.org/#dom-range-selectnodenode fn select_node(&mut self, node: JSRef) -> ErrorResult { // Steps 1, 2. let parent = try!(node.GetParentNode().ok_or(Error::InvalidNodeType)).root(); // Step 3. let index = node.index(); // Step 4. self.start.set(parent.r(), index); // Step 5. self.end.set(parent.r(), index + 1); Ok(()) } // https://dom.spec.whatwg.org/#dom-range-selectnodecontentsnode fn select_node_contents(&mut self, node: JSRef) -> ErrorResult { if node.is_doctype() { // Step 1. return Err(Error::InvalidNodeType); } // Step 2. let length = node.len(); // Step 3. self.start.set(node, 0); // Step 4. self.end.set(node, length); Ok(()) } // https://dom.spec.whatwg.org/#dom-range-comparepointnode-offset fn compare_point(&self, node: JSRef, offset: u32) -> Fallible { let start = &self.start; let start_node = start.node().root(); let start_offset = start.offset; let start_node_root = start_node.r().inclusive_ancestors().last().unwrap().root(); let node_root = node.inclusive_ancestors().last().unwrap().root(); if start_node_root.r() != node_root.r() { // Step 1. return Err(Error::WrongDocument); } if node.is_doctype() { // Step 2. return Err(Error::InvalidNodeType); } if offset > node.len() { // Step 3. return Err(Error::IndexSize); } if let Ordering::Less = bp_position(node, offset, start_node.r(), start_offset).unwrap() { // Step 4. return Ok(Ordering::Less); } let end = &self.end; let end_node = end.node().root(); let end_offset = end.offset; if let Ordering::Greater = bp_position(node, offset, end_node.r(), end_offset).unwrap() { // Step 5. return Ok(Ordering::Greater); } // Step 6. Ok(Ordering::Equal) } } #[jstraceable] #[must_root] #[privatize] pub struct BoundaryPoint { node: MutHeap>, offset: u32, } impl BoundaryPoint { fn new(node: JSRef, offset: u32) -> BoundaryPoint { debug_assert!(!node.is_doctype()); debug_assert!(offset <= node.len()); BoundaryPoint { node: MutHeap::new(JS::from_rooted(node)), offset: offset, } } pub fn node(&self) -> Temporary { Temporary::from_rooted(self.node.get()) } pub fn offset(&self) -> u32 { self.offset } fn set(&mut self, node: JSRef, offset: u32) { debug_assert!(!node.is_doctype()); debug_assert!(offset <= node.len()); self.node.set(JS::from_rooted(node)); self.offset = offset; } } #[allow(unrooted_must_root)] impl PartialOrd for BoundaryPoint { fn partial_cmp(&self, other: &Self) -> Option { bp_position(self.node().root().r(), self.offset, other.node().root().r(), other.offset) } } #[allow(unrooted_must_root)] impl PartialEq for BoundaryPoint { fn eq(&self, other: &Self) -> bool { self.node().root().r() == other.node().root().r() && self.offset == other.offset } } // https://dom.spec.whatwg.org/#concept-range-bp-position fn bp_position(a_node: JSRef, a_offset: u32, b_node: JSRef, b_offset: u32) -> Option { if a_node == b_node { // Step 1. return Some(a_offset.cmp(&b_offset)); } let position = b_node.CompareDocumentPosition(a_node); if position & NodeConstants::DOCUMENT_POSITION_DISCONNECTED != 0 { // No order is defined for nodes not in the same tree. None } else if position & NodeConstants::DOCUMENT_POSITION_FOLLOWING != 0 { // Step 2. match bp_position(b_node, b_offset, a_node, a_offset).unwrap() { Ordering::Less => Some(Ordering::Greater), Ordering::Greater => Some(Ordering::Less), Ordering::Equal => unreachable!(), } } else if position & NodeConstants::DOCUMENT_POSITION_CONTAINS != 0 { // Step 3-1, 3-2. let b_ancestors = b_node.inclusive_ancestors(); let ref child = b_ancestors.map(|child| child.root()).find(|child| { child.r().GetParentNode().unwrap().root().r() == a_node }).unwrap(); // Step 3-3. if child.r().index() < a_offset { Some(Ordering::Greater) } else { // Step 4. Some(Ordering::Less) } } else { // Step 4. Some(Ordering::Less) } }