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

Properly propagate changes when range or trees are mutated #8506

Merged
merged 6 commits into from Dec 25, 2015
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -18,6 +18,10 @@ DOMInterfaces = {
'outerObjectHook': 'Some(bindings::utils::outerize_global)',
},

'Range': {
'weakReferenceable': True,
},

#FIXME(jdm): This should be 'register': False, but then we don't generate enum types
'TestBinding': {},

@@ -19,7 +19,9 @@ use js::jsapi::{JSTracer, JS_GetReservedSlot, JS_SetReservedSlot};
use js::jsval::PrivateValue;
use libc::c_void;
use std::cell::{Cell, UnsafeCell};
use std::iter::Iterator;
use std::mem;
use std::ops::{Deref, DerefMut, Drop};
use util::mem::HeapSizeOf;

/// The index of the slot wherein a pointer to the weak holder cell is
@@ -113,6 +115,25 @@ impl<T: WeakReferenceable> HeapSizeOf for WeakRef<T> {
}
}

impl<T: WeakReferenceable> PartialEq for WeakRef<T> {
fn eq(&self, other: &Self) -> bool {
unsafe {
(**self.ptr).value.get() == (**other.ptr).value.get()
}
}
}

impl<T: WeakReferenceable> PartialEq<T> for WeakRef<T> {
fn eq(&self, other: &T) -> bool {
unsafe {
match (**self.ptr).value.get() {
Some(ptr) => *ptr == other,
None => false,
}
}
}
}

no_jsmanaged_fields!(WeakRef<T: WeakReferenceable>);

impl<T: WeakReferenceable> Drop for WeakRef<T> {
@@ -182,3 +203,81 @@ impl<T: WeakReferenceable> JSTraceable for MutableWeakRef<T> {
}
}
}

/// A vector of weak references. On tracing, the vector retains
/// only references which still point to live objects.
#[allow_unrooted_interior]
#[derive(HeapSizeOf)]
pub struct WeakRefVec<T: WeakReferenceable> {
vec: Vec<WeakRef<T>>,
}

impl<T: WeakReferenceable> WeakRefVec<T> {
/// Create a new vector of weak references.
pub fn new() -> Self {
WeakRefVec { vec: vec![] }
}

/// Calls a function on each reference which still points to a
/// live object. The order of the references isn't preserved.
pub fn update<F: FnMut(WeakRefEntry<T>)>(&mut self, mut f: F) {
let mut i = 0;
while i < self.vec.len() {
if self.vec[i].is_alive() {
f(WeakRefEntry { vec: self, index: &mut i });
} else {
self.vec.swap_remove(i);
}
}
}

/// Clears the vector of its dead references.
pub fn retain_alive(&mut self) {
self.update(|_| ());
}
}

impl<T: WeakReferenceable> Deref for WeakRefVec<T> {
type Target = Vec<WeakRef<T>>;

fn deref(&self) -> &Vec<WeakRef<T>> {
&self.vec
}
}

impl<T: WeakReferenceable> DerefMut for WeakRefVec<T> {
fn deref_mut(&mut self) -> &mut Vec<WeakRef<T>> {
&mut self.vec
}
}

/// An entry of a vector of weak references. Passed to the closure
/// given to `WeakRefVec::update`.
#[allow_unrooted_interior]
pub struct WeakRefEntry<'a, T: WeakReferenceable + 'a> {
vec: &'a mut WeakRefVec<T>,
index: &'a mut usize,
}

impl<'a, T: WeakReferenceable + 'a> WeakRefEntry<'a, T> {
/// Remove the entry from the underlying vector of weak references.
pub fn remove(self) -> WeakRef<T> {
let ref_ = self.vec.swap_remove(*self.index);
mem::forget(self);
ref_
}
}

impl<'a, T: WeakReferenceable + 'a> Deref for WeakRefEntry<'a, T> {
type Target = WeakRef<T>;

fn deref(&self) -> &WeakRef<T> {
&self.vec[*self.index]
}
}

impl<'a, T: WeakReferenceable + 'a> Drop for WeakRefEntry<'a, T> {
fn drop(&mut self) {
*self.index += 1;
}
}
@@ -6,13 +6,18 @@

use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
use dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods;
use dom::bindings::codegen::InheritTypes::{CharacterDataTypeId, NodeTypeId};
use dom::bindings::codegen::UnionTypes::NodeOrString;
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::inheritance::Castable;
use dom::bindings::js::{LayoutJS, Root};
use dom::comment::Comment;
use dom::document::Document;
use dom::element::Element;
use dom::node::{Node, NodeDamage};
use dom::processinginstruction::ProcessingInstruction;
use dom::text::Text;
use std::cell::Ref;
use util::str::DOMString;

@@ -30,6 +35,38 @@ impl CharacterData {
data: DOMRefCell::new(data),
}
}

pub fn clone_with_data(&self, data: DOMString, document: &Document) -> Root<Node> {
match self.upcast::<Node>().type_id() {
NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => {
Root::upcast(Comment::new(data, &document))
}
NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) => {
let pi = self.downcast::<ProcessingInstruction>().unwrap();
Root::upcast(ProcessingInstruction::new(pi.Target(), data, &document))
},
NodeTypeId::CharacterData(CharacterDataTypeId::Text) => {
Root::upcast(Text::new(data, &document))
},
_ => unreachable!(),
}
}

#[inline]
pub fn data(&self) -> Ref<DOMString> {
self.data.borrow()
}

#[inline]
pub fn append_data(&self, data: &str) {
self.data.borrow_mut().push_str(data);
self.content_changed();
}

fn content_changed(&self) {
let node = self.upcast::<Node>();
node.owner_doc().content_changed(node, NodeDamage::OtherNodeDamage);
}
}

impl CharacterDataMethods for CharacterData {
@@ -40,13 +77,17 @@ impl CharacterDataMethods for CharacterData {

// https://dom.spec.whatwg.org/#dom-characterdata-data
fn SetData(&self, data: DOMString) {
let old_length = self.Length();
let new_length = data.utf16_units().count() as u32;
*self.data.borrow_mut() = data;
self.content_changed();
let node = self.upcast::<Node>();
node.ranges().replace_code_units(node, 0, old_length, new_length);
}

// https://dom.spec.whatwg.org/#dom-characterdata-length
fn Length(&self) -> u32 {
self.data.borrow().chars().map(|c| c.len_utf16()).sum::<usize>() as u32
self.data.borrow().utf16_units().count() as u32
}

// https://dom.spec.whatwg.org/#dom-characterdata-substringdata
@@ -107,7 +148,10 @@ impl CharacterDataMethods for CharacterData {
};
*self.data.borrow_mut() = DOMString::from(new_data);
self.content_changed();
// FIXME: Once we have `Range`, we should implement step 8 to step 11
// Steps 8-11.
let node = self.upcast::<Node>();
node.ranges().replace_code_units(
node, offset, count, arg.utf16_units().count() as u32);
Ok(())
}

@@ -143,24 +187,6 @@ impl CharacterDataMethods for CharacterData {
}
}

impl CharacterData {
#[inline]
pub fn data(&self) -> Ref<DOMString> {
self.data.borrow()
}
#[inline]
pub fn append_data(&self, data: &str) {
// FIXME(ajeffrey): Efficient append on DOMStrings?
self.data.borrow_mut().push_str(data);
self.content_changed();
}

fn content_changed(&self) {
let node = self.upcast::<Node>();
node.owner_doc().content_changed(node, NodeDamage::OtherNodeDamage);
}
}

#[allow(unsafe_code)]
pub trait LayoutCharacterDataHelpers {
unsafe fn data_for_layout(&self) -> &str;
@@ -53,7 +53,7 @@ use dom::htmltemplateelement::HTMLTemplateElement;
use dom::htmltextareaelement::{HTMLTextAreaElement, RawLayoutHTMLTextAreaElementHelpers};
use dom::namednodemap::NamedNodeMap;
use dom::node::{CLICK_IN_PROGRESS, LayoutNodeHelpers, Node};
use dom::node::{NodeDamage, SEQUENTIALLY_FOCUSABLE};
use dom::node::{NodeDamage, SEQUENTIALLY_FOCUSABLE, UnbindContext};
use dom::node::{document_from_node, window_from_node};
use dom::nodelist::NodeList;
use dom::text::Text;
@@ -1616,12 +1616,10 @@ impl VirtualMethods for Element {
}
}

fn unbind_from_tree(&self, tree_in_doc: bool) {
if let Some(ref s) = self.super_type() {
s.unbind_from_tree(tree_in_doc);
}
fn unbind_from_tree(&self, context: &UnbindContext) {
self.super_type().unwrap().unbind_from_tree(context);

if !tree_in_doc {
if !context.tree_in_doc {
return;
}

@@ -9,7 +9,7 @@ use dom::bindings::js::Root;
use dom::document::Document;
use dom::element::{AttributeMutation, Element};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, document_from_node};
use dom::node::{Node, UnbindContext, document_from_node};
use dom::virtualmethods::VirtualMethods;
use string_cache::Atom;
use url::{Url, UrlParser};
@@ -77,8 +77,8 @@ impl VirtualMethods for HTMLBaseElement {
self.bind_unbind(tree_in_doc);
}

fn unbind_from_tree(&self, tree_in_doc: bool) {
self.super_type().unwrap().unbind_from_tree(tree_in_doc);
self.bind_unbind(tree_in_doc);
fn unbind_from_tree(&self, context: &UnbindContext) {
self.super_type().unwrap().unbind_from_tree(context);
self.bind_unbind(context.tree_in_doc);
}
}
@@ -16,7 +16,7 @@ use dom::htmlelement::HTMLElement;
use dom::htmlfieldsetelement::HTMLFieldSetElement;
use dom::htmlformelement::{FormControl, FormSubmitter};
use dom::htmlformelement::{SubmittedFrom, HTMLFormElement};
use dom::node::{Node, document_from_node, window_from_node};
use dom::node::{Node, UnbindContext, document_from_node, window_from_node};
use dom::nodelist::NodeList;
use dom::validitystate::ValidityState;
use dom::virtualmethods::VirtualMethods;
@@ -168,10 +168,8 @@ impl VirtualMethods for HTMLButtonElement {
self.upcast::<Element>().check_ancestors_disabled_state_for_form_control();
}

fn unbind_from_tree(&self, tree_in_doc: bool) {
if let Some(ref s) = self.super_type() {
s.unbind_from_tree(tree_in_doc);
}
fn unbind_from_tree(&self, context: &UnbindContext) {
self.super_type().unwrap().unbind_from_tree(context);

let node = self.upcast::<Node>();
let el = self.upcast::<Element>();
@@ -18,7 +18,7 @@ use dom::document::Document;
use dom::element::{AttributeMutation, Element, RawLayoutElementHelpers};
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::htmlelement::HTMLElement;
use dom::node::{Node, window_from_node};
use dom::node::{Node, UnbindContext, window_from_node};
use dom::urlhelper::UrlHelper;
use dom::virtualmethods::VirtualMethods;
use dom::window::Window;
@@ -471,10 +471,8 @@ impl VirtualMethods for HTMLIFrameElement {
}
}

fn unbind_from_tree(&self, tree_in_doc: bool) {
if let Some(ref s) = self.super_type() {
s.unbind_from_tree(tree_in_doc);
}
fn unbind_from_tree(&self, context: &UnbindContext) {
self.super_type().unwrap().unbind_from_tree(context);

// https://html.spec.whatwg.org/multipage/#a-browsing-context-is-discarded
if let Some(pipeline_id) = self.pipeline_id.get() {
@@ -23,7 +23,7 @@ use dom::htmlfieldsetelement::HTMLFieldSetElement;
use dom::htmlformelement::{FormControl, FormDatum, FormSubmitter, HTMLFormElement};
use dom::htmlformelement::{ResetFrom, SubmittedFrom};
use dom::keyboardevent::KeyboardEvent;
use dom::node::{Node, NodeDamage};
use dom::node::{Node, NodeDamage, UnbindContext};
use dom::node::{document_from_node, window_from_node};
use dom::nodelist::NodeList;
use dom::virtualmethods::VirtualMethods;
@@ -634,10 +634,8 @@ impl VirtualMethods for HTMLInputElement {
self.upcast::<Element>().check_ancestors_disabled_state_for_form_control();
}

fn unbind_from_tree(&self, tree_in_doc: bool) {
if let Some(ref s) = self.super_type() {
s.unbind_from_tree(tree_in_doc);
}
fn unbind_from_tree(&self, context: &UnbindContext) {
self.super_type().unwrap().unbind_from_tree(context);

let node = self.upcast::<Node>();
let el = self.upcast::<Element>();
@@ -15,7 +15,7 @@ use dom::element::{AttributeMutation, Element};
use dom::htmlelement::HTMLElement;
use dom::htmlscriptelement::HTMLScriptElement;
use dom::htmlselectelement::HTMLSelectElement;
use dom::node::Node;
use dom::node::{Node, UnbindContext};
use dom::text::Text;
use dom::virtualmethods::VirtualMethods;
use selectors::states::*;
@@ -207,10 +207,8 @@ impl VirtualMethods for HTMLOptionElement {
self.pick_if_selected_and_reset();
}

fn unbind_from_tree(&self, tree_in_doc: bool) {
if let Some(ref s) = self.super_type() {
s.unbind_from_tree(tree_in_doc);
}
fn unbind_from_tree(&self, context: &UnbindContext) {
self.super_type().unwrap().unbind_from_tree(context);

let node = self.upcast::<Node>();
let el = self.upcast::<Element>();
@@ -16,7 +16,7 @@ use dom::htmlelement::HTMLElement;
use dom::htmlfieldsetelement::HTMLFieldSetElement;
use dom::htmlformelement::{FormControl, HTMLFormElement};
use dom::htmloptionelement::HTMLOptionElement;
use dom::node::{Node, window_from_node};
use dom::node::{Node, UnbindContext, window_from_node};
use dom::nodelist::NodeList;
use dom::validitystate::ValidityState;
use dom::virtualmethods::VirtualMethods;
@@ -196,10 +196,8 @@ impl VirtualMethods for HTMLSelectElement {
self.upcast::<Element>().check_ancestors_disabled_state_for_form_control();
}

fn unbind_from_tree(&self, tree_in_doc: bool) {
if let Some(ref s) = self.super_type() {
s.unbind_from_tree(tree_in_doc);
}
fn unbind_from_tree(&self, context: &UnbindContext) {
self.super_type().unwrap().unbind_from_tree(context);

let node = self.upcast::<Node>();
let el = self.upcast::<Element>();
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.