diff --git a/components/style/build_gecko.rs b/components/style/build_gecko.rs index fe52deaac87..6f262728954 100644 --- a/components/style/build_gecko.rs +++ b/components/style/build_gecko.rs @@ -574,6 +574,8 @@ mod bindings { "nsStyleColor", "nsStyleColumn", "nsStyleContent", + "nsStyleContentData", + "nsStyleContentType", "nsStyleContext", "nsStyleCoord", "nsStyleCoord_Calc", diff --git a/components/style/gecko_bindings/bindings.rs b/components/style/gecko_bindings/bindings.rs index cced8859505..897f5236708 100644 --- a/components/style/gecko_bindings/bindings.rs +++ b/components/style/gecko_bindings/bindings.rs @@ -56,6 +56,12 @@ unsafe impl Sync for nsStyleColumn {} use gecko_bindings::structs::nsStyleContent; unsafe impl Send for nsStyleContent {} unsafe impl Sync for nsStyleContent {} +use gecko_bindings::structs::nsStyleContentData; +unsafe impl Send for nsStyleContentData {} +unsafe impl Sync for nsStyleContentData {} +use gecko_bindings::structs::nsStyleContentType; +unsafe impl Send for nsStyleContentType {} +unsafe impl Sync for nsStyleContentType {} use gecko_bindings::structs::nsStyleContext; unsafe impl Send for nsStyleContext {} unsafe impl Sync for nsStyleContext {} @@ -665,6 +671,14 @@ extern "C" { pub fn Gecko_CopyCursorArrayFrom(dest: *mut nsStyleUserInterface, src: *const nsStyleUserInterface); } +extern "C" { + pub fn Gecko_SetContentDataImage(content_data: *mut nsStyleContentData, + uri: ServoBundledURI); +} +extern "C" { + pub fn Gecko_SetContentDataArray(content_data: *mut nsStyleContentData, + type_: nsStyleContentType, len: u32); +} extern "C" { pub fn Gecko_GetNodeFlags(node: RawGeckoNodeBorrowed) -> u32; } @@ -892,7 +906,11 @@ extern "C" { } extern "C" { pub fn Gecko_CSSValue_SetString(css_value: nsCSSValueBorrowedMut, - string: nsString); + string: *const u8, len: u32); +} +extern "C" { + pub fn Gecko_CSSValue_SetIdent(css_value: nsCSSValueBorrowedMut, + string: *const u8, len: u32); } extern "C" { pub fn Gecko_CSSValue_SetArray(css_value: nsCSSValueBorrowedMut, diff --git a/components/style/gecko_bindings/sugar/ns_css_value.rs b/components/style/gecko_bindings/sugar/ns_css_value.rs index 0eb9dc5c90c..19c32348646 100644 --- a/components/style/gecko_bindings/sugar/ns_css_value.rs +++ b/components/style/gecko_bindings/sugar/ns_css_value.rs @@ -5,18 +5,13 @@ //! Little helpers for `nsCSSValue`. use app_units::Au; -use gecko_bindings::bindings::Gecko_CSSValue_Drop; -use gecko_bindings::bindings::Gecko_CSSValue_GetAbsoluteLength; -use gecko_bindings::bindings::Gecko_CSSValue_GetCalc; -use gecko_bindings::bindings::Gecko_CSSValue_GetPercentage; -use gecko_bindings::bindings::Gecko_CSSValue_SetAbsoluteLength; -use gecko_bindings::bindings::Gecko_CSSValue_SetCalc; -use gecko_bindings::bindings::Gecko_CSSValue_SetPercentage; +use gecko_bindings::bindings; use gecko_bindings::structs::{nsCSSValue, nsCSSUnit, nsCSSValue_Array, nscolor}; use std::mem; -use std::ops::Index; +use std::ops::{Index, IndexMut}; use std::slice; use values::computed::LengthOrPercentage; +use values::specified::url::SpecifiedUrl; impl nsCSSValue { /// Create a CSSValue with null unit, useful to be used as a return value. @@ -77,13 +72,13 @@ impl nsCSSValue { pub unsafe fn set_lop(&mut self, lop: LengthOrPercentage) { match lop { LengthOrPercentage::Length(au) => { - Gecko_CSSValue_SetAbsoluteLength(self, au.0) + bindings::Gecko_CSSValue_SetAbsoluteLength(self, au.0) } LengthOrPercentage::Percentage(pc) => { - Gecko_CSSValue_SetPercentage(self, pc) + bindings::Gecko_CSSValue_SetPercentage(self, pc) } LengthOrPercentage::Calc(calc) => { - Gecko_CSSValue_SetCalc(self, calc.into()) + bindings::Gecko_CSSValue_SetCalc(self, calc.into()) } } } @@ -92,27 +87,47 @@ impl nsCSSValue { pub unsafe fn get_lop(&self) -> LengthOrPercentage { match self.mUnit { nsCSSUnit::eCSSUnit_Pixel => { - LengthOrPercentage::Length(Au(Gecko_CSSValue_GetAbsoluteLength(self))) + LengthOrPercentage::Length(Au(bindings::Gecko_CSSValue_GetAbsoluteLength(self))) }, nsCSSUnit::eCSSUnit_Percent => { - LengthOrPercentage::Percentage(Gecko_CSSValue_GetPercentage(self)) + LengthOrPercentage::Percentage(bindings::Gecko_CSSValue_GetPercentage(self)) }, nsCSSUnit::eCSSUnit_Calc => { - LengthOrPercentage::Calc(Gecko_CSSValue_GetCalc(self).into()) + LengthOrPercentage::Calc(bindings::Gecko_CSSValue_GetCalc(self).into()) }, x => panic!("The unit should not be {:?}", x), } } + + /// Set to a string value + pub fn set_string(&mut self, s: &str) { + unsafe { bindings::Gecko_CSSValue_SetString(self, s.as_ptr(), s.len() as u32) } + } + + /// Set to an identifier value + pub fn set_ident(&mut self, s: &str) { + unsafe { bindings::Gecko_CSSValue_SetIdent(self, s.as_ptr(), s.len() as u32) } + } + + /// Set to a url value + pub fn set_url(&mut self, url: &SpecifiedUrl) { + unsafe { bindings::Gecko_CSSValue_SetURL(self, url.for_ffi()) } + } + + /// Set to an array of given length + pub fn set_array(&mut self, len: i32) { + unsafe { bindings::Gecko_CSSValue_SetArray(self, len) } + } } impl Drop for nsCSSValue { fn drop(&mut self) { - unsafe { Gecko_CSSValue_Drop(self) }; + unsafe { bindings::Gecko_CSSValue_Drop(self) }; } } impl nsCSSValue_Array { - /// Return the length of this `nsCSSShadowArray` + /// Return the length of this `nsCSSValue::Array` #[inline] pub fn len(&self) -> usize { self.mCount @@ -128,6 +143,12 @@ impl nsCSSValue_Array { pub fn as_slice(&self) -> &[nsCSSValue] { unsafe { slice::from_raw_parts(self.buffer(), self.len()) } } + + /// Get the array as a mutable slice of nsCSSValues. + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [nsCSSValue] { + unsafe { slice::from_raw_parts_mut(self.buffer() as *mut _, self.len()) } + } } impl Index for nsCSSValue_Array { @@ -137,3 +158,11 @@ impl Index for nsCSSValue_Array { &self.as_slice()[i] } } + +impl IndexMut for nsCSSValue_Array { + #[inline] + fn index_mut(&mut self, i: usize) -> &mut nsCSSValue { + &mut self.as_mut_slice()[i] + } +} + diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index ddda61a9dc1..65af844c8c0 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -3190,6 +3190,7 @@ clip-path pub fn set_content(&mut self, v: longhands::content::computed_value::T) { use properties::longhands::content::computed_value::T; use properties::longhands::content::computed_value::ContentItem; + use style_traits::ToCss; use gecko_bindings::structs::nsStyleContentType::*; use gecko_bindings::bindings::Gecko_ClearAndResizeStyleContents; @@ -3219,7 +3220,6 @@ clip-path items.len() as u32); } for (i, item) in items.into_iter().enumerate() { - // TODO: Servo lacks support for attr(), and URIs. // NB: Gecko compares the mString value if type is not image // or URI independently of whatever gets there. In the quote // cases, they set it to null, so do the same here. @@ -3235,6 +3235,19 @@ clip-path as_utf16_and_forget(&value); } } + ContentItem::Attr(ns, val) => { + self.gecko.mContents[i].mType = eStyleContentType_Attr; + let s = if let Some(ns) = ns { + format!("{}|{}", ns, val) + } else { + val + }; + unsafe { + // NB: we share allocators, so doing this is fine. + *self.gecko.mContents[i].mContent.mString.as_mut() = + as_utf16_and_forget(&s); + } + } ContentItem::OpenQuote => self.gecko.mContents[i].mType = eStyleContentType_OpenQuote, ContentItem::CloseQuote @@ -3245,9 +3258,30 @@ clip-path => self.gecko.mContents[i].mType = eStyleContentType_NoCloseQuote, ContentItem::MozAltContent => self.gecko.mContents[i].mType = eStyleContentType_AltContent, - ContentItem::Counter(..) | - ContentItem::Counters(..) - => self.gecko.mContents[i].mType = eStyleContentType_Uninitialized, + ContentItem::Counter(name, style) => { + unsafe { + bindings::Gecko_SetContentDataArray(&mut self.gecko.mContents[i], + eStyleContentType_Counter, 2) + } + let mut array = unsafe { &mut **self.gecko.mContents[i].mContent.mCounters.as_mut() }; + array[0].set_string(&name); + // When we support values for list-style-type this will need to be updated + array[1].set_ident(&style.to_css_string()); + } + ContentItem::Counters(name, sep, style) => { + unsafe { + bindings::Gecko_SetContentDataArray(&mut self.gecko.mContents[i], + eStyleContentType_Counters, 3) + } + let mut array = unsafe { &mut **self.gecko.mContents[i].mContent.mCounters.as_mut() }; + array[0].set_string(&name); + array[1].set_string(&sep); + // When we support values for list-style-type this will need to be updated + array[2].set_ident(&style.to_css_string()); + } + ContentItem::Url(url) => { + unsafe { bindings::Gecko_SetContentDataImage(&mut self.gecko.mContents[i], url.for_ffi()) } + } } } } diff --git a/components/style/properties/longhand/counters.mako.rs b/components/style/properties/longhand/counters.mako.rs index bab3f22e4f8..ec75f431bcc 100644 --- a/components/style/properties/longhand/counters.mako.rs +++ b/components/style/properties/longhand/counters.mako.rs @@ -10,6 +10,7 @@ use cssparser::Token; use std::ascii::AsciiExt; use values::computed::ComputedValueAsSpecified; + use values::specified::url::SpecifiedUrl; use values::HasViewportPercentage; use super::list_style_type; @@ -26,6 +27,7 @@ use cssparser; use std::fmt; use style_traits::ToCss; + use values::specified::url::SpecifiedUrl; #[derive(Debug, PartialEq, Eq, Clone)] #[cfg_attr(feature = "servo", derive(HeapSizeOf))] @@ -48,6 +50,10 @@ % if product == "gecko": /// `-moz-alt-content` MozAltContent, + /// `attr([namespace? `|`]? ident)` + Attr(Option, String), + /// `url(url)` + Url(SpecifiedUrl), % endif } @@ -80,6 +86,16 @@ % if product == "gecko": ContentItem::MozAltContent => dest.write_str("-moz-alt-content"), + ContentItem::Attr(ref ns, ref attr) => { + dest.write_str("attr(")?; + if let Some(ref ns) = *ns { + cssparser::Token::Ident((&**ns).into()).to_css(dest)?; + dest.write_str("|")?; + } + cssparser::Token::Ident((&**attr).into()).to_css(dest)?; + dest.write_str(")") + } + ContentItem::Url(ref url) => url.to_css(dest), % endif } } @@ -135,6 +151,12 @@ } let mut content = vec![]; loop { + % if product == "gecko": + if let Ok(url) = input.try(|i| SpecifiedUrl::parse(context, i)) { + content.push(ContentItem::Url(url)); + continue; + } + % endif match input.next() { Ok(Token::QuotedString(value)) => { content.push(ContentItem::String(value.into_owned())) @@ -159,6 +181,35 @@ }).unwrap_or(list_style_type::computed_value::T::decimal); Ok(ContentItem::Counters(name, separator, style)) }), + % if product == "gecko": + "attr" => input.parse_nested_block(|input| { + // Syntax is `[namespace? `|`]? ident` + // no spaces allowed + // 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()); + 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())) + } else { + return Err(()) + } + } + _ => return Err(()) + } + } + if let Some(first) = first { + Ok(ContentItem::Attr(None, first)) + } else { + Err(()) + } + }), + % endif _ => return Err(()) })); }