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

Separate the DOM and layout into separate crates. #474

Merged
merged 9 commits into from May 30, 2013

Introduce a phantom type to prevent script from accessing the layout …

…data directly.

Nodes are now parameterized over a "View" type. The particular View type
determines which methods can be called. Layout data accessors and mutators are
only accessible to nodes with a LayoutView. The only way to convert a
`Node<ScriptView>` to a `Node<LayoutView>` is through a transmutation, which is
done at the moment the layout task receives nodes. (This should be factored
better to contain the unsafety.)

We should also lock down DOM node mutation to the ScriptView to forbid data
races, but this patch doesn't do that.

This also reduces coupling between DOM and layout. Soon I would like to move
the DOM into its own crate, and this is a step on the way of doing that.
  • Loading branch information
pcwalton committed May 28, 2013
commit 4f3ca373d441bcb1bad51ddf9c579ccbd955ac95
@@ -6,7 +6,7 @@

use css::node_util::NodeUtil;
use css::select_handler::NodeSelectHandler;
use dom::node::AbstractNode;
use dom::node::{AbstractNode, LayoutView};
use newcss::complete::CompleteSelectResults;
use newcss::select::{SelectCtx, SelectResults};

@@ -16,7 +16,7 @@ pub trait MatchMethods {
fn restyle_subtree(&self, select_ctx: &SelectCtx);
}

impl MatchMethods for AbstractNode {
impl MatchMethods for AbstractNode<LayoutView> {
/**
* Performs CSS selector matching on a subtree.
*
@@ -40,7 +40,8 @@ impl MatchMethods for AbstractNode {
}
}

fn compose_results(node: AbstractNode, results: SelectResults) -> CompleteSelectResults {
fn compose_results(node: AbstractNode<LayoutView>, results: SelectResults)
-> CompleteSelectResults {
match find_parent_element_node(node) {
None => CompleteSelectResults::new_root(results),
Some(parent_node) => {
@@ -50,7 +51,7 @@ fn compose_results(node: AbstractNode, results: SelectResults) -> CompleteSelect
}
}

fn find_parent_element_node(node: AbstractNode) -> Option<AbstractNode> {
fn find_parent_element_node(node: AbstractNode<LayoutView>) -> Option<AbstractNode<LayoutView>> {
match node.parent_node() {
Some(parent) if parent.is_element() => Some(parent),
Some(parent) => find_parent_element_node(parent),
@@ -5,15 +5,16 @@
// Style retrieval from DOM elements.

use css::node_util::NodeUtil;
use dom::node::AbstractNode;
use dom::node::{AbstractNode, LayoutView};

use newcss::complete::CompleteStyle;

/// Node mixin providing `style` method that returns a `NodeStyle`
pub trait StyledNode {
fn style(&self) -> CompleteStyle;
}

impl StyledNode for AbstractNode {
impl StyledNode for AbstractNode<LayoutView> {
fn style(&self) -> CompleteStyle {
assert!(self.is_element()); // Only elements can have styles
let results = self.get_css_select_results();
@@ -2,17 +2,18 @@
* 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::node::AbstractNode;
use newcss::complete::CompleteSelectResults;
use dom::node::{AbstractNode, LayoutView};
use layout::aux::LayoutAuxMethods;

use core::cast::transmute;
use newcss::complete::CompleteSelectResults;

pub trait NodeUtil<'self> {
fn get_css_select_results(self) -> &'self CompleteSelectResults;
fn set_css_select_results(self, decl: CompleteSelectResults);
}

impl<'self> NodeUtil<'self> for AbstractNode {
impl<'self> NodeUtil<'self> for AbstractNode<LayoutView> {
/**
* Provides the computed style for the given node. If CSS selector
* Returns the style results for the given node. If CSS selector
@@ -5,15 +5,15 @@
//! CSS library requires that DOM nodes be convertable to *c_void through this trait
extern mod netsurfcss;

use dom::node::AbstractNode;
use dom::node::{AbstractNode, LayoutView};

use core::cast;

// FIXME: Rust #3908. rust-css can't reexport VoidPtrLike
use css::node_void_ptr::netsurfcss::util::VoidPtrLike;

impl VoidPtrLike for AbstractNode {
fn from_void_ptr(node: *libc::c_void) -> AbstractNode {
impl VoidPtrLike for AbstractNode<LayoutView> {
fn from_void_ptr(node: *libc::c_void) -> AbstractNode<LayoutView> {
assert!(node.is_not_null());
unsafe {
cast::transmute(node)
@@ -6,16 +6,16 @@
/// Implementation of the callbacks that the CSS selector engine uses to query the DOM.
///

use dom::node::AbstractNode;
use dom::node::{AbstractNode, LayoutView};
use newcss::select::SelectHandler;

use core::str::eq_slice;

pub struct NodeSelectHandler {
node: AbstractNode
node: AbstractNode<LayoutView>,
}

fn with_node_name<R>(node: AbstractNode, f: &fn(&str) -> R) -> R {
fn with_node_name<R>(node: AbstractNode<LayoutView>, f: &fn(&str) -> R) -> R {
if !node.is_element() {
fail!(~"attempting to style non-element node");
}
@@ -24,12 +24,13 @@ fn with_node_name<R>(node: AbstractNode, f: &fn(&str) -> R) -> R {
}
}

impl SelectHandler<AbstractNode> for NodeSelectHandler {
fn with_node_name<R>(&self, node: &AbstractNode, f: &fn(&str) -> R) -> R {
impl SelectHandler<AbstractNode<LayoutView>> for NodeSelectHandler {
fn with_node_name<R>(&self, node: &AbstractNode<LayoutView>, f: &fn(&str) -> R) -> R {
with_node_name(*node, f)
}

fn named_parent_node(&self, node: &AbstractNode, name: &str) -> Option<AbstractNode> {
fn named_parent_node(&self, node: &AbstractNode<LayoutView>, name: &str)
-> Option<AbstractNode<LayoutView>> {
match node.parent_node() {
Some(parent) => {
do with_node_name(parent) |node_name| {
@@ -44,12 +45,13 @@ impl SelectHandler<AbstractNode> for NodeSelectHandler {
}
}

fn parent_node(&self, node: &AbstractNode) -> Option<AbstractNode> {
fn parent_node(&self, node: &AbstractNode<LayoutView>) -> Option<AbstractNode<LayoutView>> {
node.parent_node()
}

// TODO: Use a Bloom filter.
fn named_ancestor_node(&self, node: &AbstractNode, name: &str) -> Option<AbstractNode> {
fn named_ancestor_node(&self, node: &AbstractNode<LayoutView>, name: &str)
-> Option<AbstractNode<LayoutView>> {
let mut node = *node;
loop {
let parent = node.parent_node();
@@ -71,11 +73,11 @@ impl SelectHandler<AbstractNode> for NodeSelectHandler {
}
}

fn node_is_root(&self, node: &AbstractNode) -> bool {
fn node_is_root(&self, node: &AbstractNode<LayoutView>) -> bool {
self.parent_node(node).is_none()
}

fn with_node_id<R>(&self, node: &AbstractNode, f: &fn(Option<&str>) -> R) -> R {
fn with_node_id<R>(&self, node: &AbstractNode<LayoutView>, f: &fn(Option<&str>) -> R) -> R {
if !node.is_element() {
fail!(~"attempting to style non-element node");
}
@@ -84,7 +86,7 @@ impl SelectHandler<AbstractNode> for NodeSelectHandler {
}
}

fn node_has_id(&self, node: &AbstractNode, id: &str) -> bool {
fn node_has_id(&self, node: &AbstractNode<LayoutView>, id: &str) -> bool {
if !node.is_element() {
fail!(~"attempting to style non-element node");
}
@@ -513,7 +513,7 @@ addExternalIface('CSSValue')
addExternalIface('Document', nativeType='Document', pointerType='@mut ')
addExternalIface('DOMStringList', nativeType='nsDOMStringList',
headerFile='nsDOMLists.h')
addExternalIface('Element', nativeType='AbstractNode', pointerType='')
addExternalIface('Element', nativeType='AbstractNode<ScriptView>', pointerType='')
addExternalIface('File')
addExternalIface('HitRegionOptions', nativeType='nsISupports')
addExternalIface('HTMLElement')
@@ -4149,6 +4149,7 @@ def makeEnumTypedef(e):
'dom::eventtarget::*', #XXXjdm
'scripting::script_task::task_from_context',
'dom::bindings::utils::EnumEntry',
'dom::node::ScriptView',
],
[],
curr)
@@ -7,7 +7,7 @@ use dom::bindings::utils::jsval_to_str;
use dom::bindings::utils::{domstring_to_jsval, WrapNewBindingObject};
use dom::bindings::utils::{str, CacheableWrapper, DOM_OBJECT_SLOT, DOMString};
use dom::element::*;
use dom::node::{AbstractNode, Element, ElementNodeTypeId};
use dom::node::{AbstractNode, Element, ElementNodeTypeId, ScriptView};
use layout::layout_task;
use scripting::script_task::task_from_context;
use super::utils;
@@ -26,7 +26,7 @@ use js::{JS_THIS_OBJECT, JS_SET_RVAL, JSPROP_NATIVE_ACCESSORS};
extern fn finalize(_fop: *JSFreeOp, obj: *JSObject) {
debug!("element finalize: %x!", obj as uint);
unsafe {
let node: AbstractNode = unwrap(obj);
let node: AbstractNode<ScriptView> = unwrap(obj);
//XXXjdm We need separate finalizers for each specialty element type like headings
let _elem: ~Element = cast::transmute(node.raw_object());
}
@@ -35,7 +35,7 @@ extern fn finalize(_fop: *JSFreeOp, obj: *JSObject) {
pub extern fn trace(tracer: *mut JSTracer, obj: *JSObject) {
let node = unsafe { unwrap(obj) };

fn trace_node(tracer: *mut JSTracer, node: Option<AbstractNode>, name: &str) {
fn trace_node(tracer: *mut JSTracer, node: Option<AbstractNode<ScriptView>>, name: &str) {
if node.is_none() {
return;
}
@@ -278,7 +278,7 @@ extern fn getTagName(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
return 1;
}

pub fn create(cx: *JSContext, node: &mut AbstractNode) -> jsobj {
pub fn create(cx: *JSContext, node: &mut AbstractNode<ScriptView>) -> jsobj {
let proto = match node.type_id() {
ElementNodeTypeId(HTMLDivElementTypeId) => ~"HTMLDivElement",
ElementNodeTypeId(HTMLHeadElementTypeId) => ~"HTMLHeadElement",
@@ -7,7 +7,7 @@ use dom::bindings::text;
use dom::bindings::utils;
use dom::bindings::utils::{CacheableWrapper, WrapperCache, DerivedWrapper};
use dom::node::{AbstractNode, Node, ElementNodeTypeId, TextNodeTypeId, CommentNodeTypeId};
use dom::node::{DoctypeNodeTypeId};
use dom::node::{DoctypeNodeTypeId, ScriptView};

use core::libc::c_uint;
use core::ptr::null;
@@ -58,7 +58,7 @@ pub fn init(compartment: @mut Compartment) {
}

#[allow(non_implicitly_copyable_typarams)]
pub fn create(cx: *JSContext, node: &mut AbstractNode) -> jsobj {
pub fn create(cx: *JSContext, node: &mut AbstractNode<ScriptView>) -> jsobj {
match node.type_id() {
ElementNodeTypeId(_) => element::create(cx, node),
TextNodeTypeId |
@@ -67,8 +67,8 @@ pub fn create(cx: *JSContext, node: &mut AbstractNode) -> jsobj {
}
}

pub unsafe fn unwrap(obj: *JSObject) -> AbstractNode {
let raw = utils::unwrap::<*mut Node>(obj);
pub unsafe fn unwrap(obj: *JSObject) -> AbstractNode<ScriptView> {
let raw = utils::unwrap::<*mut Node<ScriptView>>(obj);
AbstractNode::from_raw(raw)
}

@@ -116,7 +116,7 @@ extern fn getNextSibling(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBoo
return 1;
}

impl Node {
impl Node<ScriptView> {
fn getNodeType(&self) -> i32 {
match self.type_id {
ElementNodeTypeId(_) => 1,
@@ -126,7 +126,7 @@ impl Node {
}
}

fn getNextSibling(&mut self) -> Option<&mut AbstractNode> {
fn getNextSibling(&mut self) -> Option<&mut AbstractNode<ScriptView>> {
match self.next_sibling {
// transmute because the compiler can't deduce that the reference
// is safe outside of with_mut_base blocks.
@@ -135,7 +135,7 @@ impl Node {
}
}

fn getFirstChild(&mut self) -> Option<&mut AbstractNode> {
fn getFirstChild(&mut self) -> Option<&mut AbstractNode<ScriptView>> {
match self.first_child {
// transmute because the compiler can't deduce that the reference
// is safe outside of with_mut_base blocks.
@@ -161,7 +161,7 @@ extern fn getNodeType(cx: *JSContext, _argc: c_uint, vp: *mut JSVal) -> JSBool {
return 1;
}

impl CacheableWrapper for AbstractNode {
impl CacheableWrapper for AbstractNode<ScriptView> {
fn get_wrappercache(&mut self) -> &mut WrapperCache {
do self.with_mut_base |base| {
unsafe {
@@ -7,7 +7,7 @@ use dom::bindings::node::unwrap;
use dom::bindings::utils;
use dom::bindings::utils::{DOM_OBJECT_SLOT, CacheableWrapper};
use dom::node::{AbstractNode, Text, Comment, Doctype, TextNodeTypeId, CommentNodeTypeId};
use dom::node::{DoctypeNodeTypeId};
use dom::node::{DoctypeNodeTypeId, ScriptView};

use js::jsapi::{JSFreeOp, JSObject, JSContext};
use js::jsapi::bindgen::{JS_SetReservedSlot};
@@ -17,24 +17,24 @@ use js::rust::{Compartment, jsobj};
extern fn finalize_text(_fop: *JSFreeOp, obj: *JSObject) {
debug!("text finalize: %?!", obj as uint);
unsafe {
let node: AbstractNode = unwrap(obj);
let node: AbstractNode<ScriptView> = unwrap(obj);
let _elem: ~Text = cast::transmute(node.raw_object());
}
}

extern fn finalize_comment(_fop: *JSFreeOp, obj: *JSObject) {
debug!("comment finalize: %?!", obj as uint);
unsafe {
let node: AbstractNode = unwrap(obj);
let node: AbstractNode<ScriptView> = unwrap(obj);
let _elem: ~Comment = cast::transmute(node.raw_object());
}
}

extern fn finalize_doctype(_fop: *JSFreeOp, obj: *JSObject) {
debug!("doctype finalize: %?!", obj as uint);
unsafe {
let node: AbstractNode = unwrap(obj);
let _elem: ~Doctype = cast::transmute(node.raw_object());
let node: AbstractNode<ScriptView> = unwrap(obj);
let _elem: ~Doctype<ScriptView> = cast::transmute(node.raw_object());
}
}

@@ -64,7 +64,7 @@ pub fn init(compartment: @mut Compartment) {

}

pub fn create(cx: *JSContext, node: &mut AbstractNode) -> jsobj {
pub fn create(cx: *JSContext, node: &mut AbstractNode<ScriptView>) -> jsobj {
let (proto, instance) = match node.type_id() {
TextNodeTypeId => (~"TextPrototype", ~"Text"),
CommentNodeTypeId => (~"CommentPrototype", ~"Comment"),
@@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use dom::bindings::node;
use dom::node::AbstractNode;
use dom::node::{AbstractNode, ScriptView};
use js::glue::bindgen::*;
use js::glue::bindgen::{DefineFunctionWithReserved, GetObjectJSClass, RUST_OBJECT_TO_JSVAL};
use js::glue::{PROPERTY_STUB, STRICT_PROPERTY_STUB, ENUMERATE_STUB, CONVERT_STUB, RESOLVE_STUB};
@@ -760,7 +760,7 @@ pub trait DerivedWrapper {
fn wrap_shared(@mut self, cx: *JSContext, scope: *JSObject, vp: *mut JSVal) -> i32;
}

impl DerivedWrapper for AbstractNode {
impl DerivedWrapper for AbstractNode<ScriptView> {
fn wrap(&mut self, cx: *JSContext, _scope: *JSObject, vp: *mut JSVal) -> i32 {
let cache = self.get_wrappercache();
let wrapper = cache.get_wrapper();
@@ -5,12 +5,12 @@
//! DOM bindings for `CharacterData`.

use dom::bindings::utils::{DOMString, null_string, str};
use dom::node::{Node, NodeTypeId};
use dom::node::{Node, NodeTypeId, ScriptView};

use core::str;

pub struct CharacterData {
parent: Node,
parent: Node<ScriptView>,
data: DOMString
}

@@ -5,20 +5,20 @@
use dom::bindings::document;
use dom::bindings::utils::{DOMString, WrapperCache};
use dom::htmlcollection::HTMLCollection;
use dom::node::AbstractNode;
use dom::node::{AbstractNode, ScriptView};
use dom::window::Window;
use scripting::script_task::global_script_context;

use js::jsapi::bindgen::{JS_AddObjectRoot, JS_RemoveObjectRoot};
use servo_util::tree::{TreeNodeRef, TreeUtils};

pub struct Document {
root: AbstractNode,
root: AbstractNode<ScriptView>,
wrapper: WrapperCache,
window: Option<@mut Window>,
}

pub fn Document(root: AbstractNode, window: Option<@mut Window>) -> @mut Document {
pub fn Document(root: AbstractNode<ScriptView>, window: Option<@mut Window>) -> @mut Document {
let doc = @mut Document {
root: root,
wrapper: WrapperCache::new(),
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.