script: Make trees less generic

This commit is contained in:
Patrick Walton 2013-12-16 17:35:58 -08:00
parent 44a8b0987c
commit 199ca33b72
23 changed files with 382 additions and 523 deletions

View file

@ -15,9 +15,7 @@ use layout::incremental;
use layout::util::LayoutDataAccess;
use script::dom::node::{AbstractNode, LayoutView};
use style::Stylist;
use style::cascade;
use servo_util::tree::TreeNodeRef;
use style::{TNode, Stylist, cascade};
pub trait MatchMethods {
fn match_node(&self, stylist: &Stylist);

View file

@ -7,9 +7,8 @@ use layout::util::LayoutDataAccess;
use extra::arc::Arc;
use std::cast;
use style::ComputedValues;
use style::{ComputedValues, TNode};
use script::dom::node::{AbstractNode, LayoutView};
use servo_util::tree::TreeNodeRef;
pub trait NodeUtil {
fn get_css_select_results<'a>(&'a self) -> &'a Arc<ComputedValues>;

View file

@ -23,12 +23,11 @@ use servo_util::geometry::Au;
use servo_util::geometry;
use servo_util::range::*;
use servo_util::slot::Slot;
use servo_util::tree::{TreeNodeRef, ElementLike};
use std::cast;
use std::cell::Cell;
use std::cmp::ApproxEq;
use std::num::Zero;
use style::ComputedValues;
use style::{ComputedValues, TElement, TNode};
use style::computed_values::{LengthOrPercentage, overflow};
use style::computed_values::{border_style, clear, font_family, font_style, line_height};
use style::computed_values::{text_align, text_decoration, vertical_align, visibility};

View file

@ -36,7 +36,6 @@ use script::dom::node::{AbstractNode, CommentNodeTypeId, DoctypeNodeTypeId};
use script::dom::node::{DocumentFragmentNodeTypeId, DocumentNodeTypeId, ElementNodeTypeId};
use script::dom::node::{LayoutView, PostorderNodeMutTraversal, TextNodeTypeId};
use servo_util::slot::Slot;
use servo_util::tree::TreeNodeRef;
use std::util;
use style::computed_values::{display, float};

View file

@ -7,7 +7,6 @@
use layout::util::{LayoutData, LayoutDataAccess};
use script::dom::node::{AbstractNode, LayoutView};
use servo_util::tree::TreeNodeRef;
/// Functionality useful for querying the layout-specific data on DOM nodes.
pub trait LayoutAuxMethods {

View file

@ -22,8 +22,7 @@ use servo_util::range::Range;
use std::cell::Cell;
use std::u16;
use std::util;
use style::computed_values::text_align;
use style::computed_values::vertical_align;
use style::computed_values::{text_align, vertical_align};
/// Lineboxes are represented as offsets into the child list, rather than
/// as an object that "owns" boxes. Choosing a different set of line

View file

@ -43,16 +43,13 @@ use servo_net::local_image_cache::{ImageResponder, LocalImageCache};
use servo_util::geometry::Au;
use servo_util::time::{ProfilerChan, profile};
use servo_util::time;
use servo_util::tree::TreeNodeRef;
use std::cast::transmute;
use std::cast;
use std::cell::Cell;
use std::comm::Port;
use std::task;
use std::util;
use style::AuthorOrigin;
use style::Stylesheet;
use style::Stylist;
use style::{AuthorOrigin, Stylesheet, Stylist};
/// Information needed by the layout task.
struct LayoutTask {

View file

@ -9,7 +9,6 @@ use extra::arc::Arc;
use script::dom::node::{AbstractNode, LayoutView};
use servo_util::range::Range;
use servo_util::slot::{MutSlotRef, SlotRef};
use servo_util::tree::TreeNodeRef;
use std::cast;
use std::iter::Enumerate;
use std::libc::uintptr_t;

View file

@ -10,7 +10,6 @@ use std::cast;
use std::libc;
use std::ptr;
use js::jsapi::{JSTracer, JSTRACE_OBJECT, JS_CallTracer};
use servo_util::tree::TreeNodeRef;
impl Reflectable for AbstractNode<ScriptView> {
fn reflector<'a>(&'a self) -> &'a Reflector {

View file

@ -21,16 +21,15 @@ use dom::uievent::UIEvent;
use dom::window::Window;
use dom::htmltitleelement::HTMLTitleElement;
use html::hubbub_html_parser::build_element_from_tag;
use js::jsapi::{JSObject, JSContext, JSTracer};
use servo_util::tree::{TreeNodeRef, ElementLike};
use layout_interface::{DocumentDamageLevel, ContentChangedDocumentDamage};
use std::hashmap::HashMap;
use std::cast;
use std::str::eq_slice;
use js::jsapi::{JSObject, JSContext, JSTracer};
use std::ascii::StrAsciiExt;
use std::cast;
use std::hashmap::HashMap;
use std::str::eq_slice;
use std::unstable::raw::Box;
use style::{TElement, TNode};
#[deriving(Eq)]
pub enum DocumentTypeId {

View file

@ -20,8 +20,8 @@ use dom::namespace::Namespace;
use layout_interface::{ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery};
use layout_interface::{ContentBoxesResponse, ContentChangedDocumentDamage};
use layout_interface::{MatchSelectorsDocumentDamage};
use style::{TElement, TNode};
use style;
use servo_util::tree::{TreeNodeRef, ElementLike};
use std::comm;
use std::hashmap::HashMap;
@ -125,7 +125,7 @@ pub enum ElementTypeId {
// Element methods
//
impl ElementLike for Element {
impl TElement for Element {
fn get_local_name<'a>(&'a self) -> &'a str {
self.tag_name.as_slice()
}

View file

@ -6,7 +6,6 @@ use dom::bindings::callback::eReportExceptions;
use dom::eventtarget::{AbstractEventTarget, Capturing, Bubbling};
use dom::event::{AbstractEvent, Phase_At_Target, Phase_None, Phase_Bubbling, Phase_Capturing};
use dom::node::AbstractNode;
use servo_util::tree::{TreeNodeRef};
// See http://dom.spec.whatwg.org/#concept-event-dispatch for the full dispatch algorithm
pub fn dispatch_event(target: AbstractEventTarget,

View file

@ -11,10 +11,8 @@ use dom::node::{AbstractNode, ScriptView, ElementNodeTypeId};
use dom::window::Window;
use js::jsapi::JSTracer;
use servo_util::tree::{TreeNodeRef, ElementLike};
use std::str::eq_slice;
use style::TElement;
pub struct HTMLDocument {
parent: Document

View file

@ -14,7 +14,7 @@ use layout_interface::{ContentBoxQuery, ContentBoxResponse};
use servo_net::image_cache_task;
use servo_net::image_cache_task::ImageCacheTask;
use servo_util::url::make_url;
use servo_util::tree::ElementLike;
use style::TElement;
pub struct HTMLImageElement {
htmlelement: HTMLElement,

View file

@ -8,7 +8,7 @@ use dom::document::AbstractDocument;
use dom::element::HTMLScriptElementTypeId;
use dom::htmlelement::HTMLElement;
use dom::node::{AbstractNode, Node, ScriptView};
use servo_util::tree::ElementLike;
use style::TElement;
pub struct HTMLScriptElement {
htmlelement: HTMLElement,

View file

@ -21,11 +21,11 @@ use dom::text::Text;
use js::jsapi::{JSObject, JSContext};
use servo_util::slot::{MutSlotRef, Slot, SlotRef};
use servo_util::tree::{TreeNode, TreeNodeRef, TreeNodeRefAsElement};
use std::cast::transmute;
use std::cast;
use std::unstable::raw::Box;
use std::util;
use style::TNode;
//
// The basic Node structure
@ -210,59 +210,17 @@ impl<View> Clone for AbstractNode<View> {
}
}
impl<View> TreeNodeRef<Node<View>> for AbstractNode<View> {
fn node<'a>(&'a self) -> &'a Node<View> {
unsafe {
&(*self.obj).data
}
impl<View> TNode<Element> for AbstractNode<View> {
fn parent_node(&self) -> Option<AbstractNode<View>> {
self.node().parent_node
}
fn mut_node<'a>(&'a self) -> &'a mut Node<View> {
unsafe {
&mut (*self.obj).data
}
fn prev_sibling(&self) -> Option<AbstractNode<View>> {
self.node().prev_sibling
}
fn parent_node(node: &Node<View>) -> Option<AbstractNode<View>> {
node.parent_node
}
fn first_child(node: &Node<View>) -> Option<AbstractNode<View>> {
node.first_child
}
fn last_child(node: &Node<View>) -> Option<AbstractNode<View>> {
node.last_child
}
fn prev_sibling(node: &Node<View>) -> Option<AbstractNode<View>> {
node.prev_sibling
}
fn next_sibling(node: &Node<View>) -> Option<AbstractNode<View>> {
node.next_sibling
}
fn set_parent_node(node: &mut Node<View>, new_parent_node: Option<AbstractNode<View>>) {
let doc = node.owner_doc();
doc.document().wait_until_safe_to_modify_dom();
node.parent_node = new_parent_node
}
fn set_first_child(node: &mut Node<View>, new_first_child: Option<AbstractNode<View>>) {
let doc = node.owner_doc();
doc.document().wait_until_safe_to_modify_dom();
node.first_child = new_first_child
}
fn set_last_child(node: &mut Node<View>, new_last_child: Option<AbstractNode<View>>) {
let doc = node.owner_doc();
doc.document().wait_until_safe_to_modify_dom();
node.last_child = new_last_child
}
fn set_prev_sibling(node: &mut Node<View>, new_prev_sibling: Option<AbstractNode<View>>) {
let doc = node.owner_doc();
doc.document().wait_until_safe_to_modify_dom();
node.prev_sibling = new_prev_sibling
}
fn set_next_sibling(node: &mut Node<View>, new_next_sibling: Option<AbstractNode<View>>) {
let doc = node.owner_doc();
doc.document().wait_until_safe_to_modify_dom();
node.next_sibling = new_next_sibling
fn next_sibling(&self) -> Option<AbstractNode<View>> {
self.node().next_sibling
}
fn is_element(&self) -> bool {
@ -278,17 +236,34 @@ impl<View> TreeNodeRef<Node<View>> for AbstractNode<View> {
_ => false
}
}
}
impl<View> TreeNodeRefAsElement<Node<View>, Element> for AbstractNode<View> {
#[inline]
fn with_imm_element_like<R>(&self, f: &fn(&Element) -> R) -> R {
fn with_element<R>(&self, f: &fn(&Element) -> R) -> R {
self.with_imm_element(f)
}
}
impl<View> AbstractNode<View> {
pub fn node<'a>(&'a self) -> &'a Node<View> {
unsafe {
&(*self.obj).data
}
}
impl<View> TreeNode<AbstractNode<View>> for Node<View> { }
pub fn mut_node<'a>(&'a self) -> &'a mut Node<View> {
unsafe {
&mut (*self.obj).data
}
}
pub fn first_child(&self) -> Option<AbstractNode<View>> {
self.node().first_child
}
pub fn last_child(&self) -> Option<AbstractNode<View>> {
self.node().last_child
}
}
impl<'self, View> AbstractNode<View> {
// Unsafe accessors
@ -335,21 +310,6 @@ impl<'self, View> AbstractNode<View> {
self.node().type_id
}
/// Returns the parent node of this node. Fails if this node is borrowed mutably.
pub fn parent_node(self) -> Option<AbstractNode<View>> {
self.node().parent_node
}
/// Returns the first child of this node. Fails if this node is borrowed mutably.
pub fn first_child(self) -> Option<AbstractNode<View>> {
self.node().first_child
}
/// Returns the last child of this node. Fails if this node is borrowed mutably.
pub fn last_child(self) -> Option<AbstractNode<View>> {
self.node().last_child
}
/// Returns the previous sibling of this node. Fails if this node is borrowed mutably.
pub fn prev_sibling(self) -> Option<AbstractNode<View>> {
self.node().prev_sibling
@ -562,6 +522,14 @@ impl<'self, View> AbstractNode<View> {
format!("{:?}", self.type_id())
}
//
// Convenience accessors
//
fn is_leaf(&self) -> bool {
self.first_child().is_none()
}
pub fn children(&self) -> AbstractNodeChildrenIterator<View> {
self.node().children()
}
@ -601,8 +569,123 @@ impl AbstractNode<ScriptView> {
document.document().content_changed();
}
//
// Pointer stitching
//
/// Adds a new child to the end of this node's list of children.
///
/// Fails unless `new_child` is disconnected from the tree.
fn add_child(&self,
new_child: AbstractNode<ScriptView>,
before: Option<AbstractNode<ScriptView>>) {
let this_node = self.mut_node();
let new_child_node = new_child.mut_node();
assert!(new_child_node.parent_node.is_none());
assert!(new_child_node.prev_sibling.is_none());
assert!(new_child_node.next_sibling.is_none());
match before {
Some(before) => {
let before_node = before.mut_node();
// XXX Should assert that parent is self.
assert!(before_node.parent_node.is_some());
before_node.set_prev_sibling(Some(new_child.clone()));
new_child_node.set_next_sibling(Some(before.clone()));
match before_node.prev_sibling {
None => {
// XXX Should assert that before is the first child of
// self.
this_node.set_first_child(Some(new_child.clone()));
},
Some(prev_sibling) => {
let prev_sibling_node = prev_sibling.mut_node();
prev_sibling_node.set_next_sibling(Some(new_child.clone()));
new_child_node.set_prev_sibling(Some(prev_sibling.clone()));
},
}
},
None => {
match this_node.last_child {
None => this_node.set_first_child(Some(new_child.clone())),
Some(last_child) => {
let last_child_node = last_child.mut_node();
assert!(last_child_node.next_sibling.is_none());
last_child_node.set_next_sibling(Some(new_child.clone()));
new_child_node.set_prev_sibling(Some(last_child.clone()));
}
}
this_node.set_last_child(Some(new_child.clone()));
},
}
new_child_node.set_parent_node(Some((*self).clone()));
}
/// Removes the given child from this node's list of children.
///
/// Fails unless `child` is a child of this node. (FIXME: This is not yet checked.)
fn remove_child(&self, child: AbstractNode<ScriptView>) {
let this_node = self.mut_node();
let child_node = child.mut_node();
assert!(child_node.parent_node.is_some());
match child_node.prev_sibling {
None => this_node.set_first_child(child_node.next_sibling),
Some(prev_sibling) => {
let prev_sibling_node = prev_sibling.mut_node();
prev_sibling_node.set_next_sibling(child_node.next_sibling);
}
}
match child_node.next_sibling {
None => this_node.set_last_child(child_node.prev_sibling),
Some(next_sibling) => {
let next_sibling_node = next_sibling.mut_node();
next_sibling_node.set_prev_sibling(child_node.prev_sibling);
}
}
child_node.set_prev_sibling(None);
child_node.set_next_sibling(None);
child_node.set_parent_node(None);
}
//
// Low-level pointer stitching wrappers
//
fn set_parent_node(&self, new_parent_node: Option<AbstractNode<ScriptView>>) {
let node = self.mut_node();
node.set_parent_node(new_parent_node)
}
fn set_first_child(&self, new_first_child: Option<AbstractNode<ScriptView>>) {
let node = self.mut_node();
node.set_first_child(new_first_child)
}
fn set_last_child(&self, new_last_child: Option<AbstractNode<ScriptView>>) {
let node = self.mut_node();
node.set_last_child(new_last_child)
}
fn set_prev_sibling(&self, new_prev_sibling: Option<AbstractNode<ScriptView>>) {
let node = self.mut_node();
node.set_prev_sibling(new_prev_sibling)
}
fn set_next_sibling(&self, new_next_sibling: Option<AbstractNode<ScriptView>>) {
let node = self.mut_node();
node.set_next_sibling(new_next_sibling)
}
}
//
// Iteration and traversal
//
impl<View> Iterator<AbstractNode<View>> for AbstractNodeChildrenIterator<View> {
fn next(&mut self) -> Option<AbstractNode<View>> {
let node = self.current_node;
@ -613,6 +696,86 @@ impl<View> Iterator<AbstractNode<View>> for AbstractNodeChildrenIterator<View> {
}
}
pub struct AncestorIterator<View> {
priv current: Option<AbstractNode<View>>,
}
impl<View> Iterator<AbstractNode<View>> for AncestorIterator<View> {
fn next(&mut self) -> Option<AbstractNode<View>> {
if self.current.is_none() {
return None;
}
// FIXME: Do we need two clones here?
let x = self.current.get_ref().clone();
self.current = x.parent_node();
Some(x.clone())
}
}
// FIXME: Do this without precomputing a vector of refs.
// Easy for preorder; harder for postorder.
pub struct TreeIterator<View> {
priv nodes: ~[AbstractNode<View>],
priv index: uint,
}
impl<View> TreeIterator<View> {
fn new(nodes: ~[AbstractNode<View>]) -> TreeIterator<View> {
TreeIterator {
nodes: nodes,
index: 0,
}
}
}
impl<View> Iterator<AbstractNode<View>> for TreeIterator<View> {
fn next(&mut self) -> Option<AbstractNode<View>> {
if self.index >= self.nodes.len() {
None
} else {
let v = self.nodes[self.index].clone();
self.index += 1;
Some(v)
}
}
}
fn gather<View>(cur: &AbstractNode<View>, refs: &mut ~[AbstractNode<View>], postorder: bool) {
if !postorder {
refs.push(cur.clone());
}
for kid in cur.children() {
gather(&kid, refs, postorder)
}
if postorder {
refs.push(cur.clone());
}
}
impl<View> AbstractNode<View> {
/// Iterates over all ancestors of this node.
pub fn ancestors(&self) -> AncestorIterator<View> {
AncestorIterator {
current: self.parent_node(),
}
}
/// Iterates over this node and all its descendants, in preorder.
pub fn traverse_preorder(&self) -> TreeIterator<View> {
let mut nodes = ~[];
gather(self, &mut nodes, false);
TreeIterator::new(nodes)
}
/// Iterates over this node and all its descendants, in postorder.
pub fn sequential_traverse_postorder(&self) -> TreeIterator<View> {
let mut nodes = ~[];
gather(self, &mut nodes, true);
TreeIterator::new(nodes)
}
}
impl<View> Node<View> {
pub fn owner_doc(&self) -> AbstractDocument {
self.owner_doc.unwrap()
@ -1215,6 +1378,41 @@ impl Node<ScriptView> {
pub fn HasAttributes(&self) -> bool {
false
}
//
// Low-level pointer stitching
//
pub fn set_parent_node(&mut self, new_parent_node: Option<AbstractNode<ScriptView>>) {
let doc = self.owner_doc();
doc.document().wait_until_safe_to_modify_dom();
self.parent_node = new_parent_node
}
pub fn set_first_child(&mut self, new_first_child: Option<AbstractNode<ScriptView>>) {
let doc = self.owner_doc();
doc.document().wait_until_safe_to_modify_dom();
self.first_child = new_first_child
}
pub fn set_last_child(&mut self, new_last_child: Option<AbstractNode<ScriptView>>) {
let doc = self.owner_doc();
doc.document().wait_until_safe_to_modify_dom();
self.last_child = new_last_child
}
pub fn set_prev_sibling(&mut self, new_prev_sibling: Option<AbstractNode<ScriptView>>) {
let doc = self.owner_doc();
doc.document().wait_until_safe_to_modify_dom();
self.prev_sibling = new_prev_sibling
}
pub fn set_next_sibling(&mut self, new_next_sibling: Option<AbstractNode<ScriptView>>) {
let doc = self.owner_doc();
doc.document().wait_until_safe_to_modify_dom();
self.next_sibling = new_next_sibling
}
}
impl Reflectable for Node<ScriptView> {

View file

@ -20,7 +20,6 @@ use js::jsapi::JSContext;
use servo_msg::constellation_msg::SubpageId;
use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::{Load, Payload, Done, ResourceTask, load_whole_resource};
use servo_util::tree::{TreeNodeRef, ElementLike};
use servo_util::url::make_url;
use std::cast;
use std::cell::Cell;
@ -29,7 +28,7 @@ use std::comm;
use std::from_str::FromStr;
use std::str::eq_slice;
use std::str;
use style::Stylesheet;
use style::{Stylesheet, TElement};
macro_rules! handle_element(
($document: expr,

View file

@ -45,7 +45,6 @@ use servo_msg::constellation_msg;
use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::ResourceTask;
use servo_util::geometry::to_frac_px;
use servo_util::tree::{TreeNode, TreeNodeRef, ElementLike};
use servo_util::url::make_url;
use std::cell::Cell;
use std::comm::{Port, SharedChan};
@ -54,6 +53,7 @@ use std::ptr;
use std::str::eq_slice;
use std::task::{spawn_sched, SingleThreaded};
use std::util::replace;
use style::{TElement, TNode};
/// Messages used to control the script task.
pub enum ScriptMsg {

View file

@ -0,0 +1,28 @@
/* 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/. */
//! Traits that nodes must implement. Breaks the otherwise-cyclic dependency between script and
//! style.
/// FIXME(pcwalton): Should not require `Clone` and should instead return references. When this
/// happens this will need to only be implemented for `AbstractNode<LayoutView>`.
pub trait TNode<E:TElement> : Clone {
fn parent_node(&self) -> Option<Self>;
fn prev_sibling(&self) -> Option<Self>;
fn next_sibling(&self) -> Option<Self>;
fn is_document(&self) -> bool;
fn is_element(&self) -> bool;
/// FIXME(pcwalton): This should not use the `with` pattern.
fn with_element<R>(&self, f: &fn(&E) -> R) -> R;
}
pub trait TElement {
fn get_attr(&self, namespace: Option<~str>, attr: &str) -> Option<~str>;
fn get_link(&self) -> Option<~str>;
fn get_local_name<'a>(&'a self) -> &'a str;
fn get_namespace_url<'a>(&'a self) -> &'a str;
}

View file

@ -2,18 +2,17 @@
* 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 std::ascii::StrAsciiExt;
use std::hashmap::HashMap;
use extra::arc::Arc;
use extra::sort::tim_sort;
use std::ascii::StrAsciiExt;
use std::hashmap::HashMap;
use std::str;
use media_queries::{Device, Screen};
use node::{TElement, TNode};
use properties::{PropertyDeclaration, PropertyDeclarationBlock};
use selectors::*;
use stylesheets::{Stylesheet, iter_style_rules};
use media_queries::{Device, Screen};
use properties::{PropertyDeclaration, PropertyDeclarationBlock};
use servo_util::tree::{TreeNodeRefAsElement, TreeNode, ElementLike};
use std::str;
pub enum StylesheetOrigin {
UserAgentOrigin,
@ -67,16 +66,15 @@ impl SelectorMap {
///
/// Extract matching rules as per node's ID, classes, tag name, etc..
/// Sort the Rules at the end to maintain cascading order.
fn get_all_matching_rules<N:TreeNode<T>,
T:TreeNodeRefAsElement<N,E>,
E:ElementLike>(
fn get_all_matching_rules<E:TElement,
N:TNode<E>>(
&self,
node: &T,
node: &N,
pseudo_element: Option<PseudoElement>,
matching_rules_list: &mut ~[Rule]) {
// At the end, we're going to sort the rules that we added, so remember where we began.
let init_len = matching_rules_list.len();
node.with_imm_element_like(|element: &E| {
node.with_element(|element: &E| {
match element.get_attr(None, "id") {
Some(id) => {
SelectorMap::get_matching_rules_from_hash(node,
@ -118,10 +116,9 @@ impl SelectorMap {
tim_sort(matching_rules_list.mut_slice_from(init_len));
}
fn get_matching_rules_from_hash<N:TreeNode<T>,
T:TreeNodeRefAsElement<N,E>,
E:ElementLike>(
node: &T,
fn get_matching_rules_from_hash<E:TElement,
N:TNode<E>>(
node: &N,
pseudo_element: Option<PseudoElement>,
hash: &HashMap<~str,~[Rule]>,
key: &str,
@ -135,10 +132,9 @@ impl SelectorMap {
}
/// Adds rules in `rules` that match `node` to the `matching_rules` list.
fn get_matching_rules<N:TreeNode<T>,
T:TreeNodeRefAsElement<N,E>,
E:ElementLike>(
node: &T,
fn get_matching_rules<E:TElement,
N:TNode<E>>(
node: &N,
pseudo_element: Option<PseudoElement>,
rules: &[Rule],
matching_rules: &mut ~[Rule]) {
@ -301,11 +297,10 @@ impl Stylist {
/// Returns the applicable CSS declarations for the given element. This corresponds to
/// `ElementRuleCollector` in WebKit.
pub fn get_applicable_declarations<N:TreeNode<T>,
T:TreeNodeRefAsElement<N,E>,
E:ElementLike>(
pub fn get_applicable_declarations<E:TElement,
N:TNode<E>>(
&self,
element: &T,
element: &N,
style_attribute: Option<&PropertyDeclarationBlock>,
pseudo_element: Option<PseudoElement>)
-> ~[Arc<~[PropertyDeclaration]>] {
@ -421,7 +416,6 @@ struct Rule {
stylesheet_index: uint,
}
impl Ord for Rule {
#[inline]
fn lt(&self, other: &Rule) -> bool {
@ -431,16 +425,19 @@ impl Ord for Rule {
}
}
#[inline]
fn matches_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
selector: &Selector, element: &T, pseudo_element: Option<PseudoElement>) -> bool {
fn matches_selector<E:TElement,
N:TNode<E>>(
selector: &Selector,
element: &N,
pseudo_element: Option<PseudoElement>)
-> bool {
selector.pseudo_element == pseudo_element &&
matches_compound_selector::<N, T, E>(&selector.compound_selectors, element)
matches_compound_selector::<E,N>(&selector.compound_selectors, element)
}
fn matches_compound_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
selector: &CompoundSelector, element: &T) -> bool {
fn matches_compound_selector<E:TElement,N:TNode<E>>(selector: &CompoundSelector, element: &N)
-> bool {
if !do selector.simple_selectors.iter().all |simple_selector| {
matches_simple_selector(simple_selector, element)
} {
@ -455,12 +452,12 @@ fn matches_compound_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: E
NextSibling => (true, true),
LaterSibling => (true, false),
};
let mut node = element.clone();
let mut node = (*element).clone();
loop {
let next_node = if siblings {
node.node().prev_sibling()
node.prev_sibling()
} else {
node.node().parent_node()
node.parent_node()
};
match next_node {
None => return false,
@ -479,25 +476,24 @@ fn matches_compound_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: E
}
#[inline]
fn matches_simple_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
selector: &SimpleSelector, element: &T) -> bool {
fn matches_simple_selector<E:TElement,N:TNode<E>>(selector: &SimpleSelector, element: &N) -> bool {
match *selector {
// TODO: case-sensitivity depends on the document type
// TODO: intern element names
LocalNameSelector(ref name) => {
do element.with_imm_element_like |element: &E| {
do element.with_element |element: &E| {
element.get_local_name().eq_ignore_ascii_case(name.as_slice())
}
}
NamespaceSelector(ref url) => {
do element.with_imm_element_like |element: &E| {
do element.with_element |element: &E| {
element.get_namespace_url() == url.as_slice()
}
}
// TODO: case-sensitivity depends on the document type and quirks mode
// TODO: cache and intern IDs on elements.
IDSelector(ref id) => {
do element.with_imm_element_like |element: &E| {
do element.with_element |element: &E| {
match element.get_attr(None, "id") {
Some(attr) => str::eq_slice(attr, *id),
None => false
@ -506,7 +502,7 @@ fn matches_simple_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: Ele
}
// TODO: cache and intern classe names on elements.
ClassSelector(ref class) => {
do element.with_imm_element_like |element: &E| {
do element.with_element |element: &E| {
match element.get_attr(None, "class") {
None => false,
// TODO: case-sensitivity depends on the document type and quirks mode
@ -537,12 +533,12 @@ fn matches_simple_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: Ele
AnyLink => {
do element.with_imm_element_like |element: &E| {
do element.with_element |element: &E| {
element.get_link().is_some()
}
}
Link => {
do element.with_imm_element_like |element: &E| {
do element.with_element |element: &E| {
match element.get_link() {
Some(url) => !url_is_visited(url),
None => false,
@ -550,7 +546,7 @@ fn matches_simple_selector<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: Ele
}
}
Visited => {
do element.with_imm_element_like |element: &E| {
do element.with_element |element: &E| {
match element.get_link() {
Some(url) => url_is_visited(url),
None => false,
@ -589,12 +585,18 @@ fn url_is_visited(_url: &str) -> bool {
}
#[inline]
fn matches_generic_nth_child<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
element: &T, a: i32, b: i32, is_of_type: bool, is_from_end: bool) -> bool {
fn matches_generic_nth_child<E:TElement,
N:TNode<E>>(
element: &N,
a: i32,
b: i32,
is_of_type: bool,
is_from_end: bool)
-> bool {
let mut node = element.clone();
// fail if we can't find a parent or if the node is the root element
// of the document (Cf. Selectors Level 3)
match node.node().parent_node() {
match node.parent_node() {
Some(parent) => if parent.is_document() {
return false;
},
@ -604,7 +606,7 @@ fn matches_generic_nth_child<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: E
let mut element_local_name = "";
let mut element_namespace = "";
if is_of_type {
do element.with_imm_element_like |element: &E| {
do element.with_element |element: &E| {
element_local_name = element.get_local_name();
element_namespace = element.get_namespace_url();
}
@ -613,12 +615,12 @@ fn matches_generic_nth_child<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: E
let mut index = 1;
loop {
if is_from_end {
match node.node().next_sibling() {
match node.next_sibling() {
None => break,
Some(next_sibling) => node = next_sibling
}
} else {
match node.node().prev_sibling() {
match node.prev_sibling() {
None => break,
Some(prev_sibling) => node = prev_sibling
}
@ -626,7 +628,7 @@ fn matches_generic_nth_child<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: E
if node.is_element() {
if is_of_type {
do node.with_imm_element_like |node: &E| {
do node.with_element |node: &E| {
if element_local_name == node.get_local_name() &&
element_namespace == node.get_namespace_url() {
index += 1;
@ -648,27 +650,25 @@ fn matches_generic_nth_child<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: E
}
#[inline]
fn matches_root<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
element: &T) -> bool {
match element.node().parent_node() {
fn matches_root<E:TElement,N:TNode<E>>(element: &N) -> bool {
match element.parent_node() {
Some(parent) => parent.is_document(),
None => false
}
}
#[inline]
fn matches_first_child<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
element: &T) -> bool {
fn matches_first_child<E:TElement,N:TNode<E>>(element: &N) -> bool {
let mut node = element.clone();
loop {
match node.node().prev_sibling() {
match node.prev_sibling() {
Some(prev_sibling) => {
node = prev_sibling;
if node.is_element() {
return false
}
},
None => match node.node().parent_node() {
None => match node.parent_node() {
// Selectors level 3 says :first-child does not match the
// root of the document; Warning, level 4 says, for the time
// being, the contrary...
@ -680,18 +680,17 @@ fn matches_first_child<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: Element
}
#[inline]
fn matches_last_child<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
element: &T) -> bool {
fn matches_last_child<E:TElement,N:TNode<E>>(element: &N) -> bool {
let mut node = element.clone();
loop {
match node.node().next_sibling() {
match node.next_sibling() {
Some(next_sibling) => {
node = next_sibling;
if node.is_element() {
return false
}
},
None => match node.node().parent_node() {
None => match node.parent_node() {
// Selectors level 3 says :last-child does not match the
// root of the document; Warning, level 4 says, for the time
// being, the contrary...
@ -703,9 +702,13 @@ fn matches_last_child<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementL
}
#[inline]
fn match_attribute<N: TreeNode<T>, T: TreeNodeRefAsElement<N, E>, E: ElementLike>(
attr: &AttrSelector, element: &T, f: &fn(&str)-> bool) -> bool {
do element.with_imm_element_like |element: &E| {
fn match_attribute<E:TElement,
N:TNode<E>>(
attr: &AttrSelector,
element: &N,
f: &fn(&str) -> bool)
-> bool {
do element.with_element |element: &E| {
// FIXME: avoid .clone() here? See #1367
match element.get_attr(attr.namespace.clone(), attr.name) {
None => false,

View file

@ -25,6 +25,7 @@ pub use selector_matching::{Stylist, StylesheetOrigin, UserAgentOrigin, AuthorOr
pub use properties::{cascade, PropertyDeclaration, ComputedValues, computed_values};
pub use properties::{PropertyDeclarationBlock, parse_style_attribute}; // Style attributes
pub use errors::with_errors_silenced;
pub use node::{TElement, TNode};
mod stylesheets;
mod errors;
@ -32,5 +33,6 @@ mod selectors;
mod selector_matching;
mod properties;
mod namespaces;
mod node;
mod media_queries;
mod parsing_utils;

View file

@ -1,354 +0,0 @@
/* 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/. */
//! Helper functions for garbage collected doubly-linked trees.
// Macros to make add_child etc. less painful to write.
// Code outside this module should instead implement TreeNode
// and use its default methods.
macro_rules! get(
($node:expr, $fun:ident) => (
{
let val: Option<Self> = TreeNodeRef::<Node>::$fun($node);
val
}
)
)
macro_rules! set(
($node:expr, $fun:ident, $val:expr) => (
{
let val: Option<Self> = $val;
TreeNodeRef::<Node>::$fun($node, val)
}
)
)
pub struct ChildIterator<Ref> {
priv current: Option<Ref>,
}
impl<Node, Ref: TreeNodeRef<Node>> Iterator<Ref> for ChildIterator<Ref> {
fn next(&mut self) -> Option<Ref> {
if self.current.is_none() {
return None;
}
// FIXME: Do we need two clones here?
let x = self.current.get_ref().clone();
self.current = TreeNodeRef::<Node>::next_sibling(x.node());
Some(x.clone())
}
}
pub struct AncestorIterator<Ref> {
priv current: Option<Ref>,
}
impl<Node, Ref: TreeNodeRef<Node>> Iterator<Ref> for AncestorIterator<Ref> {
fn next(&mut self) -> Option<Ref> {
if self.current.is_none() {
return None;
}
// FIXME: Do we need two clones here?
let x = self.current.get_ref().clone();
self.current = TreeNodeRef::<Node>::parent_node(x.node());
Some(x.clone())
}
}
// FIXME: Do this without precomputing a vector of refs.
// Easy for preorder; harder for postorder.
pub struct TreeIterator<Ref> {
priv nodes: ~[Ref],
priv index: uint,
}
impl<Ref> TreeIterator<Ref> {
fn new(nodes: ~[Ref]) -> TreeIterator<Ref> {
TreeIterator {
nodes: nodes,
index: 0,
}
}
}
impl<Ref: Clone> Iterator<Ref> for TreeIterator<Ref> {
fn next(&mut self) -> Option<Ref> {
if self.index >= self.nodes.len() {
None
} else {
let v = self.nodes[self.index].clone();
self.index += 1;
Some(v)
}
}
}
/// A type implementing TreeNodeRef<Node> is a clonable reference to an underlying
/// node type Node.
///
/// We have to define both ref and node operations in the same trait, which makes
/// the latter more annoying to call (as static methods). But we provide non-static
/// proxies in trait TreeNode below.
pub trait TreeNodeRef<Node>: Clone {
// Fundamental operations on refs.
/// Borrows this node as immutable.
fn node<'a>(&'a self) -> &'a Node;
/// Borrows this node as mutable.
fn mut_node<'a>(&'a self) -> &'a mut Node;
// Fundamental operations on nodes.
/// Returns the parent of this node.
fn parent_node(node: &Node) -> Option<Self>;
/// Returns the first child of this node.
fn first_child(node: &Node) -> Option<Self>;
/// Returns the last child of this node.
fn last_child(node: &Node) -> Option<Self>;
/// Returns the previous sibling of this node.
fn prev_sibling(node: &Node) -> Option<Self>;
/// Returns the next sibling of this node.
fn next_sibling(node: &Node) -> Option<Self>;
/// Sets the parent of this node.
fn set_parent_node(node: &mut Node, new_parent: Option<Self>);
/// Sets the first child of this node.
fn set_first_child(node: &mut Node, new_first_child: Option<Self>);
/// Sets the last child of this node.
fn set_last_child(node: &mut Node, new_last_child: Option<Self>);
/// Sets the previous sibling of this node.
fn set_prev_sibling(node: &mut Node, new_prev_sibling: Option<Self>);
/// Sets the next sibling of this node.
fn set_next_sibling(node: &mut Node, new_next_sibling: Option<Self>);
// The tree utilities, operating on refs mostly.
/// Returns true if this node is disconnected from the tree or has no children.
fn is_leaf(&self) -> bool {
(get!(self.node(), first_child)).is_none()
}
/// Adds a new child to the end of this node's list of children.
///
/// Fails unless `new_child` is disconnected from the tree.
fn add_child(&self, new_child: Self, before: Option<Self>) {
let this_node = self.mut_node();
let new_child_node = new_child.mut_node();
assert!((get!(new_child_node, parent_node)).is_none());
assert!((get!(new_child_node, prev_sibling)).is_none());
assert!((get!(new_child_node, next_sibling)).is_none());
match before {
Some(before) => {
let before_node = before.mut_node();
// XXX Should assert that parent is self.
assert!((get!(before_node, parent_node)).is_some());
set!(before_node, set_prev_sibling, Some(new_child.clone()));
set!(new_child_node, set_next_sibling, Some(before.clone()));
match get!(before_node, prev_sibling) {
None => {
// XXX Should assert that before is the first child of
// self.
set!(this_node, set_first_child, Some(new_child.clone()));
},
Some(prev_sibling) => {
let prev_sibling_node = prev_sibling.mut_node();
set!(prev_sibling_node, set_next_sibling, Some(new_child.clone()));
set!(new_child_node, set_prev_sibling, Some(prev_sibling.clone()));
},
}
},
None => {
match get!(this_node, last_child) {
None => set!(this_node, set_first_child, Some(new_child.clone())),
Some(last_child) => {
let last_child_node = last_child.mut_node();
assert!((get!(last_child_node, next_sibling)).is_none());
set!(last_child_node, set_next_sibling, Some(new_child.clone()));
set!(new_child_node, set_prev_sibling, Some(last_child.clone()));
}
}
set!(this_node, set_last_child, Some(new_child.clone()));
},
}
set!(new_child_node, set_parent_node, Some((*self).clone()));
}
/// Removes the given child from this node's list of children.
///
/// Fails unless `child` is a child of this node. (FIXME: This is not yet checked.)
fn remove_child(&self, child: Self) {
let this_node = self.mut_node();
let child_node = child.mut_node();
assert!((get!(child_node, parent_node)).is_some());
match get!(child_node, prev_sibling) {
None => set!(this_node, set_first_child, get!(child_node, next_sibling)),
Some(prev_sibling) => {
let prev_sibling_node = prev_sibling.mut_node();
set!(prev_sibling_node, set_next_sibling, get!(child_node, next_sibling));
}
}
match get!(child_node, next_sibling) {
None => set!(this_node, set_last_child, get!(child_node, prev_sibling)),
Some(next_sibling) => {
let next_sibling_node = next_sibling.mut_node();
set!(next_sibling_node, set_prev_sibling, get!(child_node, prev_sibling));
}
}
set!(child_node, set_prev_sibling, None);
set!(child_node, set_next_sibling, None);
set!(child_node, set_parent_node, None);
}
/// Iterates over all children of this node.
fn children(&self) -> ChildIterator<Self> {
ChildIterator {
current: get!(self.node(), first_child),
}
}
/// Iterates over all ancestors of this node.
fn ancestors(&self) -> AncestorIterator<Self> {
AncestorIterator {
current: get!(self.node(), parent_node),
}
}
/// Iterates over this node and all its descendants, in preorder.
fn traverse_preorder(&self) -> TreeIterator<Self> {
self.traverse_preorder_prune(|_| false)
}
/// Iterates over this node and all its descendants, in postorder.
fn traverse_postorder(&self) -> TreeIterator<Self> {
self.traverse_postorder_prune(|_| false)
}
/// Like traverse_preorder but calls 'prune' first on each node. If it returns true then we
/// skip the whole subtree but continue iterating.
fn traverse_preorder_prune(&self, prune: &fn(&Self) -> bool) -> TreeIterator<Self> {
let mut nodes = ~[];
gather(self, &mut nodes, false, prune);
TreeIterator::new(nodes)
}
/// Like traverse_postorder but calls 'prune' first on each node. If it returns true then we
/// skip the whole subtree but continue iterating.
///
/// NB: 'prune' is called *before* traversing children, even though this is a
/// postorder traversal.
fn traverse_postorder_prune(&self, prune: &fn(&Self) -> bool) -> TreeIterator<Self> {
let mut nodes = ~[];
gather(self, &mut nodes, true, prune);
TreeIterator::new(nodes)
}
fn is_element(&self) -> bool;
fn is_document(&self) -> bool;
}
pub trait TreeNodeRefAsElement<Node, E: ElementLike>: TreeNodeRef<Node> {
fn with_imm_element_like<R>(&self, f: &fn(&E) -> R) -> R;
}
fn gather<Node, Ref: TreeNodeRef<Node>>(cur: &Ref, refs: &mut ~[Ref],
postorder: bool, prune: &fn(&Ref) -> bool) {
// prune shouldn't mutate, so don't clone
if prune(cur) {
return;
}
if !postorder {
refs.push(cur.clone());
}
for kid in cur.children() {
// FIXME: Work around rust#2202. We should be able to pass the callback directly.
gather(&kid, refs, postorder, |a| prune(a))
}
if postorder {
refs.push(cur.clone());
}
}
/// Access the fields of a node without a static TreeNodeRef method call.
/// If you make an impl TreeNodeRef<Node> for Ref then you should also make
/// impl TreeNode<Ref> for Node with an empty body.
pub trait TreeNode<Ref: TreeNodeRef<Self>> {
/// Returns the parent of this node.
fn parent_node(&self) -> Option<Ref> {
TreeNodeRef::<Self>::parent_node(self)
}
/// Returns the first child of this node.
fn first_child(&self) -> Option<Ref> {
TreeNodeRef::<Self>::first_child(self)
}
/// Returns the last child of this node.
fn last_child(&self) -> Option<Ref> {
TreeNodeRef::<Self>::last_child(self)
}
/// Returns the previous sibling of this node.
fn prev_sibling(&self) -> Option<Ref> {
TreeNodeRef::<Self>::prev_sibling(self)
}
/// Returns the next sibling of this node.
fn next_sibling(&self) -> Option<Ref> {
TreeNodeRef::<Self>::next_sibling(self)
}
/// Sets the parent of this node.
fn set_parent_node(&mut self, new_parent: Option<Ref>) {
TreeNodeRef::<Self>::set_parent_node(self, new_parent)
}
/// Sets the first child of this node.
fn set_first_child(&mut self, new_first_child: Option<Ref>) {
TreeNodeRef::<Self>::set_first_child(self, new_first_child)
}
/// Sets the last child of this node.
fn set_last_child(&mut self, new_last_child: Option<Ref>) {
TreeNodeRef::<Self>::set_last_child(self, new_last_child)
}
/// Sets the previous sibling of this node.
fn set_prev_sibling(&mut self, new_prev_sibling: Option<Ref>) {
TreeNodeRef::<Self>::set_prev_sibling(self, new_prev_sibling)
}
/// Sets the next sibling of this node.
fn set_next_sibling(&mut self, new_next_sibling: Option<Ref>) {
TreeNodeRef::<Self>::set_next_sibling(self, new_next_sibling)
}
}
pub trait ElementLike {
fn get_local_name<'a>(&'a self) -> &'a str;
fn get_namespace_url<'a>(&'a self) -> &'a str;
fn get_attr(&self, ns_url: Option<~str>, name: &str) -> Option<~str>;
fn get_link(&self) -> Option<~str>;
}

View file

@ -18,7 +18,6 @@ pub mod geometry;
pub mod range;
pub mod slot;
pub mod time;
pub mod tree;
pub mod url;
pub mod vec;
pub mod debug;