diff --git a/components/style/gecko/generated/bindings.rs b/components/style/gecko/generated/bindings.rs index a9c5fe2bd4b..ca5309e0c2e 100644 --- a/components/style/gecko/generated/bindings.rs +++ b/components/style/gecko/generated/bindings.rs @@ -1399,6 +1399,9 @@ extern "C" { pub fn Gecko_AddPropertyToSet(arg1: nsCSSPropertyIDSetBorrowedMut, arg2: nsCSSPropertyID); } +extern "C" { + pub fn Gecko_RegisterNamespace(ns: *mut nsIAtom) -> i32; +} extern "C" { pub fn Gecko_Construct_Default_nsStyleFont(ptr: *mut nsStyleFont, pres_context: diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index 30242dc9bcb..ff800322c90 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -273,11 +273,11 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> { } fn default_namespace(&self) -> Option { - self.namespaces.default.clone() + self.namespaces.default.clone().as_ref().map(|&(ref ns, _)| ns.clone()) } fn namespace_for_prefix(&self, prefix: &Atom) -> Option { - self.namespaces.prefixes.get(prefix).cloned() + self.namespaces.prefixes.get(prefix).map(|&(ref ns, _)| ns.clone()) } } diff --git a/components/style/parser.rs b/components/style/parser.rs index 7085dacb36f..cad388651f9 100644 --- a/components/style/parser.rs +++ b/components/style/parser.rs @@ -7,9 +7,9 @@ use context::QuirksMode; use cssparser::{Parser, SourcePosition, UnicodeRange}; use error_reporting::ParseErrorReporter; +use parking_lot::RwLock; use style_traits::OneOrMoreCommaSeparated; use stylesheets::{CssRuleType, Origin, UrlExtraData, Namespaces}; -use parking_lot::RwLock; bitflags! { /// The mode to use when parsing values. diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 55d7cbe2dd9..4871d1d5a72 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -4403,10 +4403,10 @@ clip-path } ContentItem::Attr(ns, val) => { self.gecko.mContents[i].mType = eStyleContentType_Attr; - let s = if let Some(ns) = ns { + let s = if let Some((_, ns)) = ns { format!("{}|{}", ns, val) } else { - val + val.into() }; unsafe { // NB: we share allocators, so doing this is fine. diff --git a/components/style/properties/longhand/counters.mako.rs b/components/style/properties/longhand/counters.mako.rs index 9f86c3acf03..fec8ff10822 100644 --- a/components/style/properties/longhand/counters.mako.rs +++ b/components/style/properties/longhand/counters.mako.rs @@ -14,6 +14,8 @@ use values::generics::CounterStyleOrNone; #[cfg(feature = "gecko")] use values::specified::url::SpecifiedUrl; + #[cfg(feature = "gecko")] + use gecko_string_cache::namespace::Namespace; #[cfg(feature = "servo")] use super::list_style_type; @@ -35,6 +37,8 @@ type CounterStyleType = super::super::list_style_type::computed_value::T; #[cfg(feature = "gecko")] type CounterStyleType = ::values::generics::CounterStyleOrNone; + #[cfg(feature = "gecko")] + use gecko_string_cache::namespace::Namespace; #[derive(Debug, PartialEq, Eq, Clone)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] @@ -56,7 +60,7 @@ % if product == "gecko": /// `attr([namespace? `|`]? ident)` - Attr(Option, String), + Attr(Option<(Namespace, u32)>, String), /// `url(url)` Url(SpecifiedUrl), % endif @@ -93,7 +97,7 @@ ContentItem::Attr(ref ns, ref attr) => { dest.write_str("attr(")?; if let Some(ref ns) = *ns { - cssparser::Token::Ident((&**ns).into()).to_css(dest)?; + cssparser::Token::Ident(ns.0.to_string().into()).to_css(dest)?; dest.write_str("|")?; } cssparser::Token::Ident((&**attr).into()).to_css(dest)?; @@ -208,14 +212,29 @@ // FIXME (bug 1346693) we should be checking that // this is a valid namespace and encoding it as a namespace // number from the map - let first = input.try(|i| i.expect_ident()).ok().map(|i| i.into_owned()); + let first = input.try(|i| i.expect_ident()).ok(); if let Ok(token) = input.try(|i| i.next_including_whitespace()) { match token { Token::Delim('|') => { // must be followed by an ident let tok2 = input.next_including_whitespace()?; if let Token::Ident(second) = tok2 { - return Ok(ContentItem::Attr(first, second.into_owned())) + let first: Option = first.map(|i| i.into()); + let first_with_id = match (first, context.namespaces) { + (Some(prefix), Some(map)) => { + let map = map.read(); + if let Some(ref entry) = map.prefixes.get(&prefix.0) { + Some((prefix, entry.1)) + } else { + return Err(()) + } + } + // if we don't have a namespace map (e.g. in CSSOM) + // we can't parse namespaces + (Some(_), None) => return Err(()), + _ => None + }; + return Ok(ContentItem::Attr(first_with_id, second.into_owned())) } else { return Err(()) } @@ -224,7 +243,7 @@ } } if let Some(first) = first { - Ok(ContentItem::Attr(None, first)) + Ok(ContentItem::Attr(None, first.into_owned())) } else { Err(()) } diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs index 34cec12b83f..c6f56a09b12 100644 --- a/components/style/servo/selector_parser.rs +++ b/components/style/servo/selector_parser.rs @@ -430,11 +430,11 @@ impl<'a> ::selectors::Parser for SelectorParser<'a> { } fn default_namespace(&self) -> Option { - self.namespaces.default.clone() + self.namespaces.default.as_ref().map(|&(ref ns, _)| ns.clone()) } fn namespace_for_prefix(&self, prefix: &Prefix) -> Option { - self.namespaces.prefixes.get(prefix).cloned() + self.namespaces.prefixes.get(prefix).map(|&(ref ns, _)| ns.clone()) } } diff --git a/components/style/stylesheets.rs b/components/style/stylesheets.rs index cb21c63d933..de49dee5754 100644 --- a/components/style/stylesheets.rs +++ b/components/style/stylesheets.rs @@ -96,11 +96,13 @@ pub enum Origin { } /// A set of namespaces applying to a given stylesheet. +/// +/// The u32 is the namespace id, used in gecko #[derive(Clone, Default, Debug)] #[allow(missing_docs)] pub struct Namespaces { - pub default: Option, - pub prefixes: FnvHashMap, + pub default: Option<(Namespace, u32)>, + pub prefixes: FnvHashMap, } /// Like gecko_bindings::structs::MallocSizeOf, but without the Option<> wrapper. Note that @@ -1526,6 +1528,21 @@ enum AtRulePrelude { } +#[cfg(feature = "gecko")] +fn register_namespace(ns: &Namespace) -> Result { + let id = unsafe { ::gecko_bindings::bindings::Gecko_RegisterNamespace(ns.0.as_ptr()) }; + if id == -1 { + Err(()) + } else { + Ok(id as u32) + } +} + +#[cfg(feature = "servo")] +fn register_namespace(ns: &Namespace) -> Result { + Ok(1) // servo doesn't use namespace ids +} + impl<'a> AtRuleParser for TopLevelRuleParser<'a> { type Prelude = AtRulePrelude; type AtRule = CssRule; @@ -1584,14 +1601,16 @@ impl<'a> AtRuleParser for TopLevelRuleParser<'a> { let prefix_result = input.try(|input| input.expect_ident()); let url = Namespace::from(try!(input.expect_url_or_string())); + let id = register_namespace(&url)?; + let opt_prefix = if let Ok(prefix) = prefix_result { let prefix = Prefix::from(prefix); self.context.namespaces.expect("namespaces must be set whilst parsing rules") - .write().prefixes.insert(prefix.clone(), url.clone()); + .write().prefixes.insert(prefix.clone(), (url.clone(), id)); Some(prefix) } else { self.context.namespaces.expect("namespaces must be set whilst parsing rules") - .write().default = Some(url.clone()); + .write().default = Some((url.clone(), id)); None };