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/conversions.rs b/components/style/gecko/conversions.rs index a88d7c66d95..6b42c1e04bf 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -106,26 +106,17 @@ impl nsStyleImage { self.set_gradient(gradient) }, Image::Url(ref url) if with_url => { - let (ptr, len) = match url.as_slice_components() { - Ok(value) | Err(value) => value - }; - let extra_data = url.extra_data(); unsafe { - Gecko_SetUrlImageValue(self, - ptr, - len as u32, - extra_data.base.get(), - extra_data.referrer.get(), - extra_data.principal.get()); + Gecko_SetUrlImageValue(self, url.for_ffi()); + // We unfortunately must make any url() value uncacheable, since + // the applicable declarations cache is not per document, but + // global, and the imgRequestProxy objects we store in the style + // structs don't like to be tracked by more than one document. + // + // FIXME(emilio): With the scoped TLS thing this is no longer + // true, remove this line in a follow-up! + *cacheable = false; } - // We unfortunately must make any url() value uncacheable, since - // the applicable declarations cache is not per document, but - // global, and the imgRequestProxy objects we store in the style - // structs don't like to be tracked by more than one document. - // - // FIXME(emilio): With the scoped TLS thing this is no longer - // true, remove this line in a follow-up! - *cacheable = false; }, _ => (), } diff --git a/components/style/gecko_bindings/bindings.rs b/components/style/gecko_bindings/bindings.rs index 0722a861ae6..832bd3b7ae4 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 {} @@ -631,10 +637,7 @@ extern "C" { } extern "C" { pub fn Gecko_SetUrlImageValue(image: *mut nsStyleImage, - url_bytes: *const u8, url_length: u32, - base_uri: *mut ThreadSafeURIHolder, - referrer: *mut ThreadSafeURIHolder, - principal: *mut ThreadSafePrincipalHolder); + uri: ServoBundledURI); } extern "C" { pub fn Gecko_CopyImageValueFrom(image: *mut nsStyleImage, @@ -662,15 +665,20 @@ extern "C" { } extern "C" { pub fn Gecko_SetCursorImage(cursor: *mut nsCursorImage, - string_bytes: *const u8, string_length: u32, - base_uri: *mut ThreadSafeURIHolder, - referrer: *mut ThreadSafeURIHolder, - principal: *mut ThreadSafePrincipalHolder); + uri: ServoBundledURI); } 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; } @@ -709,10 +717,26 @@ extern "C" { pub fn Gecko_ClearAndResizeStyleContents(content: *mut nsStyleContent, how_many: u32); } +extern "C" { + pub fn Gecko_ClearAndResizeCounterIncrements(content: *mut nsStyleContent, + how_many: u32); +} +extern "C" { + pub fn Gecko_ClearAndResizeCounterResets(content: *mut nsStyleContent, + how_many: u32); +} extern "C" { pub fn Gecko_CopyStyleContentsFrom(content: *mut nsStyleContent, other: *const nsStyleContent); } +extern "C" { + pub fn Gecko_CopyCounterResetsFrom(content: *mut nsStyleContent, + other: *const nsStyleContent); +} +extern "C" { + pub fn Gecko_CopyCounterIncrementsFrom(content: *mut nsStyleContent, + other: *const nsStyleContent); +} extern "C" { pub fn Gecko_EnsureImageLayersLength(layers: *mut nsStyleImageLayers, len: usize, @@ -898,7 +922,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 691f5cb5641..8d6ceeeb59c 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -394,11 +394,7 @@ fn color_to_nscolor_zero_currentcolor(color: Color) -> structs::nscolor { } SVGPaintKind::PaintServer(url) => { unsafe { - if let Some(ffi) = url.for_ffi() { - bindings::Gecko_nsStyleSVGPaint_SetURLValue(paint, ffi); - } else { - return; - } + bindings::Gecko_nsStyleSVGPaint_SetURLValue(paint, url.for_ffi()); } } SVGPaintKind::Color(color) => { @@ -511,20 +507,26 @@ fn color_to_nscolor_zero_currentcolor(color: Color) -> structs::nscolor { % endif -<%def name="impl_css_url(ident, gecko_ffi_name, need_clone=False)"> +<%def name="impl_css_url(ident, gecko_ffi_name, need_clone=False, only_resolved=False)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { use gecko_bindings::sugar::refptr::RefPtr; match v { Either::First(url) => { let refptr = unsafe { - if let Some(ffi) = url.for_ffi() { - let ptr = bindings::Gecko_NewURLValue(ffi); - RefPtr::from_addrefed(ptr) - } else { + % if only_resolved: + // -moz-binding can't handle relative URIs + if !url.has_resolved() { + self.gecko.${gecko_ffi_name}.clear(); + return; + } + % endif + let ptr = bindings::Gecko_NewURLValue(url.for_ffi()); + if ptr.is_null() { self.gecko.${gecko_ffi_name}.clear(); return; } + RefPtr::from_addrefed(ptr) }; self.gecko.${gecko_ffi_name}.set_move(refptr) } @@ -1423,7 +1425,7 @@ fn static_assert() { page-break-before page-break-after scroll-snap-points-x scroll-snap-points-y transform scroll-snap-type-y scroll-snap-coordinate - perspective-origin transform-origin""" %> + perspective-origin transform-origin -moz-binding""" %> <%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}"> // We manually-implement the |display| property until we get general @@ -1611,6 +1613,8 @@ fn static_assert() { longhands::scroll_snap_coordinate::computed_value::T(vec) } + ${impl_css_url('_moz_binding', 'mBinding', only_resolved=True)} + <%def name="transform_function_arm(name, keyword, items)"> <% pattern = None @@ -2281,12 +2285,8 @@ fn static_assert() { } Either::First(ref url) => { unsafe { - if let Some(ffi) = url.for_ffi() { - Gecko_SetListStyleImage(&mut self.gecko, - ffi); - } else { - Gecko_SetListStyleImageNone(&mut self.gecko); - } + Gecko_SetListStyleImage(&mut self.gecko, + url.for_ffi()); } // We don't need to record this struct as uncacheable, like when setting // background-image to a url() value, since only properties in reset structs @@ -2575,9 +2575,7 @@ fn static_assert() { } Url(ref url) => { unsafe { - if let Some(ffi) = url.for_ffi() { - bindings::Gecko_nsStyleFilter_SetURLValue(gecko_filter, ffi); - } + bindings::Gecko_nsStyleFilter_SetURLValue(gecko_filter, url.for_ffi()); } } } @@ -2940,9 +2938,7 @@ clip-path match v { ShapeSource::Url(ref url) => { unsafe { - if let Some(ffi) = url.for_ffi() { - bindings::Gecko_StyleClipPath_SetURLValue(clip_path, ffi); - } + bindings::Gecko_StyleClipPath_SetURLValue(clip_path, url.for_ffi()); } } ShapeSource::None => {} // don't change the type @@ -3150,16 +3146,8 @@ clip-path } for i in 0..v.images.len() { let image = &v.images[i]; - let extra_data = image.url.extra_data(); - let (ptr, len) = match image.url.as_slice_components() { - Ok(value) | Err(value) => value, - }; unsafe { - Gecko_SetCursorImage(&mut self.gecko.mCursorImages[i], - ptr, len as u32, - extra_data.base.get(), - extra_data.referrer.get(), - extra_data.principal.get()); + Gecko_SetCursorImage(&mut self.gecko.mCursorImages[i], image.url.for_ffi()); } // We don't need to record this struct as uncacheable, like when setting // background-image to a url() value, since only properties in reset structs @@ -3198,10 +3186,11 @@ clip-path <%self:impl_trait style_struct_name="Counters" - skip_longhands="content"> + skip_longhands="content counter-increment counter-reset"> 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; @@ -3231,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. @@ -3247,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 @@ -3257,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()) } + } } } } @@ -3272,6 +3294,25 @@ clip-path Gecko_CopyStyleContentsFrom(&mut self.gecko, &other.gecko) } } + + % for counter_property in ["Increment", "Reset"]: + pub fn set_counter_${counter_property.lower()}(&mut self, v: longhands::counter_increment::computed_value::T) { + unsafe { + bindings::Gecko_ClearAndResizeCounter${counter_property}s(&mut self.gecko, + v.0.len() as u32); + for (i, item) in v.0.into_iter().enumerate() { + self.gecko.m${counter_property}s[i].mCounter.assign_utf8(&item.0); + self.gecko.m${counter_property}s[i].mValue = item.1; + } + } + } + + pub fn copy_counter_${counter_property.lower()}_from(&mut self, other: &Self) { + unsafe { + bindings::Gecko_CopyCounter${counter_property}sFrom(&mut self.gecko, &other.gecko) + } + } + % endfor <%self:impl_trait style_struct_name="XUL" diff --git a/components/style/properties/longhand/counters.mako.rs b/components/style/properties/longhand/counters.mako.rs index bab3f22e4f8..8eec5fbb8b2 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(()) })); } @@ -188,7 +239,7 @@ } -<%helpers:longhand name="counter-increment" products="servo" animatable="False" +<%helpers:longhand name="counter-increment" animatable="False" spec="https://drafts.csswg.org/css-lists/#propdef-counter-increment"> use std::fmt; use style_traits::ToCss; @@ -262,7 +313,7 @@ } -<%helpers:longhand name="counter-reset" products="servo" animatable="False" +<%helpers:longhand name="counter-reset" animatable="False" spec="https://drafts.csswg.org/css-lists-3/#propdef-counter-reset"> pub use super::counter_increment::{SpecifiedValue, computed_value, get_initial_value}; use super::counter_increment::{parse_common}; diff --git a/components/style/values/specified/url.rs b/components/style/values/specified/url.rs index f6b8dd2de29..450330dc631 100644 --- a/components/style/values/specified/url.rs +++ b/components/style/values/specified/url.rs @@ -150,6 +150,11 @@ impl SpecifiedUrl { } } + /// Check if it has a resolved URI + pub fn has_resolved(&self) -> bool { + self.resolved.is_some() + } + /// Creates an already specified url value from an already resolved URL /// for insertion in the cascade. pub fn for_cascade(url: ServoUrl, extra_data: UrlExtraData) -> Self { @@ -173,19 +178,20 @@ impl SpecifiedUrl { /// Create a bundled URI suitable for sending to Gecko /// to be constructed into a css::URLValue #[cfg(feature = "gecko")] - pub fn for_ffi(&self) -> Option { + pub fn for_ffi(&self) -> ServoBundledURI { let extra_data = self.extra_data(); let (ptr, len) = match self.as_slice_components() { Ok(value) => value, - Err(_) => return None, + // we're okay with passing down the unresolved relative URI + Err(value) => value, }; - Some(ServoBundledURI { + ServoBundledURI { mURLString: ptr, mURLStringLength: len as u32, mBaseURI: extra_data.base.get(), mReferrer: extra_data.referrer.get(), mPrincipal: extra_data.principal.get(), - }) + } } }