auto merge of #1561 : SimonSapin/servo/refactor-namespace, r=pcwalton

This builds on top of #1560 (… which I realize now may confuse Critic)

This refactors Namespace to reduce the overall amount of copying and conversion. In particular, it makes parsed selectors contain Namespace enums rather than strings.
This commit is contained in:
bors-servo 2014-01-25 13:19:02 -08:00
commit edda06115a
18 changed files with 93 additions and 78 deletions

View file

@ -22,6 +22,7 @@ use servo_net::local_image_cache::LocalImageCache;
use servo_util::geometry::Au;
use servo_util::geometry;
use servo_util::range::*;
use servo_util::namespace;
use std::cast;
use std::cell::RefCell;
use std::cmp::ApproxEq;
@ -127,7 +128,7 @@ impl ImageBoxInfo {
fn convert_length(node: &LayoutNode, name: &str) -> Option<Au> {
node.with_element(|element| {
element.get_attr(None, name).and_then(|string| {
element.get_attr(&namespace::Null, name).and_then(|string| {
let n: Option<int> = FromStr::from_str(string);
n
}).and_then(|pixels| Some(Au::from_px(pixels)))

View file

@ -19,11 +19,11 @@ use script::dom::element::{Element, HTMLAreaElementTypeId, HTMLAnchorElementType
use script::dom::element::{HTMLLinkElementTypeId};
use script::dom::htmliframeelement::HTMLIFrameElement;
use script::dom::htmlimageelement::HTMLImageElement;
use script::dom::namespace;
use script::dom::namespace::Namespace;
use script::dom::node::{AbstractNode, DocumentNodeTypeId, ElementNodeTypeId, Node, NodeTypeId};
use script::dom::text::Text;
use servo_msg::constellation_msg::{PipelineId, SubpageId};
use servo_util::namespace;
use servo_util::namespace::Namespace;
use std::cast;
use style::{PropertyDeclarationBlock, TElement, TNode, AttrSelector};
@ -289,11 +289,16 @@ impl<'ln> TNode<LayoutElement<'ln>> for LayoutNode<'ln> {
} else {
attr.name.as_slice()
};
// FIXME: avoid .clone() here? See #1367
match element.get_attr(attr.namespace.clone(), name) {
match attr.namespace {
Some(ref ns) => {
match element.get_attr(ns, name) {
Some(value) => test(value),
None => false,
}
},
// FIXME: support `*|attr`, attribute selectors in any namespace
None => return false,
}
})
}
}
@ -399,17 +404,16 @@ impl<'le> TElement for LayoutElement<'le> {
}
#[inline]
fn get_namespace_url<'a>(&'a self) -> &'a str {
self.element.namespace.to_str().unwrap_or("")
fn get_namespace<'a>(&'a self) -> &'a Namespace {
&self.element.namespace
}
#[inline]
fn get_attr(&self, ns_url: Option<~str>, name: &str) -> Option<&'static str> {
let namespace = Namespace::from_str(ns_url);
fn get_attr(&self, namespace: &Namespace, name: &str) -> Option<&'static str> {
unsafe { self.element.get_attr_val_for_layout(namespace, name) }
}
fn get_link(&self) -> Option<~str> {
fn get_link(&self) -> Option<&'static str> {
// FIXME: This is HTML only.
match self.element.node.type_id {
// http://www.whatwg.org/specs/web-apps/current-work/multipage/selectors.html#
@ -417,8 +421,7 @@ impl<'le> TElement for LayoutElement<'le> {
ElementNodeTypeId(HTMLAnchorElementTypeId) |
ElementNodeTypeId(HTMLAreaElementTypeId) |
ElementNodeTypeId(HTMLLinkElementTypeId) => {
unsafe { self.element.get_attr_val_for_layout(namespace::Null, "href") }
.map(|val| val.to_owned())
unsafe { self.element.get_attr_val_for_layout(&namespace::Null, "href") }
}
_ => None,
}

View file

@ -5,8 +5,8 @@
use dom::bindings::codegen::AttrBinding;
use dom::bindings::utils::{Reflectable, Reflector, DOMString};
use dom::bindings::utils::reflect_dom_object;
use dom::namespace::{Namespace, Null};
use dom::window::Window;
use servo_util::namespace::{Namespace, Null};
use std::util;
@ -89,7 +89,10 @@ impl Attr {
}
pub fn GetNamespaceURI(&self) -> Option<DOMString> {
self.namespace.to_str().map(|s| s.to_owned())
match self.namespace.to_str() {
"" => None,
url => Some(url.to_owned()),
}
}
pub fn GetPrefix(&self) -> Option<DOMString> {

View file

@ -16,7 +16,6 @@ use dom::event::{AbstractEvent, Event};
use dom::htmlcollection::HTMLCollection;
use dom::htmldocument::HTMLDocument;
use dom::mouseevent::MouseEvent;
use dom::namespace::Null;
use dom::node::{AbstractNode, Node, ElementNodeTypeId, DocumentNodeTypeId};
use dom::text::Text;
use dom::uievent::UIEvent;
@ -24,6 +23,7 @@ use dom::window::Window;
use dom::htmltitleelement::HTMLTitleElement;
use html::hubbub_html_parser::build_element_from_tag;
use layout_interface::{DocumentDamageLevel, ContentChangedDocumentDamage};
use servo_util::namespace::Null;
use js::jsapi::{JSObject, JSContext, JSTracer};
use std::ascii::StrAsciiExt;

View file

@ -7,7 +7,7 @@
use dom::attr::Attr;
use dom::attrlist::AttrList;
use dom::bindings::utils::{Reflectable, DOMString, ErrorResult, Fallible, Reflector};
use dom::bindings::utils::NamespaceError;
use dom::bindings::utils::{null_str_as_empty_ref, NamespaceError};
use dom::bindings::utils::{InvalidCharacter, QName, Name, InvalidXMLName, xml_name_type};
use dom::htmlcollection::HTMLCollection;
use dom::clientrect::ClientRect;
@ -15,13 +15,13 @@ use dom::clientrectlist::ClientRectList;
use dom::document::AbstractDocument;
use dom::node::{AbstractNode, ElementNodeTypeId, Node, NodeIterator};
use dom::document;
use dom::namespace;
use dom::namespace::{Namespace, Null};
use dom::htmlserializer::serialize;
use layout_interface::{ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery};
use layout_interface::{ContentBoxesResponse, ContentChangedDocumentDamage};
use layout_interface::{MatchSelectorsDocumentDamage};
use style;
use servo_util::namespace;
use servo_util::namespace::{Namespace, Null};
use std::ascii::StrAsciiExt;
use std::cast;
@ -152,13 +152,14 @@ impl Element {
}).map(|&x| x)
}
pub unsafe fn get_attr_val_for_layout(&self, namespace: Namespace, name: &str)
#[inline]
pub unsafe fn get_attr_val_for_layout(&self, namespace: &Namespace, name: &str)
-> Option<&'static str> {
self.attrs.iter().find(|attr: & &@mut Attr| {
// unsafely avoid a borrow because this is accessed by many tasks
// during parallel layout
let attr: ***Box<Attr> = cast::transmute(attr);
name == (***attr).data.local_name && (***attr).data.namespace == namespace
name == (***attr).data.local_name && (***attr).data.namespace == *namespace
}).map(|attr| {
let attr: **Box<Attr> = cast::transmute(attr);
cast::transmute((**attr).data.value.as_slice())
@ -402,7 +403,7 @@ impl Element {
}
pub fn GetAttributeNS(&self, namespace: Option<DOMString>, local_name: DOMString) -> Option<DOMString> {
let namespace = Namespace::from_str(namespace);
let namespace = Namespace::from_str(null_str_as_empty_ref(&namespace));
self.get_attribute(namespace, local_name)
.map(|attr| attr.value.clone())
}
@ -430,7 +431,7 @@ impl Element {
QName => {}
}
let namespace = Namespace::from_str(namespace_url);
let namespace = Namespace::from_str(null_str_as_empty_ref(&namespace_url));
self.set_attribute(abstract_self, namespace, name, value)
}
@ -449,7 +450,7 @@ impl Element {
abstract_self: AbstractNode,
namespace: Option<DOMString>,
localname: DOMString) -> ErrorResult {
let namespace = Namespace::from_str(namespace);
let namespace = Namespace::from_str(null_str_as_empty_ref(&namespace));
self.remove_attribute(abstract_self, namespace, localname)
}

View file

@ -6,8 +6,8 @@ use dom::bindings::codegen::HTMLDocumentBinding;
use dom::bindings::utils::{Reflectable, Reflector, Traceable};
use dom::document::{AbstractDocument, Document, HTML};
use dom::htmlcollection::HTMLCollection;
use dom::namespace::Null;
use dom::window::Window;
use servo_util::namespace::Null;
use js::jsapi::JSTracer;
use std::str::eq_slice;

View file

@ -9,7 +9,7 @@ use dom::element::{Element, ElementTypeId, HTMLElementTypeId};
use dom::node::{AbstractNode, Node};
use js::jsapi::{JSContext, JSVal};
use js::JSVAL_NULL;
use dom::namespace;
use servo_util::namespace;
pub struct HTMLElement {
element: Element

View file

@ -7,7 +7,6 @@ use dom::bindings::utils::{DOMString, ErrorResult};
use dom::document::AbstractDocument;
use dom::element::HTMLImageElementTypeId;
use dom::htmlelement::HTMLElement;
use dom::namespace::Null;
use dom::node::{AbstractNode, Node};
use extra::url::Url;
use servo_util::geometry::to_px;
@ -15,6 +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::namespace::Null;
pub struct HTMLImageElement {
htmlelement: HTMLElement,

View file

@ -2,7 +2,7 @@
* 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::namespace;
use servo_util::namespace;
use dom::attr::Attr;
use dom::node::NodeIterator;
use dom::node::{DoctypeNodeTypeId, DocumentFragmentNodeTypeId, CommentNodeTypeId, DocumentNodeTypeId, ElementNodeTypeId, TextNodeTypeId, AbstractNode};

View file

@ -8,7 +8,6 @@ use dom::htmlelement::HTMLElement;
use dom::htmlheadingelement::{Heading1, Heading2, Heading3, Heading4, Heading5, Heading6};
use dom::htmliframeelement::IFrameSize;
use dom::htmlformelement::HTMLFormElement;
use dom::namespace::Null;
use dom::node::{AbstractNode, ElementNodeTypeId};
use dom::types::*;
use html::cssparse::{InlineProvenance, StylesheetProvenance, UrlProvenance, spawn_css_parser};
@ -22,6 +21,7 @@ use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::{Load, Payload, Done, ResourceTask, load_whole_resource};
use servo_util::url::make_url;
use servo_util::task::spawn_named;
use servo_util::namespace::Null;
use std::cast;
use std::cell::RefCell;
use std::comm::{Port, SharedChan};

View file

@ -136,7 +136,6 @@ pub mod dom {
pub mod htmlunknownelement;
pub mod location;
pub mod mouseevent;
pub mod namespace;
pub mod navigator;
pub mod node;
pub mod nodelist;

View file

@ -13,7 +13,6 @@ use dom::event::{Event_, ResizeEvent, ReflowEvent, ClickEvent, MouseDownEvent, M
use dom::event::Event;
use dom::eventtarget::AbstractEventTarget;
use dom::htmldocument::HTMLDocument;
use dom::namespace::Null;
use dom::node::AbstractNode;
use dom::window::{TimerData, TimerHandle, Window};
use html::hubbub_html_parser::HtmlParserResult;
@ -47,6 +46,7 @@ use servo_net::resource_task::ResourceTask;
use servo_util::geometry::to_frac_px;
use servo_util::url::make_url;
use servo_util::task::spawn_named;
use servo_util::namespace::Null;
use std::comm::{Port, SharedChan};
use std::ptr;
use std::str::eq_slice;

View file

@ -4,11 +4,12 @@
use std::hashmap::HashMap;
use cssparser::ast::*;
use servo_util::namespace::Namespace;
use errors::log_css_error;
pub struct NamespaceMap {
default: Option<~str>, // Optional URL
prefix_map: HashMap<~str, ~str>, // prefix -> URL
default: Option<Namespace>,
prefix_map: HashMap<~str, Namespace>,
}
@ -29,7 +30,7 @@ pub fn parse_namespace_rule(rule: AtRule, namespaces: &mut NamespaceMap) {
);
if rule.block.is_some() { syntax_error!() }
let mut prefix: Option<~str> = None;
let mut url: Option<~str> = None;
let mut ns: Option<Namespace> = None;
let mut iter = rule.prelude.move_skip_whitespace();
for component_value in iter {
match component_value {
@ -38,25 +39,25 @@ pub fn parse_namespace_rule(rule: AtRule, namespaces: &mut NamespaceMap) {
prefix = Some(value);
},
URL(value) | String(value) => {
if url.is_some() { syntax_error!() }
url = Some(value);
if ns.is_some() { syntax_error!() }
ns = Some(Namespace::from_str(value));
break
},
_ => syntax_error!(),
}
}
if iter.next().is_some() { syntax_error!() }
match (prefix, url) {
(Some(prefix), Some(url)) => {
if namespaces.prefix_map.swap(prefix, url).is_some() {
match (prefix, ns) {
(Some(prefix), Some(ns)) => {
if namespaces.prefix_map.swap(prefix, ns).is_some() {
log_css_error(location, "Duplicate @namespace rule");
}
},
(None, Some(url)) => {
(None, Some(ns)) => {
if namespaces.default.is_some() {
log_css_error(location, "Duplicate @namespace rule");
}
namespaces.default = Some(url);
namespaces.default = Some(ns);
},
_ => syntax_error!()
}

View file

@ -6,6 +6,7 @@
//! style.
use selectors::AttrSelector;
use servo_util::namespace::Namespace;
pub trait TNode<E:TElement> : Clone {
@ -23,9 +24,9 @@ pub trait TNode<E:TElement> : Clone {
}
pub trait TElement {
fn get_attr(&self, namespace: Option<~str>, attr: &str) -> Option<&'static str>;
fn get_link(&self) -> Option<~str>;
fn get_attr(&self, namespace: &Namespace, attr: &str) -> Option<&'static str>;
fn get_link(&self) -> Option<&'static str>;
fn get_local_name<'a>(&'a self) -> &'a str;
fn get_namespace_url<'a>(&'a self) -> &'a str;
fn get_namespace<'a>(&'a self) -> &'a Namespace;
}

View file

@ -7,6 +7,8 @@ use std::ascii::StrAsciiExt;
use std::hashmap::HashMap;
use std::str;
use servo_util::namespace;
use media_queries::{Device, Screen};
use node::{TElement, TNode};
use properties::{PropertyDeclaration, PropertyDeclarationBlock};
@ -73,7 +75,7 @@ impl SelectorMap {
// 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_element(|element: &E| {
match element.get_attr(None, "id") {
match element.get_attr(&namespace::Null, "id") {
Some(id) => {
SelectorMap::get_matching_rules_from_hash(node,
&self.id_hash,
@ -83,7 +85,7 @@ impl SelectorMap {
None => {}
}
match element.get_attr(None, "class") {
match element.get_attr(&namespace::Null, "class") {
Some(ref class_attr) => {
for class in class_attr.split(SELECTOR_WHITESPACE) {
SelectorMap::get_matching_rules_from_hash(
@ -486,16 +488,16 @@ fn matches_simple_selector<E:TElement,N:TNode<E>>(selector: &SimpleSelector, ele
element.get_local_name().eq_ignore_ascii_case(name.as_slice())
})
}
NamespaceSelector(ref url) => {
NamespaceSelector(ref namespace) => {
element.with_element(|element: &E| {
element.get_namespace_url() == url.as_slice()
element.get_namespace() == namespace
})
}
// TODO: case-sensitivity depends on the document type and quirks mode
// TODO: cache and intern IDs on elements.
IDSelector(ref id) => {
element.with_element(|element: &E| {
match element.get_attr(None, "id") {
match element.get_attr(&namespace::Null, "id") {
Some(attr) => str::eq_slice(attr, *id),
None => false
}
@ -504,7 +506,7 @@ fn matches_simple_selector<E:TElement,N:TNode<E>>(selector: &SimpleSelector, ele
// TODO: cache and intern classe names on elements.
ClassSelector(ref class) => {
element.with_element(|element: &E| {
match element.get_attr(None, "class") {
match element.get_attr(&namespace::Null, "class") {
None => false,
// TODO: case-sensitivity depends on the document type and quirks mode
Some(ref class_attr)
@ -624,7 +626,7 @@ fn matches_generic_nth_child<'a,
element.with_element(|element: &E| {
node.with_element(|node: &E| {
if element.get_local_name() == node.get_local_name() &&
element.get_namespace_url() == node.get_namespace_url() {
element.get_namespace() == node.get_namespace() {
index += 1;
}
})

View file

@ -9,6 +9,9 @@ use extra::arc::Arc;
use cssparser::ast::*;
use cssparser::parse_nth;
use servo_util::namespace::Namespace;
use servo_util::namespace;
use namespaces::NamespaceMap;
@ -55,7 +58,7 @@ pub enum SimpleSelector {
IDSelector(~str),
ClassSelector(~str),
LocalNameSelector(~str),
NamespaceSelector(~str),
NamespaceSelector(Namespace),
// Attribute selectors
AttrExists(AttrSelector), // [foo]
@ -89,7 +92,8 @@ pub enum SimpleSelector {
pub struct AttrSelector {
name: ~str,
lower_name: ~str,
namespace: Option<~str>,
/// None means "any namespace", `*|attr`
namespace: Option<Namespace>,
}
@ -270,7 +274,7 @@ fn parse_type_selector(iter: &mut Iter, namespaces: &NamespaceMap)
QualifiedName(namespace, local_name) => {
let mut simple_selectors = ~[];
match namespace {
Some(url) => simple_selectors.push(NamespaceSelector(url)),
Some(ns) => simple_selectors.push(NamespaceSelector(ns)),
None => (),
}
match local_name {
@ -357,7 +361,8 @@ fn parse_one_simple_selector(iter: &mut Iter, namespaces: &NamespaceMap, inside_
enum QualifiedNameParseResult {
InvalidQualifiedName,
NotAQualifiedName,
QualifiedName(Option<~str>, Option<~str>) // Namespace URL, local name. None means '*'
// Namespace URL, local name. None means '*'
QualifiedName(Option<Namespace>, Option<~str>)
}
fn parse_qualified_name(iter: &mut Iter, allow_universal: bool, namespaces: &NamespaceMap)
@ -365,22 +370,22 @@ fn parse_qualified_name(iter: &mut Iter, allow_universal: bool, namespaces: &Nam
#[inline]
fn default_namespace(namespaces: &NamespaceMap, local_name: Option<~str>)
-> QualifiedNameParseResult {
QualifiedName(namespaces.default.as_ref().map(|url| url.to_owned()), local_name)
QualifiedName(namespaces.default.as_ref().map(|ns| ns.clone()), local_name)
}
#[inline]
fn explicit_namespace(iter: &mut Iter, allow_universal: bool, namespace_url: Option<~str>)
fn explicit_namespace(iter: &mut Iter, allow_universal: bool, namespace: Option<Namespace>)
-> QualifiedNameParseResult {
assert!(iter.next() == Some(Delim('|')),
"Implementation error, this should not happen.");
match iter.peek() {
Some(&Delim('*')) if allow_universal => {
iter.next();
QualifiedName(namespace_url, None)
QualifiedName(namespace, None)
},
Some(&Ident(_)) => {
let local_name = get_next_ident(iter);
QualifiedName(namespace_url, Some(local_name))
QualifiedName(namespace, Some(local_name))
},
_ => InvalidQualifiedName,
}
@ -391,11 +396,11 @@ fn parse_qualified_name(iter: &mut Iter, allow_universal: bool, namespaces: &Nam
let value = get_next_ident(iter);
match iter.peek() {
Some(&Delim('|')) => {
let namespace_url = match namespaces.prefix_map.find(&value) {
let namespace = match namespaces.prefix_map.find(&value) {
None => return InvalidQualifiedName, // Undeclared namespace prefix
Some(ref url) => url.to_owned(),
Some(ref ns) => (*ns).clone(),
};
explicit_namespace(iter, allow_universal, Some(namespace_url))
explicit_namespace(iter, allow_universal, Some(namespace))
},
_ => default_namespace(namespaces, Some(value)),
}
@ -410,7 +415,7 @@ fn parse_qualified_name(iter: &mut Iter, allow_universal: bool, namespaces: &Nam
},
}
},
Some(&Delim('|')) => explicit_namespace(iter, allow_universal, Some(~"")),
Some(&Delim('|')) => explicit_namespace(iter, allow_universal, Some(namespace::Null)),
_ => NotAQualifiedName,
}
}

View file

@ -2,8 +2,6 @@
* 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::bindings::utils::{DOMString, null_str_as_empty_ref};
#[deriving(Eq, Clone)]
pub enum Namespace {
Null,
@ -17,8 +15,9 @@ pub enum Namespace {
}
impl Namespace {
pub fn from_str(url: Option<DOMString>) -> Namespace {
match null_str_as_empty_ref(&url) {
/// Empty string for "no namespace"
pub fn from_str(url: &str) -> Namespace {
match url {
"http://www.w3.org/1999/xhtml" => HTML,
"http://www.w3.org/XML/1998/namespace" => XML,
"http://www.w3.org/2000/xmlns/" => XMLNS,
@ -29,16 +28,16 @@ impl Namespace {
ns => Other(ns.to_owned())
}
}
pub fn to_str<'a>(&'a self) -> Option<&'a str> {
pub fn to_str<'a>(&'a self) -> &'a str {
match *self {
Null => None,
HTML => Some("http://www.w3.org/1999/xhtml"),
XML => Some("http://www.w3.org/XML/1998/namespace"),
XMLNS => Some("http://www.w3.org/2000/xmlns/"),
XLink => Some("http://www.w3.org/1999/xlink"),
SVG => Some("http://www.w3.org/2000/svg"),
MathML => Some("http://www.w3.org/1998/Math/MathML"),
Other(ref x) => Some(x.as_slice())
Null => "",
HTML => "http://www.w3.org/1999/xhtml",
XML => "http://www.w3.org/XML/1998/namespace",
XMLNS => "http://www.w3.org/2000/xmlns/",
XLink => "http://www.w3.org/1999/xlink",
SVG => "http://www.w3.org/2000/svg",
MathML => "http://www.w3.org/1998/Math/MathML",
Other(ref x) => x.as_slice()
}
}
}

View file

@ -21,4 +21,4 @@ pub mod debug;
pub mod io;
pub mod task;
pub mod workqueue;
pub mod namespace;