Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make ranges observe mutations on document #6639

Closed
wants to merge 7 commits into from
Prev

Make range observe mutations on document

  • Loading branch information
dzbarsky committed Jul 15, 2015
commit 976063e61b47c046b0597e3fea6a397d16349c17
@@ -68,7 +68,7 @@ use std::ffi::CString;
use std::hash::{Hash, Hasher};
use std::intrinsics::return_address;
use std::ops::{Deref, DerefMut};
use std::rc::Rc;
use std::rc::{Rc, Weak};
use std::sync::Arc;
use std::sync::mpsc::{Receiver, Sender};
use string_cache::{Atom, Namespace};
@@ -136,24 +136,33 @@ pub fn trace_object(tracer: *mut JSTracer, description: &str, obj: &Heap<*mut JS
}
}

impl<T: JSTraceable> JSTraceable for RefCell<T> {
impl<T: JSTraceable + ?Sized> JSTraceable for RefCell<T> {
fn trace(&self, trc: *mut JSTracer) {
self.borrow().trace(trc)
}
}

impl<T: JSTraceable> JSTraceable for Rc<T> {
impl<T: JSTraceable + ?Sized> JSTraceable for Rc<T> {
fn trace(&self, trc: *mut JSTracer) {
(**self).trace(trc)
}
}

impl<T: JSTraceable> JSTraceable for Box<T> {
impl<T: JSTraceable + ?Sized> JSTraceable for Box<T> {
fn trace(&self, trc: *mut JSTracer) {
(**self).trace(trc)
}
}

impl<T: JSTraceable + ?Sized> JSTraceable for Weak<T> {
fn trace(&self, trc: *mut JSTracer) {
match self.upgrade() {
Some(ptr) => ptr.trace(trc),
_ => ()
}
}
}

impl<T: JSTraceable> JSTraceable for *const T {
fn trace(&self, trc: *mut JSTracer) {
if !self.is_null() {
@@ -111,7 +111,9 @@ impl<'a> CharacterDataMethods for &'a CharacterData {
data.push_str(&arg);
data.push_str(&self.data.borrow().slice_chars((offset + count) as usize, length as usize));
*self.data.borrow_mut() = data;
// FIXME: Once we have `Range`, we should implement step7 to step11

let node = NodeCast::from_ref(self);
node.owner_doc().character_data_replaced(node, offset, count, arg);
Ok(())
}

@@ -59,6 +59,7 @@ use dom::nodelist::NodeList;
use dom::nodeiterator::NodeIterator;
use dom::text::Text;
use dom::processinginstruction::ProcessingInstruction;
use dom::range::MutationObserver;
use dom::range::Range;
use dom::servohtmlparser::ServoHTMLParser;
use dom::treewalker::TreeWalker;
@@ -97,7 +98,7 @@ use std::cell::{Cell, Ref, RefMut, RefCell};
use std::default::Default;
use std::ptr;
use std::sync::mpsc::channel;
use std::rc::Rc;
use std::rc::{Rc, Weak};
use time;

#[derive(JSTraceable, PartialEq)]
@@ -149,6 +150,8 @@ pub struct Document {
current_parser: MutNullableHeap<JS<ServoHTMLParser>>,
/// When we should kick off a reflow. This happens during parsing.
reflow_timeout: Cell<Option<u64>>,
/// List of mutation observers
observers: RefCell<Vec<Weak<RefCell<MutationObserver>>>>,
}

impl PartialEq for Document {
@@ -1072,6 +1075,7 @@ impl Document {
loader: DOMRefCell::new(doc_loader),
current_parser: Default::default(),
reflow_timeout: Cell::new(None),
observers: RefCell::new(Vec::new()),
}
}

@@ -1104,6 +1108,51 @@ impl Document {
}
document
}

pub fn add_mutation_observer(&self, observer: Weak<RefCell<MutationObserver>>) {
self.observers.borrow_mut().push(observer);
}

pub fn remove_mutation_observer(&self, observer: &MutationObserver) {
/*self.observers
.borrow_mut()
.retain(|o| &&*o.upgrade().unwrap().borrow() != &observer);*/
}

pub fn character_data_replaced(&self, node: &Node, offset: u32, count: u32, data: DOMString) {
for observer in self.observers.borrow().iter() {
let observer = observer.upgrade().unwrap();
observer.borrow_mut().character_data_replaced(node, offset, count, &data);
}
}

pub fn node_inserted(&self, parent: &Node, child: &Node, index: u32, count: u32) {
for observer in self.observers.borrow().iter() {
let observer = observer.upgrade().unwrap();
observer.borrow_mut().node_inserted(parent, child, index, count);
}
}

pub fn node_removed(&self, parent: &Node, child: &Node, index: u32) {
for observer in self.observers.borrow().iter() {
let observer = observer.upgrade().unwrap();
observer.borrow_mut().node_removed(parent, child, index);
}
}

pub fn text_will_split(&self, node: &Node, parent: &Node, offset: u32, new_node: &Node) {
for observer in self.observers.borrow().iter() {
let observer = observer.upgrade().unwrap();
observer.borrow_mut().text_will_split(node, parent, offset, new_node);
}
}

pub fn text_split(&self, node: &Node, offset: u32) {
for observer in self.observers.borrow().iter() {
let observer = observer.upgrade().unwrap();
observer.borrow_mut().text_split(node, offset);
}
}
}

trait PrivateDocumentHelpers {
@@ -1663,7 +1663,18 @@ impl Node {
}

// XXX assert owner_doc
// Step 1-3: ranges.
// Step 1.
let count = match node.type_id() {
NodeTypeId::DocumentFragment => node.children().count() as u32,
_ => 1
};

// Step 2.
if let Some(child) = child {
parent.owner_doc().node_inserted(parent, child, child.index(), count);
}

// Step 3: mutation records

match node.type_id() {
NodeTypeId::DocumentFragment => {
@@ -1766,8 +1777,12 @@ impl Node {
// https://dom.spec.whatwg.org/#concept-node-remove
fn remove(node: &Node, parent: &Node, _suppress_observers: SuppressObserver) {
assert!(node.GetParentNode().map_or(false, |node_parent| node_parent.r() == parent));
// Step 1.
let index = node.index();

// Steps 2-5
parent.owner_doc().node_removed(parent, node, index);

// Step 1-5: ranges.
// Step 6-7: mutation observers.
// Step 8.
parent.remove_child(node);
@@ -13,11 +13,14 @@ use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::error::Error::HierarchyRequest;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::trace::JSTraceable;
use dom::bindings::utils::{Reflector, reflect_dom_object};
use dom::document::{Document, DocumentHelpers};
use dom::documentfragment::DocumentFragment;
use dom::node::{Node, NodeHelpers, NodeTypeId};

use util::str::DOMString;

use std::cell::RefCell;
use std::cmp::{Ord, Ordering, PartialEq, PartialOrd};
use std::rc::Rc;
@@ -41,7 +44,9 @@ impl Range {

pub fn new_with_doc(document: &Document) -> Root<Range> {
let root = NodeCast::from_ref(document);
Range::new(document, root, 0, root, 0)
let range = Range::new(document, root, 0, root, 0);
document.add_mutation_observer(range.inner().downgrade());
range
}

pub fn new(document: &Document,
@@ -85,6 +90,12 @@ impl Range {
}
}

impl Drop for Range {
fn drop(&mut self) {
//self.StartContainer().owner_doc().remove_mutation_observer(self);
}
}

pub trait RangeHelpers<'a> {
fn inner(self) -> &'a Rc<RefCell<RangeInner>>;
}
@@ -313,21 +324,18 @@ impl<'a> RangeMethods for &'a Range {
// https://dom.spec.whatwg.org/#dom-range-clonecontents
// https://dom.spec.whatwg.org/#concept-range-clone
fn CloneContents(self) -> Fallible<Root<DocumentFragment>> {
let inner = self.inner.borrow();
let start = &inner.start;
let end = &inner.end;

// Step 3.
let start_node = start.node();
let start_offset = start.offset();
let end_node = end.node();
let end_offset = end.offset();
let start_node = self.StartContainer();
let start_offset = self.StartOffset();
let end_node = self.EndContainer();
let end_offset = self.EndOffset();

// Step 1.
let fragment = DocumentFragment::new(start_node.owner_doc().r());

// Step 2.
if start == end {
if self.Collapsed() {
return Ok(fragment);
}

@@ -783,6 +791,121 @@ impl RangeInner {
}
}

pub trait MutationObserver : JSTraceable {
fn character_data_replaced(&mut self, node: &Node, offset: u32, count: u32, data: &DOMString);
fn node_inserted(&mut self, parent: &Node, child: &Node, index: u32, count: u32);
fn node_removed(&mut self, parent: &Node, child: &Node, index: u32);
fn text_will_split(&mut self, node: &Node, parent: &Node, offset: u32, new_node: &Node);
fn text_split(&mut self, node: &Node, offset: u32);
}

impl MutationObserver for RangeInner {

// https://dom.spec.whatwg.org/#dom-characterdata-replacedata-offset-count-data-offset
fn character_data_replaced(&mut self, node: &Node, offset: u32, count: u32, data: &DOMString) {
let start_node = self.start.node();
let start_offset = self.start.offset;
let end_node = self.end.node();
let end_offset = self.end.offset;

// Step 8.
if start_node.r() == node && start_offset > offset && start_offset <= offset + count {
self.start.offset = offset;
}

// Step 9.
if end_node.r() == node && end_offset > offset && end_offset <= offset + count {
self.end.offset = offset;
}

// Step 10.
if start_node.r() == node && start_offset > offset + count {
self.start.offset =
(self.start.offset as i32 + data.len() as i32 - count as i32) as u32;
}

// Step 11.
if end_node.r() == node && end_offset > offset + count {
self.end.offset =
(self.end.offset as i32 + data.len() as i32 - count as i32) as u32;
}
}

// https://dom.spec.whatwg.org/#concept-node-insert
fn node_inserted(&mut self, parent: &Node, child: &Node, index: u32, count: u32) {
// Step 2.1.
if self.start.node().r() == parent && self.start.offset > index {
self.start.offset += count;
}

// Step 2.2.
if self.end.node().r() == parent && self.end.offset > index {
self.end.offset += count;
}
}

// https://dom.spec.whatwg.org/#concept-node-remove
fn node_removed(&mut self, parent: &Node, child: &Node, index: u32) {
// Step 2.
if child.is_inclusive_ancestor_of(self.start.node().r()) {
self.start.set(parent, index);
}

// Step 3.
if child.is_inclusive_ancestor_of(self.start.node().r()) {
self.end.set(parent, index);
}

// Step 4.
if self.start.node().r() == parent && self.start.offset > index {
self.start.offset -= 1;
}

// Step 5.
if self.end.node().r() == parent && self.end.offset > index {
self.end.offset -= 1;
}
}

// https://dom.spec.whatwg.org/#concept-Text-split
fn text_will_split(&mut self, node: &Node, parent: &Node, offset: u32, new_node: &Node) {
// Step 7.2.
if self.start.node().r() == node && self.start.offset > offset {
self.start.node = JS::from_ref(new_node);
self.start.offset -= offset;
}

// Step 7.3.
if self.end.node().r() == node && self.end.offset > offset {
self.end.node = JS::from_ref(new_node);
self.end.offset -= offset;
}

// Step 7.4.
if self.start.node().r() == parent && self.start.offset == node.index() + 1 {
self.start.offset += 1;
}

// Step 7.5.
if self.end.node().r() == parent && self.end.offset == node.index() + 1 {
self.end.offset += 1;
}
}

// https://dom.spec.whatwg.org/#concept-Text-split
fn text_split(&mut self, node: &Node, offset: u32) {
// Step 9.1.
if self.start.node().r() == node && self.start.offset > offset {
self.start.offset = offset;
}

// Step 9.2.
if self.end.node().r() == node && self.end.offset > offset {
self.end.offset = offset;
}
}
}

#[derive(JSTraceable)]
#[must_root]
#[privatize]
@@ -71,16 +71,19 @@ impl<'a> TextMethods for &'a Text {
let parent = node.GetParentNode();
if let Some(ref parent) = parent {
// Step 7.
parent.r().InsertBefore(NodeCast::from_ref(new_node.r()),
node.GetNextSibling().r())
parent.InsertBefore(NodeCast::from_ref(new_node.r()),
node.GetNextSibling().r())
.unwrap();
// TODO: Ranges.
parent.owner_doc().text_will_split(node,
parent,
offset,
NodeCast::from_ref(new_node.r()));
}
// Step 8.
cdata.DeleteData(offset, count).unwrap();
if parent.is_none() {
// Step 9.
// TODO: Ranges
node.owner_doc().text_split(node, offset);
}
// Step 10.
Ok(new_node)
@@ -19,6 +19,7 @@
#![feature(owned_ascii_ext)]
#![feature(plugin)]
#![feature(rc_unique)]
#![feature(rc_weak)]
#![feature(slice_chars)]
#![feature(str_utf16)]
#![feature(unicode)]
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.