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

Implement a safe, mostly-sound rooting rooting strategy. #2101

Merged
merged 16 commits into from May 3, 2014
Merged
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Move all methods on T to JSRef<T> or JS<T> as appropriate.

  • Loading branch information
jdm committed May 3, 2014
commit 109410900c75721a77b970be3bdd830968e47151
@@ -37,10 +37,11 @@ use script::dom::bindings::codegen::InheritTypes::{HTMLIFrameElementDerived};
use script::dom::bindings::codegen::InheritTypes::{HTMLImageElementDerived, TextDerived};
use script::dom::bindings::js::JS;
use script::dom::element::{Element, HTMLAreaElementTypeId, HTMLAnchorElementTypeId};
use script::dom::element::{HTMLLinkElementTypeId};
use script::dom::element::{HTMLLinkElementTypeId, LayoutElementHelpers, RawLayoutElementHelpers};
use script::dom::htmliframeelement::HTMLIFrameElement;
use script::dom::htmlimageelement::HTMLImageElement;
use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, Node, NodeTypeId, LayoutNodeHelpers};
use script::dom::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelpers};
use script::dom::node::{DocumentNodeTypeId, ElementNodeTypeId, Node, NodeTypeId};
use script::dom::node::{LayoutNodeHelpers, RawLayoutNodeHelpers};
use script::dom::text::Text;
use servo_msg::constellation_msg::{PipelineId, SubpageId};
use servo_util::namespace;
@@ -95,7 +96,7 @@ pub trait TLayoutNode {
fail!("not an image!")
}
let image_element: JS<HTMLImageElement> = self.get_jsmanaged().transmute_copy();
(*image_element.unsafe_get()).image().as_ref().map(|url| (*url).clone())
image_element.image().as_ref().map(|url| (*url).clone())
}
}

@@ -163,7 +164,9 @@ impl<'ln> TLayoutNode for LayoutNode<'ln> {
}

fn type_id(&self) -> Option<NodeTypeId> {
Some(self.node.type_id_for_layout())
unsafe {
Some(self.node.type_id_for_layout())
}
}

unsafe fn get_jsmanaged<'a>(&'a self) -> &'a JS<Node> {
@@ -172,7 +175,7 @@ impl<'ln> TLayoutNode for LayoutNode<'ln> {

fn first_child(&self) -> Option<LayoutNode<'ln>> {
unsafe {
self.get().first_child_ref().map(|node| self.new_with_this_lifetime(node))
self.get_jsmanaged().first_child_ref().map(|node| self.new_with_this_lifetime(node))
}
}

@@ -221,26 +224,27 @@ impl<'ln> LayoutNode<'ln> {
impl<'ln> TNode<LayoutElement<'ln>> for LayoutNode<'ln> {
fn parent_node(&self) -> Option<LayoutNode<'ln>> {
unsafe {
self.get().parent_node_ref().map(|node| self.new_with_this_lifetime(node))
self.node.parent_node_ref().map(|node| self.new_with_this_lifetime(node))
}
}

fn prev_sibling(&self) -> Option<LayoutNode<'ln>> {
unsafe {
self.get().prev_sibling_ref().map(|node| self.new_with_this_lifetime(node))
self.node.prev_sibling_ref().map(|node| self.new_with_this_lifetime(node))
}
}

fn next_sibling(&self) -> Option<LayoutNode<'ln>> {
unsafe {
self.get().next_sibling_ref().map(|node| self.new_with_this_lifetime(node))
self.node.next_sibling_ref().map(|node| self.new_with_this_lifetime(node))
}
}

/// If this is an element, accesses the element data. Fails if this is not an element node.
#[inline]
fn as_element(&self) -> LayoutElement<'ln> {
unsafe {
assert!(self.node.is_element_for_layout());
let elem: JS<Element> = self.node.transmute_copy();
let element = &*elem.unsafe_get();
LayoutElement {
@@ -258,16 +262,17 @@ impl<'ln> TNode<LayoutElement<'ln>> for LayoutNode<'ln> {
}

fn match_attr(&self, attr: &AttrSelector, test: |&str| -> bool) -> bool {
let element = self.as_element();
let name = unsafe {
if element.element.html_element_in_html_document_for_layout() {
let element: JS<Element> = self.node.transmute_copy();
if element.html_element_in_html_document_for_layout() {
attr.lower_name.as_slice()
} else {
attr.name.as_slice()
}
};
match attr.namespace {
SpecificNamespace(ref ns) => {
let element = self.as_element();
element.get_attr(ns, name)
.map_or(false, |attr| test(attr))
},
@@ -459,7 +464,7 @@ impl<'ln> TLayoutNode for ThreadSafeLayoutNode<'ln> {
}

unsafe {
self.get().first_child_ref().map(|node| self.new_with_this_lifetime(node))
self.get_jsmanaged().first_child_ref().map(|node| self.new_with_this_lifetime(node))
}
}

@@ -509,10 +514,10 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
/// Returns the next sibling of this node. Unsafe and private because this can lead to races.
unsafe fn next_sibling(&self) -> Option<ThreadSafeLayoutNode<'ln>> {
if self.pseudo == Before || self.pseudo == BeforeBlock {
return (*self.get_jsmanaged().unsafe_get()).first_child_ref().map(|node| self.new_with_this_lifetime(node))
return self.get_jsmanaged().first_child_ref().map(|node| self.new_with_this_lifetime(node))
}

(*self.get_jsmanaged().unsafe_get()).next_sibling_ref().map(|node| self.new_with_this_lifetime(node))
self.get_jsmanaged().next_sibling_ref().map(|node| self.new_with_this_lifetime(node))
}

/// Returns an iterator over this node's children.
@@ -527,7 +532,8 @@ impl<'ln> ThreadSafeLayoutNode<'ln> {
#[inline]
pub fn as_element(&self) -> ThreadSafeLayoutElement {
unsafe {
let elem: JS<Element> = self.node.get_jsmanaged().transmute_copy();
assert!(self.get_jsmanaged().is_element_for_layout());
let elem: JS<Element> = self.get_jsmanaged().transmute_copy();
let element = elem.unsafe_get();
// FIXME(pcwalton): Workaround until Rust gets multiple lifetime parameters on
// implementations.
@@ -76,97 +76,54 @@ impl DocumentDerived for EventTarget {
}
}

impl Document {
pub fn reflect_document(document: ~Document,
window: &JSRef<Window>,
wrap_fn: extern "Rust" fn(*JSContext, &JSRef<Window>, ~Document) -> JS<Document>)
-> Unrooted<Document> {
let roots = RootCollection::new();
assert!(document.reflector().get_jsobject().is_null());
let mut raw_doc = reflect_dom_object(document, window, wrap_fn).root(&roots);
assert!(raw_doc.reflector().get_jsobject().is_not_null());

let mut doc_alias = raw_doc.clone();
let node: &mut JSRef<Node> = NodeCast::from_mut_ref(&mut doc_alias);
node.get_mut().set_owner_doc(&*raw_doc);
Unrooted::new_rooted(&*raw_doc)
}

pub fn new_inherited(window: JS<Window>,
url: Option<Url>,
is_html_document: IsHTMLDocument,
content_type: Option<DOMString>) -> Document {
let url = url.unwrap_or_else(|| from_str("about:blank").unwrap());

Document {
node: Node::new_without_doc(DocumentNodeTypeId),
reflector_: Reflector::new(),
window: window,
idmap: HashMap::new(),
implementation: None,
content_type: match content_type {
Some(string) => string.clone(),
None => match is_html_document {
// http://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument
HTMLDocument => ~"text/html",
// http://dom.spec.whatwg.org/#concept-document-content-type
NonHTMLDocument => ~"application/xml"
}
},
url: Untraceable::new(url),
// http://dom.spec.whatwg.org/#concept-document-quirks
quirks_mode: Untraceable::new(NoQuirks),
// http://dom.spec.whatwg.org/#concept-document-encoding
encoding_name: ~"utf-8",
is_html_document: is_html_document == HTMLDocument,
}
}

// http://dom.spec.whatwg.org/#dom-document
pub fn Constructor(owner: &JSRef<Window>) -> Fallible<Unrooted<Document>> {
Ok(Document::new(owner, None, NonHTMLDocument, None))
}

pub fn new(window: &JSRef<Window>, url: Option<Url>, doctype: IsHTMLDocument, content_type: Option<DOMString>) -> Unrooted<Document> {
let document = Document::new_inherited(window.unrooted(), url, doctype, content_type);
Document::reflect_document(~document, window, DocumentBinding::Wrap)
}
pub trait DocumentHelpers {
fn url<'a>(&'a self) -> &'a Url;
fn quirks_mode(&self) -> QuirksMode;
fn set_quirks_mode(&mut self, mode: QuirksMode);
fn set_encoding_name(&mut self, name: DOMString);
fn content_changed(&self);
fn damage_and_reflow(&self, damage: DocumentDamageLevel);
fn wait_until_safe_to_modify_dom(&self);
fn unregister_named_element(&mut self, to_unregister: &JSRef<Element>, id: DOMString);
fn register_named_element(&mut self, element: &JSRef<Element>, id: DOMString);
}

pub fn url<'a>(&'a self) -> &'a Url {
impl<'a> DocumentHelpers for JSRef<'a, Document> {
fn url<'a>(&'a self) -> &'a Url {
&*self.url
}

pub fn quirks_mode(&self) -> QuirksMode {
fn quirks_mode(&self) -> QuirksMode {
*self.quirks_mode
}

pub fn set_quirks_mode(&mut self, mode: QuirksMode) {
fn set_quirks_mode(&mut self, mode: QuirksMode) {
*self.quirks_mode = mode;
}

pub fn set_encoding_name(&mut self, name: DOMString) {
fn set_encoding_name(&mut self, name: DOMString) {
self.encoding_name = name;
}

pub fn content_changed(&self) {
fn content_changed(&self) {
self.damage_and_reflow(ContentChangedDocumentDamage);
}

pub fn damage_and_reflow(&self, damage: DocumentDamageLevel) {
fn damage_and_reflow(&self, damage: DocumentDamageLevel) {
let roots = RootCollection::new();
self.window.root(&roots).damage_and_reflow(damage);
}

pub fn wait_until_safe_to_modify_dom(&self) {
fn wait_until_safe_to_modify_dom(&self) {
let roots = RootCollection::new();
self.window.root(&roots).wait_until_safe_to_modify_dom();
}


/// Remove any existing association between the provided id and any elements in this document.
pub fn unregister_named_element(&mut self,
to_unregister: &JSRef<Element>,
id: DOMString) {
fn unregister_named_element(&mut self,
to_unregister: &JSRef<Element>,
id: DOMString) {
let roots = RootCollection::new();
let mut is_empty = false;
match self.idmap.find_mut(&id) {
@@ -186,10 +143,9 @@ impl Document {
}

/// Associate an element present in this document with the provided id.
pub fn register_named_element(&mut self,
abstract_self: &JSRef<Document>,
element: &JSRef<Element>,
id: DOMString) {
fn register_named_element(&mut self,
element: &JSRef<Element>,
id: DOMString) {
let roots = RootCollection::new();
assert!({
let node: &JSRef<Node> = NodeCast::from_ref(element);
@@ -198,7 +154,7 @@ impl Document {

// FIXME https://github.com/mozilla/rust/issues/13195
// Use mangle() when it exists again.
let root = abstract_self.GetDocumentElement().expect("The element is in the document, so there must be a document element.").root(&roots);
let root = self.GetDocumentElement().expect("The element is in the document, so there must be a document element.").root(&roots);
match self.idmap.find_mut(&id) {
Some(elements) => {
let new_node: &JSRef<Node> = NodeCast::from_ref(element);
@@ -227,6 +183,63 @@ impl Document {
}
}

impl Document {
pub fn reflect_document(document: ~Document,
window: &JSRef<Window>,
wrap_fn: extern "Rust" fn(*JSContext, &JSRef<Window>, ~Document) -> JS<Document>)
-> Unrooted<Document> {
let roots = RootCollection::new();
assert!(document.reflector().get_jsobject().is_null());
let mut raw_doc = reflect_dom_object(document, window, wrap_fn).root(&roots);
assert!(raw_doc.reflector().get_jsobject().is_not_null());

let mut doc_alias = raw_doc.clone();
let node: &mut JSRef<Node> = NodeCast::from_mut_ref(&mut doc_alias);
node.set_owner_doc(&*raw_doc);
Unrooted::new_rooted(&*raw_doc)
}

pub fn new_inherited(window: JS<Window>,
url: Option<Url>,
is_html_document: IsHTMLDocument,
content_type: Option<DOMString>) -> Document {
let url = url.unwrap_or_else(|| from_str("about:blank").unwrap());

Document {
node: Node::new_without_doc(DocumentNodeTypeId),
reflector_: Reflector::new(),
window: window,
idmap: HashMap::new(),
implementation: None,
content_type: match content_type {
Some(string) => string.clone(),
None => match is_html_document {
// http://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument
HTMLDocument => ~"text/html",
// http://dom.spec.whatwg.org/#concept-document-content-type
NonHTMLDocument => ~"application/xml"
}
},
url: Untraceable::new(url),
// http://dom.spec.whatwg.org/#concept-document-quirks
quirks_mode: Untraceable::new(NoQuirks),
// http://dom.spec.whatwg.org/#concept-document-encoding
encoding_name: ~"utf-8",
is_html_document: is_html_document == HTMLDocument,
}
}

// http://dom.spec.whatwg.org/#dom-document
pub fn Constructor(owner: &JSRef<Window>) -> Fallible<Unrooted<Document>> {
Ok(Document::new(owner, None, NonHTMLDocument, None))
}

pub fn new(window: &JSRef<Window>, url: Option<Url>, doctype: IsHTMLDocument, content_type: Option<DOMString>) -> Unrooted<Document> {
let document = Document::new_inherited(window.unrooted(), url, doctype, content_type);
Document::reflect_document(~document, window, DocumentBinding::Wrap)
}
}

impl Reflectable for Document {
fn reflector<'a>(&'a self) -> &'a Reflector {
self.node.reflector()
@@ -237,12 +250,12 @@ impl Reflectable for Document {
}
}

trait DocumentHelpers {
trait PrivateDocumentHelpers {
fn createNodeList(&self, callback: |node: &JSRef<Node>| -> bool) -> Unrooted<NodeList>;
fn get_html_element(&self) -> Option<Unrooted<HTMLHtmlElement>>;
}

impl<'a> DocumentHelpers for JSRef<'a, Document> {
impl<'a> PrivateDocumentHelpers for JSRef<'a, Document> {
fn createNodeList(&self, callback: |node: &JSRef<Node>| -> bool) -> Unrooted<NodeList> {
let roots = RootCollection::new();
let window = self.window.root(&roots);
@@ -354,7 +367,8 @@ impl<'a> DocumentMethods for JSRef<'a, Document> {

// http://dom.spec.whatwg.org/#dom-document-doctype
fn GetDoctype(&self) -> Option<Unrooted<DocumentType>> {
self.node.children().find(|child| {
let node: &JSRef<Node> = NodeCast::from_ref(self);
node.children().find(|child| {
child.is_doctype()
}).map(|node| {
let doctype: &JSRef<DocumentType> = DocumentTypeCast::to_ref(&node).unwrap();
@@ -364,7 +378,8 @@ impl<'a> DocumentMethods for JSRef<'a, Document> {

// http://dom.spec.whatwg.org/#dom-document-documentelement
fn GetDocumentElement(&self) -> Option<Unrooted<Element>> {
self.node.child_elements().next().map(|elem| Unrooted::new_rooted(&elem))
let node: &JSRef<Node> = NodeCast::from_ref(self);
node.child_elements().next().map(|elem| Unrooted::new_rooted(&elem))
}

// http://dom.spec.whatwg.org/#dom-document-getelementsbytagname
@@ -544,7 +559,7 @@ impl<'a> DocumentMethods for JSRef<'a, Document> {
root.traverse_preorder(&roots)
.find(|node| node.type_id() == ElementNodeTypeId(HTMLTitleElementTypeId))
.map(|title_elem| {
for child in title_elem.deref().children() {
for child in title_elem.children() {
if child.is_text() {
let text: &JSRef<Text> = TextCast::to_ref(&child).unwrap();
title.push_str(text.get().characterdata.data.as_slice());
@@ -633,8 +648,9 @@ impl<'a> DocumentMethods for JSRef<'a, Document> {

// Step 1.
match new_body {
Some(ref node) => {
match node.get().element.node.type_id {
Some(ref htmlelem) => {
let node: &JSRef<Node> = NodeCast::from_ref(htmlelem);
match node.type_id() {
ElementNodeTypeId(HTMLBodyElementTypeId) | ElementNodeTypeId(HTMLFrameSetElementTypeId) => {}
_ => return Err(HierarchyRequest)
}
@@ -676,7 +692,7 @@ impl<'a> DocumentMethods for JSRef<'a, Document> {
let roots = RootCollection::new();

self.createNodeList(|node| {
if !node.get().is_element() {
if !node.is_element() {
return false;
}

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.