/* 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/. */ // `data` comes from components/style/properties.mako.rs; see build.rs for more details. <%! from data import to_rust_ident, to_camel_case from data import Keyword %> <%namespace name="helpers" file="/helpers.mako.rs" /> use app_units::Au; use cssparser::Color; use custom_properties::ComputedValuesMap; use gecko_bindings::bindings; % for style_struct in data.style_structs: use gecko_bindings::structs::${style_struct.gecko_ffi_name}; use gecko_bindings::bindings::Gecko_Construct_Default_${style_struct.gecko_ffi_name}; use gecko_bindings::bindings::Gecko_CopyConstruct_${style_struct.gecko_ffi_name}; use gecko_bindings::bindings::Gecko_Destroy_${style_struct.gecko_ffi_name}; % endfor use gecko_bindings::bindings::Gecko_Construct_nsStyleVariables; use gecko_bindings::bindings::Gecko_CopyCursorArrayFrom; use gecko_bindings::bindings::Gecko_CopyFontFamilyFrom; use gecko_bindings::bindings::Gecko_CopyImageValueFrom; use gecko_bindings::bindings::Gecko_CopyListStyleImageFrom; use gecko_bindings::bindings::Gecko_CopyListStyleTypeFrom; use gecko_bindings::bindings::Gecko_Destroy_nsStyleVariables; use gecko_bindings::bindings::Gecko_EnsureImageLayersLength; use gecko_bindings::bindings::Gecko_FontFamilyList_AppendGeneric; use gecko_bindings::bindings::Gecko_FontFamilyList_AppendNamed; use gecko_bindings::bindings::Gecko_FontFamilyList_Clear; use gecko_bindings::bindings::Gecko_SetCursorArrayLength; use gecko_bindings::bindings::Gecko_SetCursorImage; use gecko_bindings::bindings::Gecko_StyleTransition_SetUnsupportedProperty; use gecko_bindings::bindings::Gecko_NewCSSShadowArray; use gecko_bindings::bindings::Gecko_nsStyleFont_SetLang; use gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom; use gecko_bindings::bindings::Gecko_SetListStyleImage; use gecko_bindings::bindings::Gecko_SetListStyleImageNone; use gecko_bindings::bindings::Gecko_SetListStyleType; use gecko_bindings::bindings::Gecko_SetNullImageValue; use gecko_bindings::bindings::ServoComputedValuesBorrowedOrNull; use gecko_bindings::bindings::{Gecko_ResetFilters, Gecko_CopyFiltersFrom}; use gecko_bindings::bindings::RawGeckoPresContextBorrowed; use gecko_bindings::structs::{self, StyleComplexColor}; use gecko_bindings::structs::nsCSSPropertyID; use gecko_bindings::structs::nsStyleVariables; use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordData, CoordDataMut}; use gecko_bindings::sugar::ownership::HasArcFFI; use gecko::values::convert_nscolor_to_rgba; use gecko::values::convert_rgba_to_nscolor; use gecko::values::GeckoStyleCoordConvertible; use gecko::values::round_border_to_device_pixels; use logical_geometry::WritingMode; use properties::animated_properties::TransitionProperty; use properties::longhands; use properties::{Importance, LonghandId}; use properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyDeclarationId}; use std::fmt::{self, Debug}; use std::mem::{forget, transmute, zeroed}; use std::ptr; use std::sync::Arc; use std::cmp; use values::computed::ToComputedValue; use values::{Either, Auto, KeyframesName}; use computed_values::border_style; pub mod style_structs { % for style_struct in data.style_structs: pub use super::${style_struct.gecko_struct_name} as ${style_struct.name}; % endfor } #[derive(Clone, Debug)] pub struct ComputedValues { % for style_struct in data.style_structs: ${style_struct.ident}: Arc, % endfor custom_properties: Option>, pub writing_mode: WritingMode, pub root_font_size: Au, /// font-size keyword values (and font-size-relative values applied /// to keyword values) need to preserve their identity as originating /// from keywords and relative font sizes. We store this information /// out of band in the ComputedValues. When None, the font size on the /// current struct was computed from a value that was not a keyword /// or a chain of font-size-relative values applying to successive parents /// terminated by a keyword. When Some, this means the font-size was derived /// from a keyword value or a keyword value on some ancestor with only /// font-size-relative keywords and regular inheritance in between. The /// integer stores the final ratio of the chain of font size relative values. /// and is 1 when there was just a keyword and no relative values. /// /// When this is Some, we compute font sizes by computing the keyword against /// the generic font, and then multiplying it by the ratio. pub font_size_keyword: Option<(longhands::font_size::KeywordSize, f32)>, /// The cached system font. See longhand/font.mako.rs pub cached_system_font: Option, } impl ComputedValues { pub fn new(custom_properties: Option>, writing_mode: WritingMode, root_font_size: Au, font_size_keyword: Option<(longhands::font_size::KeywordSize, f32)>, % for style_struct in data.style_structs: ${style_struct.ident}: Arc, % endfor ) -> Self { ComputedValues { custom_properties: custom_properties, writing_mode: writing_mode, root_font_size: root_font_size, cached_system_font: None, font_size_keyword: font_size_keyword, % for style_struct in data.style_structs: ${style_struct.ident}: ${style_struct.ident}, % endfor } } pub fn default_values(pres_context: RawGeckoPresContextBorrowed) -> Arc { Arc::new(ComputedValues { custom_properties: None, writing_mode: WritingMode::empty(), // FIXME(bz): This seems dubious root_font_size: longhands::font_size::get_initial_value(), // FIXME(bz): Also seems dubious? font_size_keyword: Some((Default::default(), 1.)), cached_system_font: None, % for style_struct in data.style_structs: ${style_struct.ident}: style_structs::${style_struct.name}::default(pres_context), % endfor }) } #[inline] pub fn is_display_contents(&self) -> bool { self.get_box().clone_display() == longhands::display::computed_value::T::contents } % for style_struct in data.style_structs: #[inline] pub fn clone_${style_struct.name_lower}(&self) -> Arc { self.${style_struct.ident}.clone() } #[inline] pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} { &self.${style_struct.ident} } pub fn ${style_struct.name_lower}_arc(&self) -> &Arc { &self.${style_struct.ident} } #[inline] pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} { Arc::make_mut(&mut self.${style_struct.ident}) } % endfor pub fn custom_properties(&self) -> Option> { self.custom_properties.clone() } #[allow(non_snake_case)] pub fn has_moz_binding(&self) -> bool { !self.get_box().gecko.mBinding.mPtr.mRawPtr.is_null() } // FIXME(bholley): Implement this properly. #[inline] pub fn is_multicol(&self) -> bool { false } pub fn to_declaration_block(&self, property: PropertyDeclarationId) -> PropertyDeclarationBlock { match property { % for prop in data.longhands: % if prop.animatable: PropertyDeclarationId::Longhand(LonghandId::${prop.camel_case}) => { PropertyDeclarationBlock::with_one( PropertyDeclaration::${prop.camel_case}( % if prop.boxed: Box::new( % endif longhands::${prop.ident}::SpecifiedValue::from_computed_value( &self.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}()) % if prop.boxed: ) % endif ), Importance::Normal ) }, % endif % endfor PropertyDeclarationId::Custom(_name) => unimplemented!(), _ => unimplemented!() } } } <%def name="declare_style_struct(style_struct)"> pub struct ${style_struct.gecko_struct_name} { gecko: ${style_struct.gecko_ffi_name}, } impl ${style_struct.gecko_struct_name} { pub fn gecko(&self) -> &${style_struct.gecko_ffi_name} { &self.gecko } } <%def name="impl_simple_setter(ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { ${set_gecko_property(gecko_ffi_name, "v")} } <%def name="impl_simple_clone(ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { self.gecko.${gecko_ffi_name} } <%def name="impl_simple_copy(ident, gecko_ffi_name, *kwargs)"> #[allow(non_snake_case)] pub fn copy_${ident}_from(&mut self, other: &Self) { self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name}; } <%def name="impl_coord_copy(ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn copy_${ident}_from(&mut self, other: &Self) { self.gecko.${gecko_ffi_name}.copy_from(&other.gecko.${gecko_ffi_name}); } <%! def get_gecko_property(ffi_name, self_param = "self"): if "mBorderColor" in ffi_name: return ffi_name.replace("mBorderColor", "unsafe { *%s.gecko.__bindgen_anon_1.mBorderColor.as_ref() }" % self_param) return "%s.gecko.%s" % (self_param, ffi_name) def set_gecko_property(ffi_name, expr): if "mBorderColor" in ffi_name: ffi_name = ffi_name.replace("mBorderColor", "*self.gecko.__bindgen_anon_1.mBorderColor.as_mut()") return "unsafe { %s = %s };" % (ffi_name, expr) return "self.gecko.%s = %s;" % (ffi_name, expr) %> <%def name="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type='u8', on_set=None)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { use properties::longhands::${ident}::computed_value::T as Keyword; // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts let result = match v { % for value in keyword.values_for('gecko'): Keyword::${to_rust_ident(value)} => structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast(cast_type)}, % endfor }; ${set_gecko_property(gecko_ffi_name, "result")} % if on_set: self.${on_set}(); % endif } <%def name="impl_keyword_clone(ident, gecko_ffi_name, keyword)"> #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { use properties::longhands::${ident}::computed_value::T as Keyword; // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts match ${get_gecko_property(gecko_ffi_name)} ${keyword.maybe_cast("u32")} { % for value in keyword.values_for('gecko'): structs::${keyword.gecko_constant(value)} => Keyword::${to_rust_ident(value)}, % endfor % if keyword.gecko_inexhaustive: x => panic!("Found unexpected value in style struct for ${ident} property: {:?}", x), % endif } } /// Convert a Servo color into an nscolor; with currentColor as 0 /// /// Call sites will need to be updated after https://bugzilla.mozilla.org/show_bug.cgi?id=760345 fn color_to_nscolor_zero_currentcolor(color: Color) -> structs::nscolor { match color { Color::RGBA(rgba) => { convert_rgba_to_nscolor(&rgba) }, Color::CurrentColor => 0, } } <%def name="impl_color_setter(ident, gecko_ffi_name, complex_color=True)"> #[allow(unreachable_code)] #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { % if complex_color: let result = v.into(); % else: let result = color_to_nscolor_zero_currentcolor(v); % endif ${set_gecko_property(gecko_ffi_name, "result")} } <%def name="impl_color_copy(ident, gecko_ffi_name, complex_color=True)"> #[allow(non_snake_case)] pub fn copy_${ident}_from(&mut self, other: &Self) { let color = ${get_gecko_property(gecko_ffi_name, self_param = "other")}; ${set_gecko_property(gecko_ffi_name, "color")}; } <%def name="impl_color_clone(ident, gecko_ffi_name, complex_color=True)"> #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { % if complex_color: ${get_gecko_property(gecko_ffi_name)}.into() % else: Color::RGBA(convert_nscolor_to_rgba(${get_gecko_property(gecko_ffi_name)})) % endif } <%def name="impl_keyword(ident, gecko_ffi_name, keyword, need_clone, **kwargs)"> <%call expr="impl_keyword_setter(ident, gecko_ffi_name, keyword, **kwargs)"> <%call expr="impl_simple_copy(ident, gecko_ffi_name)"> %if need_clone: <%call expr="impl_keyword_clone(ident, gecko_ffi_name, keyword)"> % endif <%def name="impl_simple(ident, gecko_ffi_name, need_clone=False)"> <%call expr="impl_simple_setter(ident, gecko_ffi_name)"> <%call expr="impl_simple_copy(ident, gecko_ffi_name)"> % if need_clone: <%call expr="impl_simple_clone(ident, gecko_ffi_name)"> % endif <%def name="impl_absolute_length(ident, gecko_ffi_name, need_clone=False)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { ${set_gecko_property(gecko_ffi_name, "v.0")} } <%call expr="impl_simple_copy(ident, gecko_ffi_name)"> % if need_clone: #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { Au(self.gecko.${gecko_ffi_name}) } % endif <%def name="impl_position(ident, gecko_ffi_name, need_clone=False)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { ${set_gecko_property("%s.mXPosition" % gecko_ffi_name, "v.horizontal.0.into()")} ${set_gecko_property("%s.mYPosition" % gecko_ffi_name, "v.vertical.0.into()")} } <%call expr="impl_simple_copy(ident, gecko_ffi_name)"> % if need_clone: #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { use values::generics::position::{HorizontalPosition, Position, VerticalPosition}; Position { horizontal: HorizontalPosition(self.gecko.${gecko_ffi_name}.mXPosition.into()), vertical: VerticalPosition(self.gecko.${gecko_ffi_name}.mYPosition.into()), } } % endif <%def name="impl_color(ident, gecko_ffi_name, need_clone=False, complex_color=True)"> <%call expr="impl_color_setter(ident, gecko_ffi_name, complex_color)"> <%call expr="impl_color_copy(ident, gecko_ffi_name, complex_color)"> % if need_clone: <%call expr="impl_color_clone(ident, gecko_ffi_name, complex_color)"> % endif <%def name="impl_svg_paint(ident, gecko_ffi_name, need_clone=False, complex_color=True)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, mut v: longhands::${ident}::computed_value::T) { use values::computed::SVGPaintKind; use self::structs::nsStyleSVGPaintType; use self::structs::nsStyleSVGFallbackType; let ref mut paint = ${get_gecko_property(gecko_ffi_name)}; unsafe { bindings::Gecko_nsStyleSVGPaint_Reset(paint); } let fallback = v.fallback.take(); match v.kind { SVGPaintKind::None => return, SVGPaintKind::ContextFill => { paint.mType = nsStyleSVGPaintType::eStyleSVGPaintType_ContextFill; } SVGPaintKind::ContextStroke => { paint.mType = nsStyleSVGPaintType::eStyleSVGPaintType_ContextStroke; } SVGPaintKind::PaintServer(url) => { unsafe { bindings::Gecko_nsStyleSVGPaint_SetURLValue(paint, url.for_ffi()); } } SVGPaintKind::Color(color) => { paint.mType = nsStyleSVGPaintType::eStyleSVGPaintType_Color; unsafe { *paint.mPaint.mColor.as_mut() = color_to_nscolor_zero_currentcolor(color); } } } if let Some(fallback) = fallback { paint.mFallbackType = nsStyleSVGFallbackType::eStyleSVGFallbackType_Color; paint.mFallbackColor = color_to_nscolor_zero_currentcolor(fallback); } } #[allow(non_snake_case)] pub fn copy_${ident}_from(&mut self, other: &Self) { unsafe { bindings::Gecko_nsStyleSVGPaint_CopyFrom( &mut ${get_gecko_property(gecko_ffi_name)}, & ${get_gecko_property(gecko_ffi_name, "other")} ); } } <%def name="impl_app_units(ident, gecko_ffi_name, need_clone, inherit_from=None, round_to_pixels=False)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { let value = { % if round_to_pixels: let au_per_device_px = Au(self.gecko.mTwipsPerPixel); round_border_to_device_pixels(v, au_per_device_px).0 % else: v.0 % endif }; % if inherit_from: self.gecko.${inherit_from} = value; % endif self.gecko.${gecko_ffi_name} = value; } #[allow(non_snake_case)] pub fn copy_${ident}_from(&mut self, other: &Self) { % if inherit_from: self.gecko.${inherit_from} = other.gecko.${inherit_from}; // NOTE: This is needed to easily handle the `unset` and `initial` // keywords, which are implemented calling this function. // // In practice, this means that we may have an incorrect value here, but // we'll adjust that properly in the style fixup phase. self.gecko.${gecko_ffi_name} = other.gecko.${inherit_from}; % else: self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name}; % endif } %if need_clone: #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { Au(self.gecko.${gecko_ffi_name}) } % endif <%def name="impl_split_style_coord(ident, gecko_ffi_name, index, need_clone=False)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { v.to_gecko_style_coord(&mut self.gecko.${gecko_ffi_name}.data_at_mut(${index})); } #[allow(non_snake_case)] pub fn copy_${ident}_from(&mut self, other: &Self) { self.gecko.${gecko_ffi_name}.data_at_mut(${index}).copy_from(&other.gecko.${gecko_ffi_name}.data_at(${index})); } % if need_clone: #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { use properties::longhands::${ident}::computed_value::T; T::from_gecko_style_coord(&self.gecko.${gecko_ffi_name}.data_at(${index})) .expect("clone for ${ident} failed") } % endif <%def name="impl_style_coord(ident, gecko_ffi_name, need_clone=False)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { v.to_gecko_style_coord(&mut self.gecko.${gecko_ffi_name}); } #[allow(non_snake_case)] pub fn copy_${ident}_from(&mut self, other: &Self) { self.gecko.${gecko_ffi_name}.copy_from(&other.gecko.${gecko_ffi_name}); } % if need_clone: #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { use properties::longhands::${ident}::computed_value::T; T::from_gecko_style_coord(&self.gecko.${gecko_ffi_name}) .expect("clone for ${ident} failed") } % endif <%def name="impl_corner_style_coord(ident, gecko_ffi_name, x_index, y_index, need_clone=False)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { v.0.width.to_gecko_style_coord(&mut self.gecko.${gecko_ffi_name}.data_at_mut(${x_index})); v.0.height.to_gecko_style_coord(&mut self.gecko.${gecko_ffi_name}.data_at_mut(${y_index})); } #[allow(non_snake_case)] pub fn copy_${ident}_from(&mut self, other: &Self) { self.gecko.${gecko_ffi_name}.data_at_mut(${x_index}) .copy_from(&other.gecko.${gecko_ffi_name}.data_at(${x_index})); self.gecko.${gecko_ffi_name}.data_at_mut(${y_index}) .copy_from(&other.gecko.${gecko_ffi_name}.data_at(${y_index})); } % if need_clone: #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { use values::generics::BorderRadiusSize; let width = GeckoStyleCoordConvertible::from_gecko_style_coord( &self.gecko.${gecko_ffi_name}.data_at(${x_index})) .expect("Failed to clone ${ident}"); let height = GeckoStyleCoordConvertible::from_gecko_style_coord( &self.gecko.${gecko_ffi_name}.data_at(${y_index})) .expect("Failed to clone ${ident}"); BorderRadiusSize::new(width, height) } % endif <%def name="impl_css_url(ident, gecko_ffi_name, need_clone=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 { 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) } Either::Second(_none) => { unsafe { self.gecko.${gecko_ffi_name}.clear(); } } } } #[allow(non_snake_case)] pub fn copy_${ident}_from(&mut self, other: &Self) { unsafe { self.gecko.${gecko_ffi_name}.set(&other.gecko.${gecko_ffi_name}); } } % if need_clone: <% raise Exception("Do not know how to handle clone ") %> % endif <%def name="impl_logical(name, need_clone=False, **kwargs)"> ${helpers.logical_setter(name, need_clone)} <%def name="impl_style_struct(style_struct)"> impl ${style_struct.gecko_struct_name} { #[allow(dead_code, unused_variables)] pub fn default(pres_context: RawGeckoPresContextBorrowed) -> Arc { let mut result = Arc::new(${style_struct.gecko_struct_name} { gecko: unsafe { zeroed() } }); unsafe { Gecko_Construct_Default_${style_struct.gecko_ffi_name}(&mut Arc::get_mut(&mut result).unwrap().gecko, pres_context); } result } pub fn get_gecko(&self) -> &${style_struct.gecko_ffi_name} { &self.gecko } } impl Drop for ${style_struct.gecko_struct_name} { fn drop(&mut self) { unsafe { Gecko_Destroy_${style_struct.gecko_ffi_name}(&mut self.gecko); } } } impl Clone for ${style_struct.gecko_struct_name} { fn clone(&self) -> Self { unsafe { let mut result = ${style_struct.gecko_struct_name} { gecko: zeroed() }; Gecko_CopyConstruct_${style_struct.gecko_ffi_name}(&mut result.gecko, &self.gecko); result } } } // FIXME(bholley): Make bindgen generate Debug for all types. %if style_struct.gecko_ffi_name in ("nsStyle" + x for x in "Border Display List Background Font SVGReset".split()): impl Debug for ${style_struct.gecko_struct_name} { // FIXME(bholley): Generate this. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Gecko style struct: ${style_struct.gecko_struct_name}") } } %else: impl Debug for ${style_struct.gecko_struct_name} { // FIXME(bholley): Generate this. fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.gecko.fmt(f) } } %endif <%def name="raw_impl_trait(style_struct, skip_longhands='', skip_additionals='')"> <% longhands = [x for x in style_struct.longhands if not (skip_longhands == "*" or x.name in skip_longhands.split())] # # Make a list of types we can't auto-generate. # force_stub = []; # These have unusual representations in gecko. force_stub += ["list-style-type"] # Types used with predefined_type()-defined properties that we can auto-generate. predefined_types = { "length::LengthOrAuto": impl_style_coord, "length::LengthOrNormal": impl_style_coord, "Length": impl_absolute_length, "Position": impl_position, "LengthOrPercentage": impl_style_coord, "LengthOrPercentageOrAuto": impl_style_coord, "LengthOrPercentageOrNone": impl_style_coord, "LengthOrNone": impl_style_coord, "LengthOrNormal": impl_style_coord, "MaxLength": impl_style_coord, "MinLength": impl_style_coord, "Number": impl_simple, "Integer": impl_simple, "Opacity": impl_simple, "CSSColor": impl_color, "SVGPaint": impl_svg_paint, "UrlOrNone": impl_css_url, } def longhand_method(longhand): args = dict(ident=longhand.ident, gecko_ffi_name=longhand.gecko_ffi_name, need_clone=longhand.need_clone) # get the method and pass additional keyword or type-specific arguments if longhand.logical: method = impl_logical args.update(name=longhand.name) elif longhand.keyword: method = impl_keyword args.update(keyword=longhand.keyword) if "font" in longhand.ident: args.update(cast_type=longhand.cast_type) else: method = predefined_types[longhand.predefined_type] if longhand.predefined_type in ["CSSColor"]: args.update(complex_color=longhand.complex_color) method(**args) picked_longhands, stub_longhands = [], [] for x in longhands: if (x.keyword or x.predefined_type in predefined_types or x.logical) and x.name not in force_stub: picked_longhands.append(x) else: stub_longhands.append(x) # If one of the longhands is not handled # by either: # - being a keyword # - being a predefined longhand # - being a longhand with manual glue code (i.e. in skip_longhands) # - being generated as a stub # # then we raise an error here. # # If you hit this error, please add `product="servo"` to the longhand. # In case the longhand is used in a shorthand, add it to the force_stub # list above. for stub in stub_longhands: if stub.name not in force_stub: raise Exception("Don't know what to do with longhand %s in style struct %s" % (stub.name,style_struct. gecko_struct_name)) %> impl ${style_struct.gecko_struct_name} { /* * Manually-Implemented Methods. */ ${caller.body().strip()} /* * Auto-Generated Methods. */ <% for longhand in picked_longhands: longhand_method(longhand) %> /* * Stubs. */ % for longhand in stub_longhands: #[allow(non_snake_case)] pub fn set_${longhand.ident}(&mut self, _: longhands::${longhand.ident}::computed_value::T) { warn!("stylo: Unimplemented property setter: ${longhand.name}"); } #[allow(non_snake_case)] pub fn copy_${longhand.ident}_from(&mut self, _: &Self) { warn!("stylo: Unimplemented property setter: ${longhand.name}"); } % if longhand.need_clone: #[allow(non_snake_case)] pub fn clone_${longhand.ident}(&self) -> longhands::${longhand.ident}::computed_value::T { unimplemented!() } % endif % if longhand.need_index: pub fn ${longhand.ident}_count(&self) -> usize { 0 } pub fn ${longhand.ident}_at(&self, _index: usize) -> longhands::${longhand.ident}::computed_value::SingleComputedValue { unimplemented!() } % endif % endfor <% additionals = [x for x in style_struct.additional_methods if skip_additionals != "*" and not x.name in skip_additionals.split()] %> % for additional in additionals: ${additional.stub()} % endfor } <% data.manual_style_structs = [] %> <%def name="impl_trait(style_struct_name, skip_longhands='', skip_additionals='')"> <%self:raw_impl_trait style_struct="${next(x for x in data.style_structs if x.name == style_struct_name)}" skip_longhands="${skip_longhands}" skip_additionals="${skip_additionals}"> ${caller.body()} <% data.manual_style_structs.append(style_struct_name) %> <%! class Side(object): def __init__(self, name, index): self.name = name self.ident = name.lower() self.index = index class Corner(object): def __init__(self, vert, horiz, index): self.x_name = "HalfCorner::eCorner" + vert + horiz + "X" self.y_name = "HalfCorner::eCorner" + vert + horiz + "Y" self.ident = (vert + "_" + horiz).lower() self.x_index = 2 * index self.y_index = 2 * index + 1 class GridLine(object): def __init__(self, name): self.ident = "grid-" + name.lower() self.name = self.ident.replace('-', '_') self.gecko = "m" + to_camel_case(self.ident) SIDES = [Side("Top", 0), Side("Right", 1), Side("Bottom", 2), Side("Left", 3)] CORNERS = [Corner("Top", "Left", 0), Corner("Top", "Right", 1), Corner("Bottom", "Right", 2), Corner("Bottom", "Left", 3)] GRID_LINES = map(GridLine, ["row-start", "row-end", "column-start", "column-end"]) %> #[allow(dead_code)] fn static_assert() { unsafe { % for corner in CORNERS: transmute::<_, [u32; ${corner.x_index}]>([1; structs::${corner.x_name} as usize]); transmute::<_, [u32; ${corner.y_index}]>([1; structs::${corner.y_name} as usize]); % endfor } // Note: using the above technique with an enum hits a rust bug when |structs| is in a different crate. % for side in SIDES: { const DETAIL: u32 = [0][(structs::Side::eSide${side.name} as usize != ${side.index}) as usize]; let _ = DETAIL; } % endfor } <% border_style_keyword = Keyword("border-style", "none solid double dotted dashed hidden groove ridge inset outset") %> <% skip_border_longhands = " ".join(["border-{0}-{1}".format(x.ident, y) for x in SIDES for y in ["color", "style", "width"]] + ["border-{0}-radius".format(x.ident.replace("_", "-")) for x in CORNERS]) %> <% skip_moz_border_color_longhands = " ".join("-moz-border-{0}-colors".format(x.ident) for x in SIDES) %> <%self:impl_trait style_struct_name="Border" skip_longhands="${skip_border_longhands} border-image-source border-image-outset border-image-repeat border-image-width border-image-slice ${skip_moz_border_color_longhands}" skip_additionals="*"> % for side in SIDES: <% impl_keyword("border_%s_style" % side.ident, "mBorderStyle[%s]" % side.index, border_style_keyword, on_set="update_border_%s" % side.ident, need_clone=True) %> // This is needed because the initial mComputedBorder value is set to zero. // // In order to compute stuff, we start from the initial struct, and keep // going down the tree applying properties. // // That means, effectively, that when we set border-style to something // non-hidden, we should use the initial border instead. // // Servo stores the initial border-width in the initial struct, and then // adjusts as needed in the fixup phase. This means that the initial struct // is technically not valid without fixups, and that you lose pretty much // any sharing of the initial struct, which is kind of unfortunate. // // Gecko has two fields for this, one that stores the "specified" border, // and other that stores the actual computed one. That means that when we // set border-style, border-width may change and we need to sync back to the // specified one. This is what this function does. // // Note that this doesn't impose any dependency in the order of computation // of the properties. This is only relevant if border-style is specified, // but border-width isn't. If border-width is specified at some point, the // two mBorder and mComputedBorder fields would be the same already. // // Once we're here, we know that we'll run style fixups, so it's fine to // just copy the specified border here, we'll adjust it if it's incorrect // later. fn update_border_${side.ident}(&mut self) { self.gecko.mComputedBorder.${side.ident} = self.gecko.mBorder.${side.ident}; } <% impl_color("border_%s_color" % side.ident, "(mBorderColor)[%s]" % side.index, need_clone=True) %> <% impl_app_units("border_%s_width" % side.ident, "mComputedBorder.%s" % side.ident, inherit_from="mBorder.%s" % side.ident, need_clone=True, round_to_pixels=True) %> pub fn border_${side.ident}_has_nonzero_width(&self) -> bool { self.gecko.mComputedBorder.${side.ident} != 0 } #[allow(non_snake_case)] pub fn set__moz_border_${side.ident}_colors(&mut self, v: longhands::_moz_border_${side.ident}_colors::computed_value::T) { match v.0 { None => { unsafe { bindings::Gecko_ClearMozBorderColors(&mut self.gecko, structs::Side::eSide${to_camel_case(side.ident)}); } }, Some(ref colors) => { unsafe { bindings::Gecko_EnsureMozBorderColors(&mut self.gecko); bindings::Gecko_ClearMozBorderColors(&mut self.gecko, structs::Side::eSide${to_camel_case(side.ident)}); } for color in colors { let c = color_to_nscolor_zero_currentcolor(*color); unsafe { bindings::Gecko_AppendMozBorderColors(&mut self.gecko, structs::Side::eSide${to_camel_case(side.ident)}, c); } } } } } #[allow(non_snake_case)] pub fn copy__moz_border_${side.ident}_colors_from(&mut self, other: &Self) { unsafe { bindings::Gecko_CopyMozBorderColors(&mut self.gecko, &other.gecko, structs::Side::eSide${to_camel_case(side.ident)}); } } % endfor % for corner in CORNERS: <% impl_corner_style_coord("border_%s_radius" % corner.ident, "mBorderRadius", corner.x_index, corner.y_index, need_clone=True) %> % endfor pub fn set_border_image_source(&mut self, v: longhands::border_image_source::computed_value::T) { unsafe { // Prevent leaking of the last elements we did set Gecko_SetNullImageValue(&mut self.gecko.mBorderImageSource); } if let Some(image) = v.0 { self.gecko.mBorderImageSource.set(image, &mut false) } } pub fn copy_border_image_source_from(&mut self, other: &Self) { unsafe { Gecko_CopyImageValueFrom(&mut self.gecko.mBorderImageSource, &other.gecko.mBorderImageSource); } } pub fn set_border_image_outset(&mut self, v: longhands::border_image_outset::computed_value::T) { % for side in SIDES: v.${side.index}.to_gecko_style_coord(&mut self.gecko.mBorderImageOutset .data_at_mut(${side.index})); % endfor } pub fn copy_border_image_outset_from(&mut self, other: &Self) { % for side in SIDES: self.gecko.mBorderImageOutset.data_at_mut(${side.index}) .copy_from(&other.gecko.mBorderImageOutset.data_at(${side.index})); % endfor } pub fn set_border_image_repeat(&mut self, v: longhands::border_image_repeat::computed_value::T) { use properties::longhands::border_image_repeat::computed_value::RepeatKeyword; use gecko_bindings::structs::{NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH, NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT}; use gecko_bindings::structs::{NS_STYLE_BORDER_IMAGE_REPEAT_ROUND, NS_STYLE_BORDER_IMAGE_REPEAT_SPACE}; % for i, side in enumerate(["H", "V"]): let k = match v.${i} { RepeatKeyword::Stretch => NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH, RepeatKeyword::Repeat => NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT, RepeatKeyword::Round => NS_STYLE_BORDER_IMAGE_REPEAT_ROUND, RepeatKeyword::Space => NS_STYLE_BORDER_IMAGE_REPEAT_SPACE, }; self.gecko.mBorderImageRepeat${side} = k as u8; % endfor } pub fn copy_border_image_repeat_from(&mut self, other: &Self) { self.gecko.mBorderImageRepeatH = other.gecko.mBorderImageRepeatH; self.gecko.mBorderImageRepeatV = other.gecko.mBorderImageRepeatV; } pub fn set_border_image_width(&mut self, v: longhands::border_image_width::computed_value::T) { use properties::longhands::border_image_width::computed_value::SingleComputedValue; % for side in SIDES: match v.${side.index} { SingleComputedValue::Auto => { self.gecko.mBorderImageWidth.data_at_mut(${side.index}).set_value(CoordDataValue::Auto) }, SingleComputedValue::LengthOrPercentage(l) => { l.to_gecko_style_coord(&mut self.gecko.mBorderImageWidth.data_at_mut(${side.index})) }, SingleComputedValue::Number(n) => { self.gecko.mBorderImageWidth.data_at_mut(${side.index}).set_value(CoordDataValue::Factor(n)) }, } % endfor } pub fn copy_border_image_width_from(&mut self, other: &Self) { % for side in SIDES: self.gecko.mBorderImageWidth.data_at_mut(${side.index}) .copy_from(&other.gecko.mBorderImageWidth.data_at(${side.index})); % endfor } pub fn set_border_image_slice(&mut self, v: longhands::border_image_slice::computed_value::T) { use gecko_bindings::structs::{NS_STYLE_BORDER_IMAGE_SLICE_NOFILL, NS_STYLE_BORDER_IMAGE_SLICE_FILL}; for (i, corner) in v.corners.iter().enumerate() { corner.to_gecko_style_coord(&mut self.gecko.mBorderImageSlice.data_at_mut(i)); } let fill = if v.fill { NS_STYLE_BORDER_IMAGE_SLICE_FILL } else { NS_STYLE_BORDER_IMAGE_SLICE_NOFILL }; self.gecko.mBorderImageFill = fill as u8; } pub fn copy_border_image_slice_from(&mut self, other: &Self) { for i in 0..4 { self.gecko.mBorderImageSlice.data_at_mut(i) .copy_from(&other.gecko.mBorderImageSlice.data_at(i)); } self.gecko.mBorderImageFill = other.gecko.mBorderImageFill; } <% skip_margin_longhands = " ".join(["margin-%s" % x.ident for x in SIDES]) %> <%self:impl_trait style_struct_name="Margin" skip_longhands="${skip_margin_longhands}"> % for side in SIDES: <% impl_split_style_coord("margin_%s" % side.ident, "mMargin", side.index, need_clone=True) %> % endfor <% skip_padding_longhands = " ".join(["padding-%s" % x.ident for x in SIDES]) %> <%self:impl_trait style_struct_name="Padding" skip_longhands="${skip_padding_longhands}"> % for side in SIDES: <% impl_split_style_coord("padding_%s" % side.ident, "mPadding", side.index, need_clone=True) %> % endfor <% skip_position_longhands = " ".join(x.ident for x in SIDES + GRID_LINES) %> <%self:impl_trait style_struct_name="Position" skip_longhands="${skip_position_longhands} z-index box-sizing order align-content justify-content align-self justify-self align-items justify-items grid-auto-rows grid-auto-columns grid-auto-flow"> % for side in SIDES: <% impl_split_style_coord("%s" % side.ident, "mOffset", side.index, need_clone=True) %> % endfor pub fn set_z_index(&mut self, v: longhands::z_index::computed_value::T) { match v { Either::First(n) => self.gecko.mZIndex.set_value(CoordDataValue::Integer(n)), Either::Second(Auto) => self.gecko.mZIndex.set_value(CoordDataValue::Auto), } } pub fn copy_z_index_from(&mut self, other: &Self) { use gecko_bindings::structs::nsStyleUnit; // z-index is never a calc(). If it were, we'd be leaking here, so // assert that it isn't. debug_assert!(self.gecko.mZIndex.unit() != nsStyleUnit::eStyleUnit_Calc); unsafe { self.gecko.mZIndex.copy_from_unchecked(&other.gecko.mZIndex); } } pub fn clone_z_index(&self) -> longhands::z_index::computed_value::T { return match self.gecko.mZIndex.as_value() { CoordDataValue::Integer(n) => Either::First(n), CoordDataValue::Auto => Either::Second(Auto), _ => { debug_assert!(false); Either::First(0) } } } pub fn set_align_content(&mut self, v: longhands::align_content::computed_value::T) { self.gecko.mAlignContent = v.bits() } ${impl_simple_copy('align_content', 'mAlignContent')} pub fn set_justify_content(&mut self, v: longhands::justify_content::computed_value::T) { self.gecko.mJustifyContent = v.bits() } ${impl_simple_copy('justify_content', 'mJustifyContent')} pub fn set_align_self(&mut self, v: longhands::align_self::computed_value::T) { self.gecko.mAlignSelf = v.0.bits() } ${impl_simple_copy('align_self', 'mAlignSelf')} pub fn set_justify_self(&mut self, v: longhands::justify_self::computed_value::T) { self.gecko.mJustifySelf = v.0.bits() } ${impl_simple_copy('justify_self', 'mJustifySelf')} pub fn set_align_items(&mut self, v: longhands::align_items::computed_value::T) { self.gecko.mAlignItems = v.0.bits() } ${impl_simple_copy('align_items', 'mAlignItems')} pub fn clone_align_items(&self) -> longhands::align_items::computed_value::T { use values::specified::align::{AlignFlags, AlignItems}; AlignItems(AlignFlags::from_bits(self.gecko.mAlignItems) .expect("mAlignItems contains valid flags")) } pub fn set_justify_items(&mut self, v: longhands::justify_items::computed_value::T) { self.gecko.mJustifyItems = v.0.bits() } ${impl_simple_copy('justify_items', 'mJustifyItems')} pub fn clone_justify_items(&self) -> longhands::justify_items::computed_value::T { use values::specified::align::{AlignFlags, JustifyItems}; JustifyItems(AlignFlags::from_bits(self.gecko.mJustifyItems) .expect("mJustifyItems contains valid flags")) } pub fn set_box_sizing(&mut self, v: longhands::box_sizing::computed_value::T) { use computed_values::box_sizing::T; use gecko_bindings::structs::StyleBoxSizing; // TODO: guess what to do with box-sizing: padding-box self.gecko.mBoxSizing = match v { T::content_box => StyleBoxSizing::Content, T::border_box => StyleBoxSizing::Border } } ${impl_simple_copy('box_sizing', 'mBoxSizing')} pub fn set_order(&mut self, v: longhands::order::computed_value::T) { self.gecko.mOrder = v; } pub fn clone_order(&self) -> longhands::order::computed_value::T { self.gecko.mOrder } ${impl_simple_copy('order', 'mOrder')} % for value in GRID_LINES: pub fn set_${value.name}(&mut self, v: longhands::${value.name}::computed_value::T) { use gecko_bindings::structs::{nsStyleGridLine_kMinLine, nsStyleGridLine_kMaxLine}; let ident = v.ident.unwrap_or(String::new()); self.gecko.${value.gecko}.mLineName.assign_utf8(&ident); self.gecko.${value.gecko}.mHasSpan = v.is_span; self.gecko.${value.gecko}.mInteger = v.integer.map(|i| { // clamping the integer between a range cmp::max(nsStyleGridLine_kMinLine, cmp::min(i, nsStyleGridLine_kMaxLine)) }).unwrap_or(0); } pub fn copy_${value.name}_from(&mut self, other: &Self) { self.gecko.${value.gecko}.mHasSpan = other.gecko.${value.gecko}.mHasSpan; self.gecko.${value.gecko}.mInteger = other.gecko.${value.gecko}.mInteger; self.gecko.${value.gecko}.mLineName.assign(&*other.gecko.${value.gecko}.mLineName); } % endfor % for kind in ["rows", "columns"]: pub fn set_grid_auto_${kind}(&mut self, v: longhands::grid_auto_rows::computed_value::T) { use values::specified::grid::TrackSize; match v { TrackSize::FitContent(lop) => { // Gecko sets min value to None and max value to the actual value in fit-content // https://dxr.mozilla.org/mozilla-central/rev/0eef1d5/layout/style/nsRuleNode.cpp#8221 self.gecko.mGridAuto${kind.title()}Min.set_value(CoordDataValue::None); lop.to_gecko_style_coord(&mut self.gecko.mGridAuto${kind.title()}Max); }, TrackSize::Breadth(breadth) => { // Set the value to both fields if there's one breadth value // https://dxr.mozilla.org/mozilla-central/rev/0eef1d5/layout/style/nsRuleNode.cpp#8230 breadth.to_gecko_style_coord(&mut self.gecko.mGridAuto${kind.title()}Min); breadth.to_gecko_style_coord(&mut self.gecko.mGridAuto${kind.title()}Max); }, TrackSize::MinMax(min, max) => { min.to_gecko_style_coord(&mut self.gecko.mGridAuto${kind.title()}Min); max.to_gecko_style_coord(&mut self.gecko.mGridAuto${kind.title()}Max); }, } } pub fn copy_grid_auto_${kind}_from(&mut self, other: &Self) { self.gecko.mGridAuto${kind.title()}Min.copy_from(&other.gecko.mGridAuto${kind.title()}Min); self.gecko.mGridAuto${kind.title()}Max.copy_from(&other.gecko.mGridAuto${kind.title()}Max); } % endfor pub fn set_grid_auto_flow(&mut self, v: longhands::grid_auto_flow::computed_value::T) { use gecko_bindings::structs::NS_STYLE_GRID_AUTO_FLOW_ROW; use gecko_bindings::structs::NS_STYLE_GRID_AUTO_FLOW_COLUMN; use gecko_bindings::structs::NS_STYLE_GRID_AUTO_FLOW_DENSE; use properties::longhands::grid_auto_flow::computed_value::AutoFlow::{Row, Column}; self.gecko.mGridAutoFlow = 0; let value = match v.autoflow { Row => NS_STYLE_GRID_AUTO_FLOW_ROW, Column => NS_STYLE_GRID_AUTO_FLOW_COLUMN, }; self.gecko.mGridAutoFlow |= value as u8; if v.dense { self.gecko.mGridAutoFlow |= NS_STYLE_GRID_AUTO_FLOW_DENSE as u8; } } ${impl_simple_copy('grid_auto_flow', 'mGridAutoFlow')} <% skip_outline_longhands = " ".join("outline-style outline-width".split() + ["-moz-outline-radius-{0}".format(x.ident.replace("_", "")) for x in CORNERS]) %> <%self:impl_trait style_struct_name="Outline" skip_longhands="${skip_outline_longhands}" skip_additionals="*"> #[allow(non_snake_case)] pub fn set_outline_style(&mut self, v: longhands::outline_style::computed_value::T) { // FIXME(bholley): Align binary representations and ditch |match| for // cast + static_asserts let result = match v { % for value in border_style_keyword.values_for('gecko'): Either::Second(border_style::T::${to_rust_ident(value)}) => structs::${border_style_keyword.gecko_constant(value)} ${border_style_keyword.maybe_cast("u8")}, % endfor Either::First(Auto) => structs::${border_style_keyword.gecko_constant('auto')} ${border_style_keyword.maybe_cast("u8")}, }; ${set_gecko_property("mOutlineStyle", "result")} // NB: This is needed to correctly handling the initial value of // outline-width when outline-style changes, see the // update_border_${side} comment for more details. self.gecko.mActualOutlineWidth = self.gecko.mOutlineWidth; } #[allow(non_snake_case)] pub fn copy_outline_style_from(&mut self, other: &Self) { self.gecko.mOutlineStyle = other.gecko.mOutlineStyle; } #[allow(non_snake_case)] pub fn clone_outline_style(&self) -> longhands::outline_style::computed_value::T { // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts match ${get_gecko_property("mOutlineStyle")} ${border_style_keyword.maybe_cast("u32")} { % for value in border_style_keyword.values_for('gecko'): structs::${border_style_keyword.gecko_constant(value)} => Either::Second(border_style::T::${value}), % endfor structs::${border_style_keyword.gecko_constant('auto')} => Either::First(Auto), % if border_style_keyword.gecko_inexhaustive: x => panic!("Found unexpected value in style struct for outline_style property: {:?}", x), % endif } } <% impl_app_units("outline_width", "mActualOutlineWidth", inherit_from="mOutlineWidth", need_clone=True, round_to_pixels=True) %> % for corner in CORNERS: <% impl_corner_style_coord("_moz_outline_radius_%s" % corner.ident.replace("_", ""), "mOutlineRadius", corner.x_index, corner.y_index) %> % endfor pub fn outline_has_nonzero_width(&self) -> bool { self.gecko.mActualOutlineWidth != 0 } <% skip_font_longhands = """font-family font-size font-size-adjust font-weight font-synthesis -x-lang font-variant-alternates font-variant-east-asian font-variant-ligatures font-variant-numeric font-language-override""" %> <%self:impl_trait style_struct_name="Font" skip_longhands="${skip_font_longhands}" skip_additionals="*"> pub fn set_font_family(&mut self, v: longhands::font_family::computed_value::T) { use properties::longhands::font_family::computed_value::FontFamily; use gecko_bindings::structs::FontFamilyType; let list = &mut self.gecko.mFont.fontlist; unsafe { Gecko_FontFamilyList_Clear(list); } for family in &v.0 { match *family { FontFamily::FamilyName(ref f) => { unsafe { Gecko_FontFamilyList_AppendNamed(list, f.name.as_ptr(), f.quoted); } } FontFamily::Generic(ref name) => { let (family_type, generic) = if name == &atom!("serif") { (FontFamilyType::eFamily_serif, structs::kGenericFont_serif) } else if name == &atom!("sans-serif") { (FontFamilyType::eFamily_sans_serif, structs::kGenericFont_sans_serif) } else if name == &atom!("cursive") { (FontFamilyType::eFamily_cursive, structs::kGenericFont_cursive) } else if name == &atom!("fantasy") { (FontFamilyType::eFamily_fantasy, structs::kGenericFont_fantasy) } else if name == &atom!("monospace") { (FontFamilyType::eFamily_monospace, structs::kGenericFont_monospace) } else if name == &atom!("-moz-fixed") { (FontFamilyType::eFamily_moz_fixed, structs::kGenericFont_moz_fixed) } else { panic!("Unknown generic font family") }; if v.0.len() == 1 { self.gecko.mGenericID = generic; } unsafe { Gecko_FontFamilyList_AppendGeneric(list, family_type); } } } } } pub fn font_family_count(&self) -> usize { 0 } pub fn font_family_at(&self, _: usize) -> longhands::font_family::computed_value::FontFamily { unimplemented!() } pub fn copy_font_family_from(&mut self, other: &Self) { unsafe { Gecko_CopyFontFamilyFrom(&mut self.gecko.mFont, &other.gecko.mFont); } } // FIXME(bholley): Gecko has two different sizes, one of which (mSize) is the // actual computed size, and the other of which (mFont.size) is the 'display // size' which takes font zooming into account. We don't handle font zooming yet. pub fn set_font_size(&mut self, v: longhands::font_size::computed_value::T) { self.gecko.mFont.size = v.0; self.gecko.mSize = v.0; self.gecko.mScriptUnconstrainedSize = v.0; } /// Set font size, taking into account scriptminsize and scriptlevel /// Returns Some(size) if we have to recompute the script unconstrained size pub fn apply_font_size(&mut self, v: longhands::font_size::computed_value::T, parent: &Self) -> Option { let (adjusted_size, adjusted_unconstrained_size) = self.calculate_script_level_size(parent); // In this case, we have been unaffected by scriptminsize, ignore it if parent.gecko.mSize == parent.gecko.mScriptUnconstrainedSize && adjusted_size == adjusted_unconstrained_size { self.set_font_size(v); None } else { self.gecko.mFont.size = v.0; self.gecko.mSize = v.0; Some(Au(parent.gecko.mScriptUnconstrainedSize)) } } pub fn apply_unconstrained_font_size(&mut self, v: Au) { self.gecko.mScriptUnconstrainedSize = v.0; } /// Calculates the constrained and unconstrained font sizes to be inherited /// from the parent. /// /// See ComputeScriptLevelSize in Gecko's nsRuleNode.cpp /// /// scriptlevel is a property that affects how font-size is inherited. If scriptlevel is /// +1, for example, it will inherit as the script size multiplier times /// the parent font. This does not affect cases where the font-size is /// explicitly set. /// /// However, this transformation is not allowed to reduce the size below /// scriptminsize. If this inheritance will reduce it to below /// scriptminsize, it will be set to scriptminsize or the parent size, /// whichever is smaller (the parent size could be smaller than the min size /// because it was explicitly specified). /// /// Now, within a node that has inherited a font-size which was /// crossing scriptminsize once the scriptlevel was applied, a negative /// scriptlevel may be used to increase the size again. /// /// This should work, however if we have already been capped by the /// scriptminsize multiple times, this can lead to a jump in the size. /// /// For example, if we have text of the form: /// /// huge large medium small tiny reallytiny tiny small medium huge /// /// which is represented by progressive nesting and scriptlevel values of /// +1 till the center after which the scriptlevel is -1, the "tiny"s should /// be the same size, as should be the "small"s and "medium"s, etc. /// /// However, if scriptminsize kicked it at around "medium", then /// medium/tiny/reallytiny will all be the same size (the min size). /// A -1 scriptlevel change after this will increase the min size by the /// multiplier, making the second tiny larger than medium. /// /// Instead, we wish for the second "tiny" to still be capped by the script /// level, and when we reach the second "large", it should be the same size /// as the original one. /// /// We do this by cascading two separate font sizes. The font size (mSize) /// is the actual displayed font size. The unconstrained font size /// (mScriptUnconstrainedSize) is the font size in the situation where /// scriptminsize never applied. /// /// We calculate the proposed inherited font size based on scriptlevel and /// the parent unconstrained size, instead of using the parent font size. /// This is stored in the node's unconstrained size and will also be stored /// in the font size provided that it is above the min size. /// /// All of this only applies when inheriting. When the font size is /// manually set, scriptminsize does not apply, and both the real and /// unconstrained size are set to the explicit value. However, if the font /// size is manually set to an em or percent unit, the unconstrained size /// will be set to the value of that unit computed against the parent /// unconstrained size, whereas the font size will be set computing against /// the parent font size. pub fn calculate_script_level_size(&self, parent: &Self) -> (Au, Au) { use std::cmp; let delta = self.gecko.mScriptLevel - parent.gecko.mScriptLevel; let parent_size = Au(parent.gecko.mSize); let parent_unconstrained_size = Au(parent.gecko.mScriptUnconstrainedSize); if delta == 0 { return (parent_size, parent_unconstrained_size) } /// XXXManishearth this should also handle text zoom let min = Au(parent.gecko.mScriptMinSize); let scale = (parent.gecko.mScriptSizeMultiplier as f32).powi(delta as i32); let new_size = parent_size.scale_by(scale); let new_unconstrained_size = parent_unconstrained_size.scale_by(scale); if scale < 1. { // The parent size can be smaller than scriptminsize, // e.g. if it was specified explicitly. Don't scale // in this case, but we don't want to set it to scriptminsize // either since that will make it larger. if parent_size < min { (parent_size, new_unconstrained_size) } else { (cmp::max(min, new_size), new_unconstrained_size) } } else { // If the new unconstrained size is larger than the min size, // this means we have escaped the grasp of scriptminsize // and can revert to using the unconstrained size. // However, if the new size is even larger (perhaps due to usage // of em units), use that instead. (cmp::min(new_size, cmp::max(new_unconstrained_size, min)), new_unconstrained_size) } } /// This function will also handle scriptminsize and scriptlevel /// so should not be called when you just want the font sizes to be copied. /// Hence the different name. pub fn inherit_font_size_from(&mut self, parent: &Self, kw_inherited_size: Option) { let (adjusted_size, adjusted_unconstrained_size) = self.calculate_script_level_size(parent); if adjusted_size.0 != parent.gecko.mSize || adjusted_unconstrained_size.0 != parent.gecko.mScriptUnconstrainedSize { // This is incorrect. When there is both a keyword size being inherited // and a scriptlevel change, we must handle the keyword size the same // way we handle em units. This complicates things because we now have // to keep track of the adjusted and unadjusted ratios in the kw font size. // This only affects the use case of a generic font being used in MathML. // // If we were to fix this I would prefer doing it by removing the // ruletree walk on the Gecko side in nsRuleNode::SetGenericFont // and instead using extra bookkeeping in the mSize and mScriptUnconstrainedSize // values, and reusing those instead of font_size_keyword. // In the case that MathML has given us an adjusted size, apply it. // Keep track of the unconstrained adjusted size. self.gecko.mFont.size = adjusted_size.0; self.gecko.mSize = adjusted_size.0; self.gecko.mScriptUnconstrainedSize = adjusted_unconstrained_size.0; } else if let Some(size) = kw_inherited_size { // Parent element was a keyword-derived size. self.gecko.mFont.size = size.0; self.gecko.mSize = size.0; // MathML constraints didn't apply here, so we can ignore this. self.gecko.mScriptUnconstrainedSize = size.0; } else { // MathML isn't affecting us, and our parent element does not // have a keyword-derived size. Set things normally. self.gecko.mFont.size = parent.gecko.mFont.size; self.gecko.mSize = parent.gecko.mSize; self.gecko.mScriptUnconstrainedSize = parent.gecko.mScriptUnconstrainedSize; } } pub fn clone_font_size(&self) -> longhands::font_size::computed_value::T { Au(self.gecko.mSize) } pub fn set_font_weight(&mut self, v: longhands::font_weight::computed_value::T) { self.gecko.mFont.weight = v as u16; } ${impl_simple_copy('font_weight', 'mFont.weight')} pub fn clone_font_weight(&self) -> longhands::font_weight::computed_value::T { debug_assert!(self.gecko.mFont.weight >= 100); debug_assert!(self.gecko.mFont.weight <= 900); debug_assert!(self.gecko.mFont.weight % 10 == 0); unsafe { transmute(self.gecko.mFont.weight) } } pub fn set_font_synthesis(&mut self, v: longhands::font_synthesis::computed_value::T) { use gecko_bindings::structs::{NS_FONT_SYNTHESIS_WEIGHT, NS_FONT_SYNTHESIS_STYLE}; self.gecko.mFont.synthesis = 0; if v.weight { self.gecko.mFont.synthesis |= NS_FONT_SYNTHESIS_WEIGHT as u8; } if v.style { self.gecko.mFont.synthesis |= NS_FONT_SYNTHESIS_STYLE as u8; } } pub fn copy_font_synthesis_from(&mut self, other: &Self) { self.gecko.mFont.synthesis = other.gecko.mFont.synthesis; } pub fn set_font_size_adjust(&mut self, v: longhands::font_size_adjust::computed_value::T) { use properties::longhands::font_size_adjust::computed_value::T; match v { T::None => self.gecko.mFont.sizeAdjust = -1.0 as f32, T::Number(n) => self.gecko.mFont.sizeAdjust = n, } } pub fn copy_font_size_adjust_from(&mut self, other: &Self) { self.gecko.mFont.sizeAdjust = other.gecko.mFont.sizeAdjust; } pub fn clone_font_size_adjust(&self) -> longhands::font_size_adjust::computed_value::T { use properties::longhands::font_size_adjust::computed_value::T; match self.gecko.mFont.sizeAdjust { -1.0 => T::None, _ => T::Number(self.gecko.mFont.sizeAdjust), } } #[allow(non_snake_case)] pub fn set__x_lang(&mut self, v: longhands::_x_lang::computed_value::T) { let ptr = v.0.as_ptr(); forget(v); unsafe { Gecko_nsStyleFont_SetLang(&mut self.gecko, ptr); } } #[allow(non_snake_case)] pub fn copy__x_lang_from(&mut self, other: &Self) { unsafe { Gecko_nsStyleFont_CopyLangFrom(&mut self.gecko, &other.gecko); } } pub fn set_font_language_override(&mut self, v: longhands::font_language_override::computed_value::T) { self.gecko.mFont.languageOverride = v.0; } ${impl_simple_copy('font_language_override', 'mFont.languageOverride')} pub fn set_font_variant_alternates(&mut self, v: longhands::font_variant_alternates::computed_value::T) { self.gecko.mFont.variantAlternates = v.to_gecko_keyword() } #[allow(non_snake_case)] pub fn copy_font_variant_alternates_from(&mut self, other: &Self) { self.gecko.mFont.variantAlternates = other.gecko.mFont.variantAlternates; // FIXME: Copy alternateValues as well. // self.gecko.mFont.alternateValues = other.gecko.mFont.alternateValues; } pub fn set_font_variant_ligatures(&mut self, v: longhands::font_variant_ligatures::computed_value::T) { self.gecko.mFont.variantLigatures = v.to_gecko_keyword() } ${impl_simple_copy('font_variant_ligatures', 'mFont.variantLigatures')} pub fn set_font_variant_east_asian(&mut self, v: longhands::font_variant_east_asian::computed_value::T) { self.gecko.mFont.variantEastAsian = v.to_gecko_keyword() } ${impl_simple_copy('font_variant_east_asian', 'mFont.variantEastAsian')} pub fn set_font_variant_numeric(&mut self, v: longhands::font_variant_numeric::computed_value::T) { self.gecko.mFont.variantNumeric = v.to_gecko_keyword() } ${impl_simple_copy('font_variant_numeric', 'mFont.variantNumeric')} <%def name="impl_copy_animation_or_transition_value(type, ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn copy_${type}_${ident}_from(&mut self, other: &Self) { unsafe { self.gecko.m${type.capitalize()}s.ensure_len(other.gecko.m${type.capitalize()}s.len()) }; let count = other.gecko.m${type.capitalize()}${gecko_ffi_name}Count; self.gecko.m${type.capitalize()}${gecko_ffi_name}Count = count; // The length of mTransitions or mAnimations is often greater than m{Transition|Animation}XXCount, // don't copy values over the count. for (index, gecko) in self.gecko.m${type.capitalize()}s.iter_mut().enumerate().take(count as usize) { gecko.m${gecko_ffi_name} = other.gecko.m${type.capitalize()}s[index].m${gecko_ffi_name}; } } <%def name="impl_animation_or_transition_count(type, ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn ${type}_${ident}_count(&self) -> usize { self.gecko.m${type.capitalize()}${gecko_ffi_name}Count as usize } <%def name="impl_animation_or_transition_time_value(type, ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn set_${type}_${ident}(&mut self, v: longhands::${type}_${ident}::computed_value::T) { debug_assert!(!v.0.is_empty()); let input_len = v.0.len(); unsafe { self.gecko.m${type.capitalize()}s.ensure_len(input_len) }; self.gecko.m${type.capitalize()}${gecko_ffi_name}Count = input_len as u32; for (i, gecko) in self.gecko.m${type.capitalize()}s.iter_mut().enumerate() { gecko.m${gecko_ffi_name} = v.0[i % input_len].seconds() * 1000.; } } #[allow(non_snake_case)] pub fn ${type}_${ident}_at(&self, index: usize) -> longhands::${type}_${ident}::computed_value::SingleComputedValue { use values::computed::Time; Time::from_seconds(self.gecko.m${type.capitalize()}s[index].m${gecko_ffi_name} / 1000.) } ${impl_animation_or_transition_count(type, ident, gecko_ffi_name)} ${impl_copy_animation_or_transition_value(type, ident, gecko_ffi_name)} <%def name="impl_animation_or_transition_timing_function(type)"> pub fn set_${type}_timing_function(&mut self, v: longhands::${type}_timing_function::computed_value::T) { debug_assert!(!v.0.is_empty()); let input_len = v.0.len(); unsafe { self.gecko.m${type.capitalize()}s.ensure_len(input_len) }; self.gecko.m${type.capitalize()}TimingFunctionCount = input_len as u32; for (i, gecko) in self.gecko.m${type.capitalize()}s.iter_mut().enumerate() { gecko.mTimingFunction = v.0[i % input_len].into(); } } ${impl_animation_or_transition_count(type, 'timing_function', 'TimingFunction')} ${impl_copy_animation_or_transition_value(type, 'timing_function', 'TimingFunction')} pub fn ${type}_timing_function_at(&self, index: usize) -> longhands::${type}_timing_function::computed_value::SingleComputedValue { self.gecko.m${type.capitalize()}s[index].mTimingFunction.into() } <%def name="impl_transition_time_value(ident, gecko_ffi_name)"> ${impl_animation_or_transition_time_value('transition', ident, gecko_ffi_name)} <%def name="impl_transition_count(ident, gecko_ffi_name)"> ${impl_animation_or_transition_count('transition', ident, gecko_ffi_name)} <%def name="impl_copy_animation_value(ident, gecko_ffi_name)"> ${impl_copy_animation_or_transition_value('animation', ident, gecko_ffi_name)} <%def name="impl_transition_timing_function()"> ${impl_animation_or_transition_timing_function('transition')} <%def name="impl_animation_count(ident, gecko_ffi_name)"> ${impl_animation_or_transition_count('animation', ident, gecko_ffi_name)} <%def name="impl_animation_time_value(ident, gecko_ffi_name)"> ${impl_animation_or_transition_time_value('animation', ident, gecko_ffi_name)} <%def name="impl_animation_timing_function()"> ${impl_animation_or_transition_timing_function('animation')} <%def name="impl_animation_keyword(ident, gecko_ffi_name, keyword, cast_type='u8')"> #[allow(non_snake_case)] pub fn set_animation_${ident}(&mut self, v: longhands::animation_${ident}::computed_value::T) { use properties::longhands::animation_${ident}::single_value::computed_value::T as Keyword; use gecko_bindings::structs; debug_assert!(!v.0.is_empty()); let input_len = v.0.len(); unsafe { self.gecko.mAnimations.ensure_len(input_len) }; self.gecko.mAnimation${gecko_ffi_name}Count = input_len as u32; for (i, gecko) in self.gecko.mAnimations.iter_mut().enumerate() { let result = match v.0[i % input_len] { % for value in keyword.gecko_values(): Keyword::${to_rust_ident(value)} => structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast(cast_type)}, % endfor }; gecko.m${gecko_ffi_name} = result; } } #[allow(non_snake_case)] pub fn animation_${ident}_at(&self, index: usize) -> longhands::animation_${ident}::computed_value::SingleComputedValue { use properties::longhands::animation_${ident}::single_value::computed_value::T as Keyword; match self.gecko.mAnimations[index].m${gecko_ffi_name} ${keyword.maybe_cast("u32")} { % for value in keyword.gecko_values(): structs::${keyword.gecko_constant(value)} => Keyword::${to_rust_ident(value)}, % endfor x => panic!("Found unexpected value for animation-${ident}: {:?}", x), } } ${impl_animation_count(ident, gecko_ffi_name)} ${impl_copy_animation_value(ident, gecko_ffi_name)} <% skip_box_longhands= """display overflow-y vertical-align animation-name animation-delay animation-duration animation-direction animation-fill-mode animation-play-state animation-iteration-count animation-timing-function transition-duration transition-delay transition-timing-function transition-property 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 -moz-binding will-change shape-outside contain""" %> <%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}"> // We manually-implement the |display| property until we get general // infrastructure for preffing certain values. <% display_keyword = Keyword("display", "inline block inline-block table inline-table table-row-group " + "table-header-group table-footer-group table-row table-column-group " + "table-column table-cell table-caption list-item flex none " + "inline-flex grid inline-grid ruby ruby-base ruby-base-container " + "ruby-text ruby-text-container contents flow-root -webkit-box " + "-webkit-inline-box -moz-box -moz-inline-box -moz-grid -moz-inline-grid " + "-moz-grid-group -moz-grid-line -moz-stack -moz-inline-stack -moz-deck " + "-moz-popup -moz-groupbox", gecko_enum_prefix="StyleDisplay", gecko_strip_moz_prefix=False) %> pub fn set_display(&mut self, v: longhands::display::computed_value::T) { use properties::longhands::display::computed_value::T as Keyword; // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts let result = match v { % for value in display_keyword.values_for('gecko'): Keyword::${to_rust_ident(value)} => structs::${display_keyword.gecko_constant(value)}, % endfor }; self.gecko.mDisplay = result; self.gecko.mOriginalDisplay = result; } /// Set the display value from the style adjustment code. This is pretty /// much like set_display, but without touching the mOriginalDisplay field, /// which we want to keep. pub fn set_adjusted_display(&mut self, v: longhands::display::computed_value::T, _is_item_or_root: bool) { use properties::longhands::display::computed_value::T as Keyword; let result = match v { % for value in display_keyword.values_for('gecko'): Keyword::${to_rust_ident(value)} => structs::${display_keyword.gecko_constant(value)}, % endfor }; self.gecko.mDisplay = result; } pub fn copy_display_from(&mut self, other: &Self) { self.gecko.mDisplay = other.gecko.mDisplay; self.gecko.mOriginalDisplay = other.gecko.mDisplay; } <%call expr="impl_keyword_clone('display', 'mDisplay', display_keyword)"> <% overflow_x = data.longhands_by_name["overflow-x"] %> pub fn set_overflow_y(&mut self, v: longhands::overflow_y::computed_value::T) { use properties::longhands::overflow_x::computed_value::T as BaseType; // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts self.gecko.mOverflowY = match v { % for value in overflow_x.keyword.values_for('gecko'): BaseType::${to_rust_ident(value)} => structs::${overflow_x.keyword.gecko_constant(value)} as u8, % endfor }; } ${impl_simple_copy('overflow_y', 'mOverflowY')} pub fn clone_overflow_y(&self) -> longhands::overflow_y::computed_value::T { use properties::longhands::overflow_x::computed_value::T as BaseType; // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts match self.gecko.mOverflowY as u32 { % for value in overflow_x.keyword.values_for('gecko'): structs::${overflow_x.keyword.gecko_constant(value)} => BaseType::${to_rust_ident(value)}, % endfor x => panic!("Found unexpected value in style struct for overflow_y property: {}", x), } } pub fn set_vertical_align(&mut self, v: longhands::vertical_align::computed_value::T) { <% keyword = data.longhands_by_name["vertical-align"].keyword %> use properties::longhands::vertical_align::computed_value::T; // FIXME: Align binary representations and ditch |match| for cast + static_asserts match v { % for value in keyword.values_for('gecko'): T::${to_rust_ident(value)} => self.gecko.mVerticalAlign.set_value( CoordDataValue::Enumerated(structs::${keyword.gecko_constant(value)})), % endfor T::LengthOrPercentage(v) => self.gecko.mVerticalAlign.set(v), } } pub fn clone_vertical_align(&self) -> longhands::vertical_align::computed_value::T { use properties::longhands::vertical_align::computed_value::T; use values::computed::LengthOrPercentage; match self.gecko.mVerticalAlign.as_value() { % for value in keyword.values_for('gecko'): CoordDataValue::Enumerated(structs::${keyword.gecko_constant(value)}) => T::${to_rust_ident(value)}, % endfor CoordDataValue::Enumerated(_) => panic!("Unexpected enum variant for vertical-align"), _ => { let v = LengthOrPercentage::from_gecko_style_coord(&self.gecko.mVerticalAlign) .expect("Expected length or percentage for vertical-align"); T::LengthOrPercentage(v) } } } <%call expr="impl_coord_copy('vertical_align', 'mVerticalAlign')"> // Temp fix for Bugzilla bug 24000. // Map 'auto' and 'avoid' to false, and 'always', 'left', and 'right' to true. // "A conforming user agent may interpret the values 'left' and 'right' // as 'always'." - CSS2.1, section 13.3.1 pub fn set_page_break_before(&mut self, v: longhands::page_break_before::computed_value::T) { use computed_values::page_break_before::T; let result = match v { T::auto => false, T::always => true, T::avoid => false, T::left => true, T::right => true }; self.gecko.mBreakBefore = result; } ${impl_simple_copy('page_break_before', 'mBreakBefore')} // Temp fix for Bugzilla bug 24000. // See set_page_break_before for detail. pub fn set_page_break_after(&mut self, v: longhands::page_break_after::computed_value::T) { use computed_values::page_break_after::T; let result = match v { T::auto => false, T::always => true, T::avoid => false, T::left => true, T::right => true }; self.gecko.mBreakAfter = result; } ${impl_simple_copy('page_break_after', 'mBreakAfter')} pub fn set_scroll_snap_points_x(&mut self, v: longhands::scroll_snap_points_x::computed_value::T) { match v.0 { None => self.gecko.mScrollSnapPointsX.set_value(CoordDataValue::None), Some(l) => l.to_gecko_style_coord(&mut self.gecko.mScrollSnapPointsX), }; } ${impl_coord_copy('scroll_snap_points_x', 'mScrollSnapPointsX')} pub fn set_scroll_snap_points_y(&mut self, v: longhands::scroll_snap_points_y::computed_value::T) { match v.0 { None => self.gecko.mScrollSnapPointsY.set_value(CoordDataValue::None), Some(l) => l.to_gecko_style_coord(&mut self.gecko.mScrollSnapPointsY), }; } ${impl_coord_copy('scroll_snap_points_y', 'mScrollSnapPointsY')} pub fn set_scroll_snap_coordinate(&mut self, v: longhands::scroll_snap_coordinate::computed_value::T) { unsafe { self.gecko.mScrollSnapCoordinate.set_len_pod(v.0.len() as u32); } for (gecko, servo) in self.gecko.mScrollSnapCoordinate .iter_mut() .zip(v.0.iter()) { gecko.mXPosition = servo.horizontal.0.into(); gecko.mYPosition = servo.vertical.0.into(); } } pub fn copy_scroll_snap_coordinate_from(&mut self, other: &Self) { unsafe { self.gecko.mScrollSnapCoordinate .set_len_pod(other.gecko.mScrollSnapCoordinate.len() as u32); } for (this, that) in self.gecko.mScrollSnapCoordinate .iter_mut() .zip(other.gecko.mScrollSnapCoordinate.iter()) { *this = *that; } } pub fn clone_scroll_snap_coordinate(&self) -> longhands::scroll_snap_coordinate::computed_value::T { let vec = self.gecko.mScrollSnapCoordinate.iter().map(|f| f.into()).collect(); longhands::scroll_snap_coordinate::computed_value::T(vec) } ${impl_css_url('_moz_binding', 'mBinding.mPtr')} <%def name="transform_function_arm(name, keyword, items)"> <% pattern = None if keyword == "matrix3d": # m11: number1, m12: number2, .. single_patterns = ["m%s: %s" % (str(a / 4 + 1) + str(a % 4 + 1), b + str(a + 1)) for (a, b) in enumerate(items)] if name == "Matrix": pattern = "ComputedMatrix { %s }" % ", ".join(single_patterns) else: pattern = "ComputedMatrixWithPercents { %s }" % ", ".join(single_patterns) else: # Generate contents of pattern from items pattern = ", ".join([b + str(a+1) for (a,b) in enumerate(items)]) # First %s substituted with the call to GetArrayItem, the second # %s substituted with the corresponding variable css_value_setters = { "length" : "bindings::Gecko_CSSValue_SetAbsoluteLength(%s, %s.0)", "percentage" : "bindings::Gecko_CSSValue_SetPercentage(%s, %s)", "lop" : "%s.set_lop(%s)", "angle" : "%s.set_angle(%s)", "number" : "bindings::Gecko_CSSValue_SetNumber(%s, %s)", } %> longhands::transform::computed_value::ComputedOperation::${name}(${pattern}) => { bindings::Gecko_CSSValue_SetFunction(gecko_value, ${len(items) + 1}); bindings::Gecko_CSSValue_SetKeyword( bindings::Gecko_CSSValue_GetArrayItem(gecko_value, 0), eCSSKeyword_${keyword} ); % for index, item in enumerate(items): ${css_value_setters[item] % ( "bindings::Gecko_CSSValue_GetArrayItem(gecko_value, %d)" % (index + 1), item + str(index + 1) )}; % endfor } pub fn convert_transform(input: Vec, output: &mut structs::root::RefPtr) { use gecko_bindings::structs::nsCSSKeyword::*; use gecko_bindings::sugar::refptr::RefPtr; use properties::longhands::transform::computed_value::ComputedMatrix; use properties::longhands::transform::computed_value::ComputedMatrixWithPercents; unsafe { output.clear() }; let list = unsafe { RefPtr::from_addrefed(bindings::Gecko_NewCSSValueSharedList(input.len() as u32)) }; let mut cur = list.mHead; let mut iter = input.into_iter(); while !cur.is_null() { let gecko_value = unsafe { &mut (*cur).mValue }; let servo = iter.next().expect("Gecko_NewCSSValueSharedList should create a shared \ value list of the same length as the transform vector"); unsafe { match servo { ${transform_function_arm("Matrix", "matrix3d", ["number"] * 16)} ${transform_function_arm("MatrixWithPercents", "matrix3d", ["number"] * 12 + ["lop"] * 2 + ["length"] + ["number"])} ${transform_function_arm("Skew", "skew", ["angle"] * 2)} ${transform_function_arm("Translate", "translate3d", ["lop", "lop", "length"])} ${transform_function_arm("Scale", "scale3d", ["number"] * 3)} ${transform_function_arm("Rotate", "rotate3d", ["number"] * 3 + ["angle"])} ${transform_function_arm("Perspective", "perspective", ["length"])} } cur = (*cur).mNext; } } debug_assert!(iter.next().is_none()); unsafe { output.set_move(list) }; } pub fn set_transform(&mut self, other: longhands::transform::computed_value::T) { let vec = if let Some(v) = other.0 { v } else { unsafe { self.gecko.mSpecifiedTransform.clear(); } return; }; Self::convert_transform(vec, &mut self.gecko.mSpecifiedTransform); } pub fn copy_transform_from(&mut self, other: &Self) { unsafe { self.gecko.mSpecifiedTransform.set(&other.gecko.mSpecifiedTransform); } } <%def name="computed_operation_arm(name, keyword, items)"> <% # %s is substituted with the call to GetArrayItem. css_value_getters = { "length" : "Au(bindings::Gecko_CSSValue_GetAbsoluteLength(%s))", "lop" : "%s.get_lop()", "angle" : "%s.get_angle()", "number" : "bindings::Gecko_CSSValue_GetNumber(%s)", } %> eCSSKeyword_${keyword} => { ComputedOperation::${name}( % if keyword == "matrix3d": ComputedMatrix { % endif % for index, item in enumerate(items): % if keyword == "matrix3d": m${index / 4 + 1}${index % 4 + 1}: % endif ${css_value_getters[item] % ( "bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, %d)" % (index + 1) )}, % endfor % if keyword == "matrix3d": } % endif ) }, pub fn clone_transform(&self) -> longhands::transform::computed_value::T { use app_units::Au; use gecko_bindings::structs::nsCSSKeyword::*; use properties::longhands::transform::computed_value; use properties::longhands::transform::computed_value::ComputedMatrix; use properties::longhands::transform::computed_value::ComputedOperation; if self.gecko.mSpecifiedTransform.mRawPtr.is_null() { return computed_value::T(None); } let mut result = vec![]; let mut cur = unsafe { (*self.gecko.mSpecifiedTransform.to_safe().get()).mHead }; while !cur.is_null() { let gecko_value = unsafe { &(*cur).mValue }; let transform_function = unsafe { bindings::Gecko_CSSValue_GetKeyword(bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 0)) }; let servo = unsafe { match transform_function { ${computed_operation_arm("Matrix", "matrix3d", ["number"] * 16)} ${computed_operation_arm("Skew", "skew", ["angle"] * 2)} ${computed_operation_arm("Translate", "translate3d", ["lop", "lop", "length"])} ${computed_operation_arm("Scale", "scale3d", ["number"] * 3)} ${computed_operation_arm("Rotate", "rotate3d", ["number"] * 3 + ["angle"])} ${computed_operation_arm("Perspective", "perspective", ["length"])} _ => panic!("We shouldn't set any other transform function types"), } }; result.push(servo); unsafe { cur = (&*cur).mNext }; } computed_value::T(Some(result)) } ${impl_transition_time_value('delay', 'Delay')} ${impl_transition_time_value('duration', 'Duration')} ${impl_transition_timing_function()} pub fn transition_combined_duration_at(&self, index: usize) -> f32 { // https://drafts.csswg.org/css-transitions/#transition-combined-duration self.gecko.mTransitions[index].mDuration.max(0.0) + self.gecko.mTransitions[index].mDelay } pub fn set_transition_property(&mut self, v: longhands::transition_property::computed_value::T) { use gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_no_properties; if !v.0.is_empty() { unsafe { self.gecko.mTransitions.ensure_len(v.0.len()) }; self.gecko.mTransitionPropertyCount = v.0.len() as u32; for (servo, gecko) in v.0.into_iter().zip(self.gecko.mTransitions.iter_mut()) { match servo { TransitionProperty::Unsupported(ref atom) => unsafe { Gecko_StyleTransition_SetUnsupportedProperty(gecko, atom.as_ptr()) }, _ => gecko.mProperty = (&servo).into(), } } } else { // In gecko |none| is represented by eCSSPropertyExtra_no_properties. self.gecko.mTransitionPropertyCount = 1; self.gecko.mTransitions[0].mProperty = eCSSPropertyExtra_no_properties; } } /// Returns whether there are any transitions specified. pub fn specifies_transitions(&self) -> bool { use gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_all_properties; if self.gecko.mTransitionPropertyCount == 1 && self.gecko.mTransitions[0].mProperty == eCSSPropertyExtra_all_properties && self.gecko.mTransitions[0].mDuration.max(0.0) + self.gecko.mTransitions[0].mDelay <= 0.0f32 { return false; } self.gecko.mTransitionPropertyCount > 0 } pub fn transition_property_at(&self, index: usize) -> longhands::transition_property::computed_value::SingleComputedValue { use gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_no_properties; use gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable; use gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN; let property = self.gecko.mTransitions[index].mProperty; if property == eCSSProperty_UNKNOWN || property == eCSSPropertyExtra_variable { let atom = self.gecko.mTransitions[index].mUnknownProperty.raw(); debug_assert!(!atom.is_null()); TransitionProperty::Unsupported(atom.into()) } else if property == eCSSPropertyExtra_no_properties { // Actually, we don't expect TransitionProperty::Unsupported also represents "none", // but if the caller wants to convert it, it is fine. Please use it carefully. TransitionProperty::Unsupported(atom!("none")) } else { property.into() } } pub fn transition_nscsspropertyid_at(&self, index: usize) -> nsCSSPropertyID { self.gecko.mTransitions[index].mProperty } pub fn copy_transition_property_from(&mut self, other: &Self) { use gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable; use gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN; unsafe { self.gecko.mTransitions.ensure_len(other.gecko.mTransitions.len()) }; let count = other.gecko.mTransitionPropertyCount; self.gecko.mTransitionPropertyCount = count; for (index, transition) in self.gecko.mTransitions.iter_mut().enumerate().take(count as usize) { transition.mProperty = other.gecko.mTransitions[index].mProperty; if transition.mProperty == eCSSProperty_UNKNOWN || transition.mProperty == eCSSPropertyExtra_variable { let atom = other.gecko.mTransitions[index].mUnknownProperty.raw(); debug_assert!(!atom.is_null()); unsafe { Gecko_StyleTransition_SetUnsupportedProperty(transition, atom) }; } } } ${impl_transition_count('property', 'Property')} pub fn animations_equals(&self, other: &Self) -> bool { unsafe { bindings::Gecko_StyleAnimationsEquals(&self.gecko.mAnimations, &other.gecko.mAnimations) } } pub fn set_animation_name(&mut self, v: longhands::animation_name::computed_value::T) { debug_assert!(!v.0.is_empty()); unsafe { self.gecko.mAnimations.ensure_len(v.0.len()) }; self.gecko.mAnimationNameCount = v.0.len() as u32; for (servo, gecko) in v.0.into_iter().zip(self.gecko.mAnimations.iter_mut()) { // TODO This is inefficient. We should fix this in bug 1329169. gecko.mName.assign(match servo.0 { Some(ref name) => name.as_atom().as_slice(), None => &[], // Empty string for 'none' }); } } pub fn animation_name_at(&self, index: usize) -> longhands::animation_name::computed_value::SingleComputedValue { use properties::longhands::animation_name::single_value::SpecifiedValue as AnimationName; // XXX: Is there any effective ways? let atom = &self.gecko.mAnimations[index].mName; if atom.is_empty() { AnimationName(None) } else { AnimationName(Some(KeyframesName::from_ident(atom.to_string()))) } } pub fn copy_animation_name_from(&mut self, other: &Self) { unsafe { self.gecko.mAnimations.ensure_len(other.gecko.mAnimations.len()) }; let count = other.gecko.mAnimationNameCount; self.gecko.mAnimationNameCount = count; // The length of mAnimations is often greater than mAnimationXXCount, // don't copy values over the count. for (index, animation) in self.gecko.mAnimations.iter_mut().enumerate().take(count as usize) { animation.mName.assign(&*other.gecko.mAnimations[index].mName); } } ${impl_animation_count('name', 'Name')} ${impl_animation_time_value('delay', 'Delay')} ${impl_animation_time_value('duration', 'Duration')} ${impl_animation_keyword('direction', 'Direction', data.longhands_by_name["animation-direction"].keyword)} ${impl_animation_keyword('fill_mode', 'FillMode', data.longhands_by_name["animation-fill-mode"].keyword)} ${impl_animation_keyword('play_state', 'PlayState', data.longhands_by_name["animation-play-state"].keyword)} pub fn set_animation_iteration_count(&mut self, v: longhands::animation_iteration_count::computed_value::T) { use std::f32; use properties::longhands::animation_iteration_count::single_value::SpecifiedValue as AnimationIterationCount; debug_assert!(!v.0.is_empty()); let input_len = v.0.len(); unsafe { self.gecko.mAnimations.ensure_len(input_len) }; self.gecko.mAnimationIterationCountCount = input_len as u32; for (i, gecko) in self.gecko.mAnimations.iter_mut().enumerate() { match v.0[i % input_len] { AnimationIterationCount::Number(n) => gecko.mIterationCount = n, AnimationIterationCount::Infinite => gecko.mIterationCount = f32::INFINITY, } } } pub fn animation_iteration_count_at(&self, index: usize) -> longhands::animation_iteration_count::computed_value::SingleComputedValue { use properties::longhands::animation_iteration_count::single_value::computed_value::T as AnimationIterationCount; if self.gecko.mAnimations[index].mIterationCount.is_infinite() { AnimationIterationCount::Infinite } else { AnimationIterationCount::Number(self.gecko.mAnimations[index].mIterationCount) } } ${impl_animation_count('iteration_count', 'IterationCount')} ${impl_copy_animation_value('iteration_count', 'IterationCount')} ${impl_animation_timing_function()} <% scroll_snap_type_keyword = Keyword("scroll-snap-type", "none mandatory proximity") %> ${impl_keyword('scroll_snap_type_y', 'mScrollSnapTypeY', scroll_snap_type_keyword, need_clone=False)} pub fn set_perspective_origin(&mut self, v: longhands::perspective_origin::computed_value::T) { self.gecko.mPerspectiveOrigin[0].set(v.horizontal); self.gecko.mPerspectiveOrigin[1].set(v.vertical); } pub fn copy_perspective_origin_from(&mut self, other: &Self) { self.gecko.mPerspectiveOrigin[0].copy_from(&other.gecko.mPerspectiveOrigin[0]); self.gecko.mPerspectiveOrigin[1].copy_from(&other.gecko.mPerspectiveOrigin[1]); } pub fn clone_perspective_origin(&self) -> longhands::perspective_origin::computed_value::T { use properties::longhands::perspective_origin::computed_value::T; use values::computed::LengthOrPercentage; T { horizontal: LengthOrPercentage::from_gecko_style_coord(&self.gecko.mPerspectiveOrigin[0]) .expect("Expected length or percentage for horizontal value of perspective-origin"), vertical: LengthOrPercentage::from_gecko_style_coord(&self.gecko.mPerspectiveOrigin[1]) .expect("Expected length or percentage for vertical value of perspective-origin"), } } pub fn set_transform_origin(&mut self, v: longhands::transform_origin::computed_value::T) { self.gecko.mTransformOrigin[0].set(v.horizontal); self.gecko.mTransformOrigin[1].set(v.vertical); self.gecko.mTransformOrigin[2].set(v.depth); } pub fn copy_transform_origin_from(&mut self, other: &Self) { self.gecko.mTransformOrigin[0].copy_from(&other.gecko.mTransformOrigin[0]); self.gecko.mTransformOrigin[1].copy_from(&other.gecko.mTransformOrigin[1]); self.gecko.mTransformOrigin[2].copy_from(&other.gecko.mTransformOrigin[2]); } pub fn clone_transform_origin(&self) -> longhands::transform_origin::computed_value::T { use properties::longhands::transform_origin::computed_value::T; use values::computed::LengthOrPercentage; T { horizontal: LengthOrPercentage::from_gecko_style_coord(&self.gecko.mTransformOrigin[0]) .expect("clone for LengthOrPercentage failed"), vertical: LengthOrPercentage::from_gecko_style_coord(&self.gecko.mTransformOrigin[1]) .expect("clone for LengthOrPercentage failed"), depth: Au::from_gecko_style_coord(&self.gecko.mTransformOrigin[2]) .expect("clone for Length failed"), } } pub fn set_will_change(&mut self, v: longhands::will_change::computed_value::T) { use gecko_bindings::bindings::{Gecko_AppendWillChange, Gecko_ClearWillChange}; use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_OPACITY; use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_SCROLL; use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_TRANSFORM; use properties::PropertyId; use properties::longhands::will_change::computed_value::T; fn will_change_bitfield_from_prop_flags(prop: &LonghandId) -> u8 { use properties::{ABSPOS_CB, CREATES_STACKING_CONTEXT, FIXPOS_CB}; use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_ABSPOS_CB; use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_FIXPOS_CB; use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_STACKING_CONTEXT; let servo_flags = prop.flags(); let mut bitfield = 0; if servo_flags.contains(CREATES_STACKING_CONTEXT) { bitfield |= NS_STYLE_WILL_CHANGE_STACKING_CONTEXT; } if servo_flags.contains(FIXPOS_CB) { bitfield |= NS_STYLE_WILL_CHANGE_FIXPOS_CB; } if servo_flags.contains(ABSPOS_CB) { bitfield |= NS_STYLE_WILL_CHANGE_ABSPOS_CB; } bitfield as u8 } self.gecko.mWillChangeBitField = 0; match v { T::AnimateableFeatures(features) => { unsafe { Gecko_ClearWillChange(&mut self.gecko, features.len()); } for feature in features.iter() { if feature == &atom!("scroll-position") { self.gecko.mWillChangeBitField |= NS_STYLE_WILL_CHANGE_SCROLL as u8; } else if feature == &atom!("opacity") { self.gecko.mWillChangeBitField |= NS_STYLE_WILL_CHANGE_OPACITY as u8; } else if feature == &atom!("transform") { self.gecko.mWillChangeBitField |= NS_STYLE_WILL_CHANGE_TRANSFORM as u8; } unsafe { Gecko_AppendWillChange(&mut self.gecko, feature.as_ptr()); } if let Ok(prop_id) = PropertyId::parse(feature.to_string().into()) { match prop_id.as_shorthand() { Ok(shorthand) => { for longhand in shorthand.longhands() { self.gecko.mWillChangeBitField |= will_change_bitfield_from_prop_flags(longhand); } }, Err(longhand_or_custom) => { if let PropertyDeclarationId::Longhand(longhand) = longhand_or_custom { self.gecko.mWillChangeBitField |= will_change_bitfield_from_prop_flags(&longhand); } }, } } } }, T::Auto => { unsafe { Gecko_ClearWillChange(&mut self.gecko, 0); } }, }; } pub fn copy_will_change_from(&mut self, other: &Self) { use gecko_bindings::bindings::Gecko_CopyWillChangeFrom; self.gecko.mWillChangeBitField = other.gecko.mWillChangeBitField; unsafe { Gecko_CopyWillChangeFrom(&mut self.gecko, &other.gecko as *const _ as *mut _); } } <% impl_shape_source("shape_outside", "mShapeOutside") %> pub fn set_contain(&mut self, v: longhands::contain::computed_value::T) { use gecko_bindings::structs::NS_STYLE_CONTAIN_NONE; use gecko_bindings::structs::NS_STYLE_CONTAIN_STRICT; use gecko_bindings::structs::NS_STYLE_CONTAIN_LAYOUT; use gecko_bindings::structs::NS_STYLE_CONTAIN_STYLE; use gecko_bindings::structs::NS_STYLE_CONTAIN_PAINT; use gecko_bindings::structs::NS_STYLE_CONTAIN_ALL_BITS; use properties::longhands::contain; if v.is_empty() { self.gecko.mContain = NS_STYLE_CONTAIN_NONE as u8; return; } if v.contains(contain::STRICT) { self.gecko.mContain = (NS_STYLE_CONTAIN_STRICT | NS_STYLE_CONTAIN_ALL_BITS) as u8; return; } let mut bitfield = 0; if v.contains(contain::LAYOUT) { bitfield |= NS_STYLE_CONTAIN_LAYOUT; } if v.contains(contain::STYLE) { bitfield |= NS_STYLE_CONTAIN_STYLE; } if v.contains(contain::PAINT) { bitfield |= NS_STYLE_CONTAIN_PAINT; } self.gecko.mContain = bitfield as u8; } pub fn clone_contain(&self) -> longhands::contain::computed_value::T { use gecko_bindings::structs::NS_STYLE_CONTAIN_STRICT; use gecko_bindings::structs::NS_STYLE_CONTAIN_LAYOUT; use gecko_bindings::structs::NS_STYLE_CONTAIN_STYLE; use gecko_bindings::structs::NS_STYLE_CONTAIN_PAINT; use gecko_bindings::structs::NS_STYLE_CONTAIN_ALL_BITS; use properties::longhands::contain; let mut servo_flags = contain::computed_value::T::empty(); let gecko_flags = self.gecko.mContain; if gecko_flags & (NS_STYLE_CONTAIN_STRICT as u8) != 0 && gecko_flags & (NS_STYLE_CONTAIN_ALL_BITS as u8) != 0 { servo_flags.insert(contain::STRICT | contain::STRICT_BITS); return servo_flags; } if gecko_flags & (NS_STYLE_CONTAIN_LAYOUT as u8) != 0 { servo_flags.insert(contain::LAYOUT); } if gecko_flags & (NS_STYLE_CONTAIN_STYLE as u8) != 0{ servo_flags.insert(contain::STYLE); } if gecko_flags & (NS_STYLE_CONTAIN_PAINT as u8) != 0 { servo_flags.insert(contain::PAINT); } return servo_flags; } ${impl_simple_copy("contain", "mContain")} <%def name="simple_image_array_property(name, shorthand, field_name)"> <% image_layers_field = "mImage" if shorthand == "background" else "mMask" %> pub fn copy_${shorthand}_${name}_from(&mut self, other: &Self) { use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; unsafe { Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, other.gecko.${image_layers_field}.mLayers.len(), LayerType::${shorthand.title()}); } for (layer, other) in self.gecko.${image_layers_field}.mLayers.iter_mut() .zip(other.gecko.${image_layers_field}.mLayers.iter()) .take(other.gecko.${image_layers_field} .${field_name}Count as usize) { layer.${field_name} = other.${field_name}; } self.gecko.${image_layers_field}.${field_name}Count = other.gecko.${image_layers_field}.${field_name}Count; } pub fn set_${shorthand}_${name}(&mut self, v: longhands::${shorthand}_${name}::computed_value::T) { use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; unsafe { Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, v.0.len(), LayerType::${shorthand.title()}); } self.gecko.${image_layers_field}.${field_name}Count = v.0.len() as u32; for (servo, geckolayer) in v.0.into_iter() .zip(self.gecko.${image_layers_field}.mLayers.iter_mut()) { geckolayer.${field_name} = { ${caller.body()} }; } } <%def name="impl_common_image_layer_properties(shorthand)"> <% image_layers_field = "mImage" if shorthand == "background" else "mMask" %> <%self:simple_image_array_property name="repeat" shorthand="${shorthand}" field_name="mRepeat"> use properties::longhands::${shorthand}_repeat::single_value::computed_value::RepeatKeyword; use gecko_bindings::structs::nsStyleImageLayers_Repeat; use gecko_bindings::structs::NS_STYLE_IMAGELAYER_REPEAT_REPEAT; use gecko_bindings::structs::NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT; use gecko_bindings::structs::NS_STYLE_IMAGELAYER_REPEAT_SPACE; use gecko_bindings::structs::NS_STYLE_IMAGELAYER_REPEAT_ROUND; fn to_ns(repeat: RepeatKeyword) -> u32 { match repeat { RepeatKeyword::Repeat => NS_STYLE_IMAGELAYER_REPEAT_REPEAT, RepeatKeyword::Space => NS_STYLE_IMAGELAYER_REPEAT_SPACE, RepeatKeyword::Round => NS_STYLE_IMAGELAYER_REPEAT_ROUND, RepeatKeyword::NoRepeat => NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT, } } let repeat_x = to_ns(servo.0); let repeat_y = to_ns(servo.1); nsStyleImageLayers_Repeat { mXRepeat: repeat_x as u8, mYRepeat: repeat_y as u8, } <%self:simple_image_array_property name="clip" shorthand="${shorthand}" field_name="mClip"> use gecko_bindings::structs::StyleGeometryBox; use properties::longhands::${shorthand}_clip::single_value::computed_value::T; match servo { T::border_box => StyleGeometryBox::BorderBox, T::padding_box => StyleGeometryBox::PaddingBox, T::content_box => StyleGeometryBox::ContentBox, % if shorthand == "mask": T::fill_box => StyleGeometryBox::FillBox, T::stroke_box => StyleGeometryBox::StrokeBox, T::view_box => StyleGeometryBox::ViewBox, T::no_clip => StyleGeometryBox::NoClip, % elif shorthand == "background": T::text => StyleGeometryBox::Text, % endif } <%self:simple_image_array_property name="origin" shorthand="${shorthand}" field_name="mOrigin"> use gecko_bindings::structs::StyleGeometryBox; use properties::longhands::${shorthand}_origin::single_value::computed_value::T; match servo { T::border_box => StyleGeometryBox::BorderBox, T::padding_box => StyleGeometryBox::PaddingBox, T::content_box => StyleGeometryBox::ContentBox, % if shorthand == "mask": T::fill_box => StyleGeometryBox::FillBox, T::stroke_box => StyleGeometryBox::StrokeBox, T::view_box => StyleGeometryBox::ViewBox, % endif } % for orientation in [("x", "Horizontal"), ("y", "Vertical")]: pub fn copy_${shorthand}_position_${orientation[0]}_from(&mut self, other: &Self) { use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; self.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count = cmp::min(1, other.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count); self.gecko.${image_layers_field}.mLayers.mFirstElement.mPosition = other.gecko.${image_layers_field}.mLayers.mFirstElement.mPosition; unsafe { Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, other.gecko.${image_layers_field}.mLayers.len(), LayerType::${shorthand.capitalize()}); } for (layer, other) in self.gecko.${image_layers_field}.mLayers.iter_mut() .zip(other.gecko.${image_layers_field}.mLayers.iter()) { layer.mPosition.m${orientation[0].upper()}Position = other.mPosition.m${orientation[0].upper()}Position; } self.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count = other.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count; } pub fn clone_${shorthand}_position_${orientation[0]}(&self) -> longhands::${shorthand}_position_${orientation[0]}::computed_value::T { use values::generics::position::${orientation[1]}Position; longhands::${shorthand}_position_${orientation[0]}::computed_value::T( self.gecko.${image_layers_field}.mLayers.iter() .take(self.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count as usize) .map(|position| ${orientation[1]}Position(position.mPosition.m${orientation[0].upper()}Position.into())) .collect() ) } pub fn set_${shorthand}_position_${orientation[0]}(&mut self, v: longhands::${shorthand}_position_${orientation[0]}::computed_value::T) { use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; unsafe { Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, v.0.len(), LayerType::${shorthand.capitalize()}); } self.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count = v.0.len() as u32; for (servo, geckolayer) in v.0.into_iter().zip(self.gecko.${image_layers_field} .mLayers.iter_mut()) { geckolayer.mPosition.m${orientation[0].upper()}Position = servo.0.into(); } } % endfor <%self:simple_image_array_property name="size" shorthand="${shorthand}" field_name="mSize"> use gecko_bindings::structs::nsStyleImageLayers_Size_Dimension; use gecko_bindings::structs::nsStyleImageLayers_Size_DimensionType; use gecko_bindings::structs::{nsStyleCoord_CalcValue, nsStyleImageLayers_Size}; use properties::longhands::background_size::single_value::computed_value::T; let mut width = nsStyleCoord_CalcValue::new(); let mut height = nsStyleCoord_CalcValue::new(); let (w_type, h_type) = match servo { T::Explicit(size) => { let mut w_type = nsStyleImageLayers_Size_DimensionType::eAuto; let mut h_type = nsStyleImageLayers_Size_DimensionType::eAuto; if let Some(w) = size.width.to_calc_value() { width = w; w_type = nsStyleImageLayers_Size_DimensionType::eLengthPercentage; } if let Some(h) = size.height.to_calc_value() { height = h; h_type = nsStyleImageLayers_Size_DimensionType::eLengthPercentage; } (w_type, h_type) } T::Cover => (nsStyleImageLayers_Size_DimensionType::eCover, nsStyleImageLayers_Size_DimensionType::eCover), T::Contain => (nsStyleImageLayers_Size_DimensionType::eContain, nsStyleImageLayers_Size_DimensionType::eContain), }; nsStyleImageLayers_Size { mWidth: nsStyleImageLayers_Size_Dimension { _base: width }, mHeight: nsStyleImageLayers_Size_Dimension { _base: height }, mWidthType: w_type as u8, mHeightType: h_type as u8, } pub fn clone_${shorthand}_size(&self) -> longhands::background_size::computed_value::T { use gecko_bindings::structs::nsStyleCoord_CalcValue as CalcValue; use gecko_bindings::structs::nsStyleImageLayers_Size_DimensionType as DimensionType; use properties::longhands::background_size::single_value::computed_value::{ExplicitSize, T}; use values::computed::LengthOrPercentageOrAuto; fn to_servo(value: CalcValue, ty: u8) -> LengthOrPercentageOrAuto { if ty == DimensionType::eAuto as u8 { LengthOrPercentageOrAuto::Auto } else { debug_assert!(ty == DimensionType::eLengthPercentage as u8); LengthOrPercentageOrAuto::Calc(value.into()) } } longhands::background_size::computed_value::T( self.gecko.${image_layers_field}.mLayers.iter().map(|ref layer| { if DimensionType::eCover as u8 == layer.mSize.mWidthType { debug_assert!(layer.mSize.mHeightType == DimensionType::eCover as u8); return T::Cover } if DimensionType::eContain as u8 == layer.mSize.mWidthType { debug_assert!(layer.mSize.mHeightType == DimensionType::eContain as u8); return T::Contain } T::Explicit(ExplicitSize { width: to_servo(layer.mSize.mWidth._base, layer.mSize.mWidthType), height: to_servo(layer.mSize.mHeight._base, layer.mSize.mHeightType), }) }).collect() ) } pub fn copy_${shorthand}_image_from(&mut self, other: &Self) { unsafe { Gecko_CopyImageValueFrom(&mut self.gecko.${image_layers_field}.mLayers.mFirstElement.mImage, &other.gecko.${image_layers_field}.mLayers.mFirstElement.mImage); } } #[allow(unused_variables)] pub fn set_${shorthand}_image(&mut self, images: longhands::${shorthand}_image::computed_value::T, cacheable: &mut bool) { use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; unsafe { // Prevent leaking of the last elements we did set for image in &mut self.gecko.${image_layers_field}.mLayers { Gecko_SetNullImageValue(&mut image.mImage) } // XXXManishearth clear mSourceURI for masks Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, images.0.len(), LayerType::${shorthand.title()}); } self.gecko.${image_layers_field}.mImageCount = images.0.len() as u32; for (image, geckoimage) in images.0.into_iter().zip(self.gecko.${image_layers_field} .mLayers.iter_mut()) { % if shorthand == "background": if let Some(image) = image.0 { geckoimage.mImage.set(image, cacheable) } % else: use properties::longhands::mask_image::single_value::computed_value::T; match image { T::Image(image) => geckoimage.mImage.set(image, cacheable), _ => () } % endif } } <% fill_fields = "mRepeat mClip mOrigin mPositionX mPositionY mImage mSize" if shorthand == "background": fill_fields += " mAttachment mBlendMode" else: # mSourceURI uses mImageCount fill_fields += " mMaskMode mComposite" %> pub fn fill_arrays(&mut self) { use gecko_bindings::bindings::Gecko_FillAll${shorthand.title()}Lists; use std::cmp; let mut max_len = 1; % for member in fill_fields.split(): max_len = cmp::max(max_len, self.gecko.${image_layers_field}.${member}Count); % endfor unsafe { // While we could do this manually, we'd need to also manually // run all the copy constructors, so we just delegate to gecko Gecko_FillAll${shorthand.title()}Lists(&mut self.gecko.${image_layers_field}, max_len); } } // TODO: Gecko accepts lists in most background-related properties. We just use // the first element (which is the common case), but at some point we want to // add support for parsing these lists in servo and pushing to nsTArray's. <% skip_background_longhands = """background-repeat background-image background-clip background-origin background-attachment background-size background-position background-blend-mode background-position-x background-position-y""" %> <%self:impl_trait style_struct_name="Background" skip_longhands="${skip_background_longhands}" skip_additionals="*"> <% impl_common_image_layer_properties("background") %> <%self:simple_image_array_property name="attachment" shorthand="background" field_name="mAttachment"> use properties::longhands::background_attachment::single_value::computed_value::T; match servo { T::scroll => structs::NS_STYLE_IMAGELAYER_ATTACHMENT_SCROLL as u8, T::fixed => structs::NS_STYLE_IMAGELAYER_ATTACHMENT_FIXED as u8, T::local => structs::NS_STYLE_IMAGELAYER_ATTACHMENT_LOCAL as u8, } <%self:simple_image_array_property name="blend_mode" shorthand="background" field_name="mBlendMode"> use properties::longhands::background_blend_mode::single_value::computed_value::T; match servo { T::normal => structs::NS_STYLE_BLEND_NORMAL as u8, T::multiply => structs::NS_STYLE_BLEND_MULTIPLY as u8, T::screen => structs::NS_STYLE_BLEND_SCREEN as u8, T::overlay => structs::NS_STYLE_BLEND_OVERLAY as u8, T::darken => structs::NS_STYLE_BLEND_DARKEN as u8, T::lighten => structs::NS_STYLE_BLEND_LIGHTEN as u8, T::color_dodge => structs::NS_STYLE_BLEND_COLOR_DODGE as u8, T::color_burn => structs::NS_STYLE_BLEND_COLOR_BURN as u8, T::hard_light => structs::NS_STYLE_BLEND_HARD_LIGHT as u8, T::soft_light => structs::NS_STYLE_BLEND_SOFT_LIGHT as u8, T::difference => structs::NS_STYLE_BLEND_DIFFERENCE as u8, T::exclusion => structs::NS_STYLE_BLEND_EXCLUSION as u8, T::hue => structs::NS_STYLE_BLEND_HUE as u8, T::saturation => structs::NS_STYLE_BLEND_SATURATION as u8, T::color => structs::NS_STYLE_BLEND_COLOR as u8, T::luminosity => structs::NS_STYLE_BLEND_LUMINOSITY as u8, } <%self:impl_trait style_struct_name="List" skip_longhands="list-style-image list-style-type quotes -moz-image-region" skip_additionals="*"> pub fn set_list_style_image(&mut self, image: longhands::list_style_image::computed_value::T) { use values::Either; match image { Either::Second(_none) => { unsafe { Gecko_SetListStyleImageNone(&mut self.gecko); } } Either::First(ref url) => { unsafe { 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 // are re-used from the applicable declaration cache, and the List struct // is an inherited struct. } } } pub fn copy_list_style_image_from(&mut self, other: &Self) { unsafe { Gecko_CopyListStyleImageFrom(&mut self.gecko, &other.gecko); } } pub fn set_list_style_type(&mut self, v: longhands::list_style_type::computed_value::T) { use properties::longhands::list_style_type::computed_value::T as Keyword; <% keyword = data.longhands_by_name["list-style-type"].keyword # The first four are @counter-styles # The rest have special fallback behavior special = """upper-roman lower-roman upper-alpha lower-alpha japanese-informal japanese-formal korean-hangul-formal korean-hanja-informal korean-hanja-formal simp-chinese-informal simp-chinese-formal trad-chinese-informal trad-chinese-formal""".split() %> let result = match v { % for value in keyword.values_for('gecko'): % if value in special: // Special keywords are implemented as @counter-styles // and need to be manually set as strings Keyword::${to_rust_ident(value)} => structs::${keyword.gecko_constant("none")}, % else: Keyword::${to_rust_ident(value)} => structs::${keyword.gecko_constant(value)}, % endif % endfor }; unsafe { Gecko_SetListStyleType(&mut self.gecko, result as u32); } } pub fn copy_list_style_type_from(&mut self, other: &Self) { unsafe { Gecko_CopyListStyleTypeFrom(&mut self.gecko, &other.gecko); } } pub fn set_quotes(&mut self, other: longhands::quotes::computed_value::T) { use gecko_bindings::bindings::Gecko_NewStyleQuoteValues; use gecko_bindings::sugar::refptr::UniqueRefPtr; let mut refptr = unsafe { UniqueRefPtr::from_addrefed(Gecko_NewStyleQuoteValues(other.0.len() as u32)) }; for (servo, gecko) in other.0.into_iter().zip(refptr.mQuotePairs.iter_mut()) { gecko.first.assign_utf8(&servo.0); gecko.second.assign_utf8(&servo.1); } unsafe { self.gecko.mQuotes.set_move(refptr.get()) } } pub fn copy_quotes_from(&mut self, other: &Self) { unsafe { self.gecko.mQuotes.set(&other.gecko.mQuotes); } } #[allow(non_snake_case)] pub fn set__moz_image_region(&mut self, v: longhands::_moz_image_region::computed_value::T) { use values::Either; match v { Either::Second(_auto) => { self.gecko.mImageRegion.x = 0; self.gecko.mImageRegion.y = 0; self.gecko.mImageRegion.width = 0; self.gecko.mImageRegion.height = 0; } Either::First(rect) => { self.gecko.mImageRegion.x = rect.left.unwrap_or(Au(0)).0; self.gecko.mImageRegion.y = rect.top.unwrap_or(Au(0)).0; self.gecko.mImageRegion.height = rect.bottom.unwrap_or(Au(0)).0 - self.gecko.mImageRegion.y; self.gecko.mImageRegion.width = rect.right.unwrap_or(Au(0)).0 - self.gecko.mImageRegion.x; } } } ${impl_simple_copy('_moz_image_region', 'mImageRegion')} <%self:impl_trait style_struct_name="Table" skip_longhands="-x-span"> #[allow(non_snake_case)] pub fn set__x_span(&mut self, v: longhands::_x_span::computed_value::T) { self.gecko.mSpan = v.0 } ${impl_simple_copy('_x_span', 'mSpan')} <%self:impl_trait style_struct_name="Effects" skip_longhands="box-shadow clip filter"> pub fn set_box_shadow(&mut self, v: longhands::box_shadow::computed_value::T) { self.gecko.mBoxShadow.replace_with_new(v.0.len() as u32); for (servo, gecko_shadow) in v.0.into_iter() .zip(self.gecko.mBoxShadow.iter_mut()) { gecko_shadow.mXOffset = servo.offset_x.0; gecko_shadow.mYOffset = servo.offset_y.0; gecko_shadow.mRadius = servo.blur_radius.0; gecko_shadow.mSpread = servo.spread_radius.0; gecko_shadow.mSpread = servo.spread_radius.0; gecko_shadow.mInset = servo.inset; gecko_shadow.mColor = match servo.color { Color::RGBA(rgba) => { gecko_shadow.mHasColor = true; convert_rgba_to_nscolor(&rgba) }, // TODO handle currentColor // https://bugzilla.mozilla.org/show_bug.cgi?id=760345 Color::CurrentColor => 0, } } } pub fn copy_box_shadow_from(&mut self, other: &Self) { self.gecko.mBoxShadow.copy_from(&other.gecko.mBoxShadow); } pub fn clone_box_shadow(&self) -> longhands::box_shadow::computed_value::T { let buf = self.gecko.mBoxShadow.iter().map(|shadow| { longhands::box_shadow::single_value::computed_value::T { offset_x: Au(shadow.mXOffset), offset_y: Au(shadow.mYOffset), blur_radius: Au(shadow.mRadius), spread_radius: Au(shadow.mSpread), inset: shadow.mInset, color: Color::RGBA(convert_nscolor_to_rgba(shadow.mColor)), } }).collect(); longhands::box_shadow::computed_value::T(buf) } pub fn set_clip(&mut self, v: longhands::clip::computed_value::T) { use gecko_bindings::structs::NS_STYLE_CLIP_AUTO; use gecko_bindings::structs::NS_STYLE_CLIP_RECT; use gecko_bindings::structs::NS_STYLE_CLIP_LEFT_AUTO; use gecko_bindings::structs::NS_STYLE_CLIP_TOP_AUTO; use gecko_bindings::structs::NS_STYLE_CLIP_RIGHT_AUTO; use gecko_bindings::structs::NS_STYLE_CLIP_BOTTOM_AUTO; use values::Either; match v { Either::First(rect) => { self.gecko.mClipFlags = NS_STYLE_CLIP_RECT as u8; if let Some(left) = rect.left { self.gecko.mClip.x = left.0; } else { self.gecko.mClip.x = 0; self.gecko.mClipFlags |= NS_STYLE_CLIP_LEFT_AUTO as u8; } if let Some(top) = rect.top { self.gecko.mClip.y = top.0; } else { self.gecko.mClip.y = 0; self.gecko.mClipFlags |= NS_STYLE_CLIP_TOP_AUTO as u8; } if let Some(bottom) = rect.bottom { self.gecko.mClip.height = bottom.0 - self.gecko.mClip.y; } else { self.gecko.mClip.height = 1 << 30; // NS_MAXSIZE self.gecko.mClipFlags |= NS_STYLE_CLIP_BOTTOM_AUTO as u8; } if let Some(right) = rect.right { self.gecko.mClip.width = right.0 - self.gecko.mClip.x; } else { self.gecko.mClip.width = 1 << 30; // NS_MAXSIZE self.gecko.mClipFlags |= NS_STYLE_CLIP_RIGHT_AUTO as u8; } }, Either::Second(_auto) => { self.gecko.mClipFlags = NS_STYLE_CLIP_AUTO as u8; self.gecko.mClip.x = 0; self.gecko.mClip.y = 0; self.gecko.mClip.width = 0; self.gecko.mClip.height = 0; } } } pub fn copy_clip_from(&mut self, other: &Self) { self.gecko.mClip = other.gecko.mClip; self.gecko.mClipFlags = other.gecko.mClipFlags; } pub fn clone_clip(&self) -> longhands::clip::computed_value::T { use gecko_bindings::structs::NS_STYLE_CLIP_AUTO; use gecko_bindings::structs::NS_STYLE_CLIP_BOTTOM_AUTO; use gecko_bindings::structs::NS_STYLE_CLIP_LEFT_AUTO; use gecko_bindings::structs::NS_STYLE_CLIP_RIGHT_AUTO; use gecko_bindings::structs::NS_STYLE_CLIP_TOP_AUTO; use values::computed::{ClipRect, ClipRectOrAuto}; use values::Either; if self.gecko.mClipFlags == NS_STYLE_CLIP_AUTO as u8 { ClipRectOrAuto::auto() } else { let left = if self.gecko.mClipFlags & NS_STYLE_CLIP_LEFT_AUTO as u8 != 0 { debug_assert!(self.gecko.mClip.x == 0); None } else { Some(Au(self.gecko.mClip.x)) }; let top = if self.gecko.mClipFlags & NS_STYLE_CLIP_TOP_AUTO as u8 != 0 { debug_assert!(self.gecko.mClip.y == 0); None } else { Some(Au(self.gecko.mClip.y)) }; let bottom = if self.gecko.mClipFlags & NS_STYLE_CLIP_BOTTOM_AUTO as u8 != 0 { debug_assert!(self.gecko.mClip.height == 1 << 30); // NS_MAXSIZE None } else { Some(Au(self.gecko.mClip.y + self.gecko.mClip.height)) }; let right = if self.gecko.mClipFlags & NS_STYLE_CLIP_RIGHT_AUTO as u8 != 0 { debug_assert!(self.gecko.mClip.width == 1 << 30); // NS_MAXSIZE None } else { Some(Au(self.gecko.mClip.x + self.gecko.mClip.width)) }; Either::First(ClipRect { top: top, right: right, bottom: bottom, left: left, }) } } pub fn set_filter(&mut self, v: longhands::filter::computed_value::T) { use properties::longhands::filter::computed_value::Filter::*; use gecko_bindings::structs::nsCSSShadowArray; use gecko_bindings::structs::nsStyleFilter; use gecko_bindings::structs::NS_STYLE_FILTER_BLUR; use gecko_bindings::structs::NS_STYLE_FILTER_BRIGHTNESS; use gecko_bindings::structs::NS_STYLE_FILTER_CONTRAST; use gecko_bindings::structs::NS_STYLE_FILTER_GRAYSCALE; use gecko_bindings::structs::NS_STYLE_FILTER_INVERT; use gecko_bindings::structs::NS_STYLE_FILTER_OPACITY; use gecko_bindings::structs::NS_STYLE_FILTER_SATURATE; use gecko_bindings::structs::NS_STYLE_FILTER_SEPIA; use gecko_bindings::structs::NS_STYLE_FILTER_HUE_ROTATE; use gecko_bindings::structs::NS_STYLE_FILTER_DROP_SHADOW; fn fill_filter(m_type: u32, value: CoordDataValue, gecko_filter: &mut nsStyleFilter){ gecko_filter.mType = m_type; gecko_filter.mFilterParameter.set_value(value); } unsafe { Gecko_ResetFilters(&mut self.gecko, v.filters.len()); } debug_assert!(v.filters.len() == self.gecko.mFilters.len()); for (servo, gecko_filter) in v.filters.into_iter().zip(self.gecko.mFilters.iter_mut()) { //TODO: URL, drop-shadow match servo { Blur(len) => fill_filter(NS_STYLE_FILTER_BLUR, CoordDataValue::Coord(len.0), gecko_filter), Brightness(factor) => fill_filter(NS_STYLE_FILTER_BRIGHTNESS, CoordDataValue::Factor(factor), gecko_filter), Contrast(factor) => fill_filter(NS_STYLE_FILTER_CONTRAST, CoordDataValue::Factor(factor), gecko_filter), Grayscale(factor) => fill_filter(NS_STYLE_FILTER_GRAYSCALE, CoordDataValue::Factor(factor), gecko_filter), HueRotate(angle) => fill_filter(NS_STYLE_FILTER_HUE_ROTATE, CoordDataValue::from(angle), gecko_filter), Invert(factor) => fill_filter(NS_STYLE_FILTER_INVERT, CoordDataValue::Factor(factor), gecko_filter), Opacity(factor) => fill_filter(NS_STYLE_FILTER_OPACITY, CoordDataValue::Factor(factor), gecko_filter), Saturate(factor) => fill_filter(NS_STYLE_FILTER_SATURATE, CoordDataValue::Factor(factor), gecko_filter), Sepia(factor) => fill_filter(NS_STYLE_FILTER_SEPIA, CoordDataValue::Factor(factor), gecko_filter), DropShadow(shadow) => { gecko_filter.mType = NS_STYLE_FILTER_DROP_SHADOW; fn init_shadow(filter: &mut nsStyleFilter) -> &mut nsCSSShadowArray { unsafe { let ref mut union = filter.__bindgen_anon_1; let mut shadow_array: &mut *mut nsCSSShadowArray = union.mDropShadow.as_mut(); *shadow_array = Gecko_NewCSSShadowArray(1); &mut **shadow_array } } let mut gecko_shadow = init_shadow(gecko_filter); gecko_shadow.mArray[0].mXOffset = shadow.offset_x.0; gecko_shadow.mArray[0].mYOffset = shadow.offset_y.0; gecko_shadow.mArray[0].mRadius = shadow.blur_radius.0; // mSpread is not supported in the spec, so we leave it as 0 gecko_shadow.mArray[0].mInset = false; // Not supported in spec level 1 gecko_shadow.mArray[0].mColor = match shadow.color { Color::RGBA(rgba) => { gecko_shadow.mArray[0].mHasColor = true; convert_rgba_to_nscolor(&rgba) }, // TODO handle currentColor // https://bugzilla.mozilla.org/show_bug.cgi?id=760345 Color::CurrentColor => 0, }; } Url(ref url) => { unsafe { bindings::Gecko_nsStyleFilter_SetURLValue(gecko_filter, url.for_ffi()); } } } } } pub fn copy_filter_from(&mut self, other: &Self) { unsafe { Gecko_CopyFiltersFrom(&other.gecko as *const _ as *mut _, &mut self.gecko); } } <%self:impl_trait style_struct_name="InheritedBox" skip_longhands="image-orientation"> // FIXME: Gecko uses a tricky way to store computed value of image-orientation // within an u8. We could inline following glue codes by implementing all // those tricky parts for Servo as well. But, it's not done yet just for // convenience. pub fn set_image_orientation(&mut self, v: longhands::image_orientation::computed_value::T) { use properties::longhands::image_orientation::computed_value::T; match v { T::FromImage => { unsafe { bindings::Gecko_SetImageOrientationAsFromImage(&mut self.gecko); } }, T::AngleWithFlipped(ref angle, flipped) => { unsafe { bindings::Gecko_SetImageOrientation(&mut self.gecko, angle.radians() as f64, flipped); } } } } pub fn copy_image_orientation_from(&mut self, other: &Self) { unsafe { bindings::Gecko_CopyImageOrientationFrom(&mut self.gecko, &other.gecko); } } <%self:impl_trait style_struct_name="InheritedTable" skip_longhands="border-spacing"> pub fn set_border_spacing(&mut self, v: longhands::border_spacing::computed_value::T) { self.gecko.mBorderSpacingCol = v.horizontal.0; self.gecko.mBorderSpacingRow = v.vertical.0; } pub fn copy_border_spacing_from(&mut self, other: &Self) { self.gecko.mBorderSpacingCol = other.gecko.mBorderSpacingCol; self.gecko.mBorderSpacingRow = other.gecko.mBorderSpacingRow; } pub fn clone_border_spacing(&self) -> longhands::border_spacing::computed_value::T { longhands::border_spacing::computed_value::T { horizontal: Au(self.gecko.mBorderSpacingCol), vertical: Au(self.gecko.mBorderSpacingRow) } } <%self:impl_trait style_struct_name="InheritedText" skip_longhands="text-align text-emphasis-style text-shadow line-height letter-spacing word-spacing -webkit-text-stroke-width text-emphasis-position -moz-tab-size -moz-text-size-adjust"> <% text_align_keyword = Keyword("text-align", "start end left right center justify -moz-center -moz-left -moz-right char", gecko_strip_moz_prefix=False) %> <% text_align_reachable_keyword = Keyword("text-align", "start end left right center justify char") %> ${impl_keyword('text_align', 'mTextAlign', text_align_keyword, need_clone=False)} ${impl_keyword_clone('text_align', 'mTextAlign', text_align_keyword)} pub fn set_text_shadow(&mut self, v: longhands::text_shadow::computed_value::T) { self.gecko.mTextShadow.replace_with_new(v.0.len() as u32); for (servo, gecko_shadow) in v.0.into_iter() .zip(self.gecko.mTextShadow.iter_mut()) { gecko_shadow.mXOffset = servo.offset_x.0; gecko_shadow.mYOffset = servo.offset_y.0; gecko_shadow.mRadius = servo.blur_radius.0; gecko_shadow.mHasColor = false; gecko_shadow.mColor = match servo.color { Color::RGBA(rgba) => { gecko_shadow.mHasColor = true; convert_rgba_to_nscolor(&rgba) }, // TODO handle currentColor // https://bugzilla.mozilla.org/show_bug.cgi?id=760345 Color::CurrentColor => 0, } } } pub fn copy_text_shadow_from(&mut self, other: &Self) { self.gecko.mTextShadow.copy_from(&other.gecko.mTextShadow); } pub fn clone_text_shadow(&self) -> longhands::text_shadow::computed_value::T { let buf = self.gecko.mTextShadow.iter().map(|shadow| { longhands::text_shadow::computed_value::TextShadow { offset_x: Au(shadow.mXOffset), offset_y: Au(shadow.mYOffset), blur_radius: Au(shadow.mRadius), color: Color::RGBA(convert_nscolor_to_rgba(shadow.mColor)), } }).collect(); longhands::text_shadow::computed_value::T(buf) } pub fn set_line_height(&mut self, v: longhands::line_height::computed_value::T) { use properties::longhands::line_height::computed_value::T; // FIXME: Align binary representations and ditch |match| for cast + static_asserts let en = match v { T::Normal => CoordDataValue::Normal, T::Length(val) => CoordDataValue::Coord(val.0), T::Number(val) => CoordDataValue::Factor(val), T::MozBlockHeight => CoordDataValue::Enumerated(structs::NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT), }; self.gecko.mLineHeight.set_value(en); } pub fn clone_line_height(&self) -> longhands::line_height::computed_value::T { use properties::longhands::line_height::computed_value::T; return match self.gecko.mLineHeight.as_value() { CoordDataValue::Normal => T::Normal, CoordDataValue::Coord(coord) => T::Length(Au(coord)), CoordDataValue::Factor(n) => T::Number(n), CoordDataValue::Enumerated(val) if val == structs::NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT => T::MozBlockHeight, _ => { debug_assert!(false); T::MozBlockHeight } } } <%call expr="impl_coord_copy('line_height', 'mLineHeight')"> pub fn set_letter_spacing(&mut self, v: longhands::letter_spacing::computed_value::T) { match v.0 { Some(au) => self.gecko.mLetterSpacing.set(au), None => self.gecko.mLetterSpacing.set_value(CoordDataValue::Normal) } } pub fn clone_letter_spacing(&self) -> longhands::letter_spacing::computed_value::T { use properties::longhands::letter_spacing::computed_value::T; debug_assert!( matches!(self.gecko.mLetterSpacing.as_value(), CoordDataValue::Normal | CoordDataValue::Coord(_)), "Unexpected computed value for letter-spacing"); T(Au::from_gecko_style_coord(&self.gecko.mLetterSpacing)) } <%call expr="impl_coord_copy('letter_spacing', 'mLetterSpacing')"> pub fn set_word_spacing(&mut self, v: longhands::word_spacing::computed_value::T) { match v.0 { Some(lop) => self.gecko.mWordSpacing.set(lop), // https://drafts.csswg.org/css-text-3/#valdef-word-spacing-normal None => self.gecko.mWordSpacing.set_value(CoordDataValue::Coord(0)), } } pub fn clone_word_spacing(&self) -> longhands::word_spacing::computed_value::T { use properties::longhands::word_spacing::computed_value::T; use values::computed::LengthOrPercentage; debug_assert!( matches!(self.gecko.mWordSpacing.as_value(), CoordDataValue::Normal | CoordDataValue::Coord(_) | CoordDataValue::Percent(_) | CoordDataValue::Calc(_)), "Unexpected computed value for word-spacing"); T(LengthOrPercentage::from_gecko_style_coord(&self.gecko.mWordSpacing)) } <%call expr="impl_coord_copy('word_spacing', 'mWordSpacing')"> fn clear_text_emphasis_style_if_string(&mut self) { use nsstring::nsString; if self.gecko.mTextEmphasisStyle == structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING as u8 { self.gecko.mTextEmphasisStyleString.assign(&nsString::new()); self.gecko.mTextEmphasisStyle = structs::NS_STYLE_TEXT_EMPHASIS_STYLE_NONE as u8; } } pub fn set_text_emphasis_position(&mut self, v: longhands::text_emphasis_position::computed_value::T) { use properties::longhands::text_emphasis_position::*; let mut result = match v.0 { HorizontalWritingModeValue::Over => structs::NS_STYLE_TEXT_EMPHASIS_POSITION_OVER as u8, HorizontalWritingModeValue::Under => structs::NS_STYLE_TEXT_EMPHASIS_POSITION_UNDER as u8, }; match v.1 { VerticalWritingModeValue::Right => { result |= structs::NS_STYLE_TEXT_EMPHASIS_POSITION_RIGHT as u8; } VerticalWritingModeValue::Left => { result |= structs::NS_STYLE_TEXT_EMPHASIS_POSITION_LEFT as u8; } } self.gecko.mTextEmphasisPosition = result; } <%call expr="impl_simple_copy('text_emphasis_position', 'mTextEmphasisPosition')"> pub fn set_text_emphasis_style(&mut self, v: longhands::text_emphasis_style::computed_value::T) { use properties::longhands::text_emphasis_style::computed_value::T; use properties::longhands::text_emphasis_style::ShapeKeyword; self.clear_text_emphasis_style_if_string(); let (te, s) = match v { T::None => (structs::NS_STYLE_TEXT_EMPHASIS_STYLE_NONE, ""), T::Keyword(ref keyword) => { let fill = if keyword.fill { structs::NS_STYLE_TEXT_EMPHASIS_STYLE_FILLED } else { structs::NS_STYLE_TEXT_EMPHASIS_STYLE_OPEN }; let shape = match keyword.shape { ShapeKeyword::Dot => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_DOT, ShapeKeyword::Circle => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_CIRCLE, ShapeKeyword::DoubleCircle => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_DOUBLE_CIRCLE, ShapeKeyword::Triangle => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_TRIANGLE, ShapeKeyword::Sesame => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_SESAME, }; (shape | fill, keyword.shape.char(keyword.fill)) }, T::String(ref s) => { (structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING, &**s) }, }; self.gecko.mTextEmphasisStyleString.assign_utf8(s); self.gecko.mTextEmphasisStyle = te as u8; } pub fn copy_text_emphasis_style_from(&mut self, other: &Self) { self.clear_text_emphasis_style_if_string(); if other.gecko.mTextEmphasisStyle == structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING as u8 { self.gecko.mTextEmphasisStyleString .assign(&*other.gecko.mTextEmphasisStyleString) } self.gecko.mTextEmphasisStyle = other.gecko.mTextEmphasisStyle; } <%call expr="impl_app_units('_webkit_text_stroke_width', 'mWebkitTextStrokeWidth', need_clone=False)"> #[allow(non_snake_case)] pub fn set__moz_tab_size(&mut self, v: longhands::_moz_tab_size::computed_value::T) { use values::Either; match v { Either::Second(number) => { self.gecko.mTabSize.set_value(CoordDataValue::Factor(number)); } Either::First(au) => { self.gecko.mTabSize.set(au); } } } <%call expr="impl_coord_copy('_moz_tab_size', 'mTabSize')"> <% text_size_adjust_keyword = Keyword("text-size-adjust", "auto none") %> ${impl_keyword('_moz_text_size_adjust', 'mTextSizeAdjust', text_size_adjust_keyword, need_clone=False)} <%self:impl_trait style_struct_name="Text" skip_longhands="text-decoration-line text-overflow initial-letter" skip_additionals="*"> pub fn set_text_decoration_line(&mut self, v: longhands::text_decoration_line::computed_value::T) { let mut bits: u8 = 0; if v.contains(longhands::text_decoration_line::UNDERLINE) { bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE as u8; } if v.contains(longhands::text_decoration_line::OVERLINE) { bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_OVERLINE as u8; } if v.contains(longhands::text_decoration_line::LINE_THROUGH) { bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH as u8; } if v.contains(longhands::text_decoration_line::BLINK) { bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_BLINK as u8; } if v.contains(longhands::text_decoration_line::COLOR_OVERRIDE) { bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_OVERRIDE_ALL as u8; } self.gecko.mTextDecorationLine = bits; } ${impl_simple_copy('text_decoration_line', 'mTextDecorationLine')} fn clear_overflow_sides_if_string(&mut self) { use gecko_bindings::structs::nsStyleTextOverflowSide; use nsstring::nsString; fn clear_if_string(side: &mut nsStyleTextOverflowSide) { if side.mType == structs::NS_STYLE_TEXT_OVERFLOW_STRING as u8 { side.mString.assign(&nsString::new()); side.mType = structs::NS_STYLE_TEXT_OVERFLOW_CLIP as u8; } } clear_if_string(&mut self.gecko.mTextOverflow.mLeft); clear_if_string(&mut self.gecko.mTextOverflow.mRight); } pub fn set_text_overflow(&mut self, v: longhands::text_overflow::computed_value::T) { use gecko_bindings::structs::nsStyleTextOverflowSide; use properties::longhands::text_overflow::{SpecifiedValue, Side}; fn set(side: &mut nsStyleTextOverflowSide, value: &Side) { let ty = match *value { Side::Clip => structs::NS_STYLE_TEXT_OVERFLOW_CLIP, Side::Ellipsis => structs::NS_STYLE_TEXT_OVERFLOW_ELLIPSIS, Side::String(ref s) => { side.mString.assign_utf8(s); structs::NS_STYLE_TEXT_OVERFLOW_STRING } }; side.mType = ty as u8; } self.clear_overflow_sides_if_string(); if v.second.is_none() { self.gecko.mTextOverflow.mLogicalDirections = true; } let SpecifiedValue { ref first, ref second } = v; let second = second.as_ref().unwrap_or(&first); set(&mut self.gecko.mTextOverflow.mLeft, first); set(&mut self.gecko.mTextOverflow.mRight, second); } pub fn copy_text_overflow_from(&mut self, other: &Self) { use gecko_bindings::structs::nsStyleTextOverflowSide; fn set(side: &mut nsStyleTextOverflowSide, other: &nsStyleTextOverflowSide) { if other.mType == structs::NS_STYLE_TEXT_OVERFLOW_STRING as u8 { side.mString.assign(&*other.mString) } side.mType = other.mType } self.clear_overflow_sides_if_string(); set(&mut self.gecko.mTextOverflow.mLeft, &other.gecko.mTextOverflow.mLeft); set(&mut self.gecko.mTextOverflow.mRight, &other.gecko.mTextOverflow.mRight); self.gecko.mTextOverflow.mLogicalDirections = other.gecko.mTextOverflow.mLogicalDirections; } pub fn set_initial_letter(&mut self, v: longhands::initial_letter::computed_value::T) { use properties::longhands::initial_letter::computed_value::T; match v { T::Normal => { self.gecko.mInitialLetterSize = 0.; self.gecko.mInitialLetterSink = 0; }, T::Specified(size, sink) => { self.gecko.mInitialLetterSize = size.get(); if let Some(sink) = sink { self.gecko.mInitialLetterSink = sink.value(); } else { self.gecko.mInitialLetterSink = size.get().floor() as i32; } } } } pub fn copy_initial_letter_from(&mut self, other: &Self) { self.gecko.mInitialLetterSize = other.gecko.mInitialLetterSize; self.gecko.mInitialLetterSink = other.gecko.mInitialLetterSink; } #[inline] pub fn has_underline(&self) -> bool { (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE as u8)) != 0 } #[inline] pub fn has_overline(&self) -> bool { (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_OVERLINE as u8)) != 0 } #[inline] pub fn has_line_through(&self) -> bool { (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH as u8)) != 0 } <%def name="impl_shape_source(ident, gecko_ffi_name)"> pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { use gecko_bindings::bindings::{Gecko_NewBasicShape, Gecko_DestroyShapeSource}; use gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType, StyleShapeSourceType}; use gecko_bindings::structs::{StyleFillRule, StyleGeometryBox, StyleShapeSource}; use gecko::conversions::basic_shape::set_corners_from_radius; use gecko::values::GeckoStyleCoordConvertible; use values::computed::basic_shape::BasicShape; use values::generics::basic_shape::{ShapeSource, FillRule}; let ref mut ${ident} = self.gecko.${gecko_ffi_name}; // clean up existing struct unsafe { Gecko_DestroyShapeSource(${ident}) }; ${ident}.mType = StyleShapeSourceType::None; match v { ShapeSource::Url(ref url) => { unsafe { bindings::Gecko_StyleShapeSource_SetURLValue(${ident}, url.for_ffi()) } } ShapeSource::None => {} // don't change the type ShapeSource::Box(reference) => { ${ident}.mReferenceBox = reference.into(); ${ident}.mType = StyleShapeSourceType::Box; } ShapeSource::Shape(servo_shape, maybe_box) => { ${ident}.mReferenceBox = maybe_box.map(Into::into) .unwrap_or(StyleGeometryBox::NoBox); ${ident}.mType = StyleShapeSourceType::Shape; fn init_shape(${ident}: &mut StyleShapeSource, ty: StyleBasicShapeType) -> &mut StyleBasicShape { unsafe { // We have to be very careful to avoid a copy here! let ref mut union = ${ident}.__bindgen_anon_1; let mut shape: &mut *mut StyleBasicShape = union.mBasicShape.as_mut(); *shape = Gecko_NewBasicShape(ty); &mut **shape } } match servo_shape { BasicShape::Inset(rect) => { let mut shape = init_shape(${ident}, StyleBasicShapeType::Inset); unsafe { shape.mCoordinates.set_len(4) }; // set_len() can't call constructors, so the coordinates // can contain any value. set_value() attempts to free // allocated coordinates, so we don't want to feed it // garbage values which it may misinterpret. // Instead, we use leaky_set_value to blindly overwrite // the garbage data without // attempting to clean up. shape.mCoordinates[0].leaky_set_null(); rect.top.to_gecko_style_coord(&mut shape.mCoordinates[0]); shape.mCoordinates[1].leaky_set_null(); rect.right.to_gecko_style_coord(&mut shape.mCoordinates[1]); shape.mCoordinates[2].leaky_set_null(); rect.bottom.to_gecko_style_coord(&mut shape.mCoordinates[2]); shape.mCoordinates[3].leaky_set_null(); rect.left.to_gecko_style_coord(&mut shape.mCoordinates[3]); set_corners_from_radius(rect.round, &mut shape.mRadius); } BasicShape::Circle(circ) => { let mut shape = init_shape(${ident}, StyleBasicShapeType::Circle); unsafe { shape.mCoordinates.set_len(1) }; shape.mCoordinates[0].leaky_set_null(); circ.radius.to_gecko_style_coord(&mut shape.mCoordinates[0]); shape.mPosition = circ.position.into(); } BasicShape::Ellipse(el) => { let mut shape = init_shape(${ident}, StyleBasicShapeType::Ellipse); unsafe { shape.mCoordinates.set_len(2) }; shape.mCoordinates[0].leaky_set_null(); el.semiaxis_x.to_gecko_style_coord(&mut shape.mCoordinates[0]); shape.mCoordinates[1].leaky_set_null(); el.semiaxis_y.to_gecko_style_coord(&mut shape.mCoordinates[1]); shape.mPosition = el.position.into(); } BasicShape::Polygon(poly) => { let mut shape = init_shape(${ident}, StyleBasicShapeType::Polygon); unsafe { shape.mCoordinates.set_len(poly.coordinates.len() as u32 * 2); } for (i, coord) in poly.coordinates.iter().enumerate() { shape.mCoordinates[2 * i].leaky_set_null(); shape.mCoordinates[2 * i + 1].leaky_set_null(); coord.0.to_gecko_style_coord(&mut shape.mCoordinates[2 * i]); coord.1.to_gecko_style_coord(&mut shape.mCoordinates[2 * i + 1]); } shape.mFillRule = if poly.fill == FillRule::EvenOdd { StyleFillRule::Evenodd } else { StyleFillRule::Nonzero }; } } } } } pub fn copy_${ident}_from(&mut self, other: &Self) { use gecko_bindings::bindings::Gecko_CopyShapeSourceFrom; unsafe { Gecko_CopyShapeSourceFrom(&mut self.gecko.${gecko_ffi_name}, &other.gecko.${gecko_ffi_name}); } } <% skip_svg_longhands = """ mask-mode mask-repeat mask-clip mask-origin mask-composite mask-position-x mask-position-y mask-size mask-image clip-path """ %> <%self:impl_trait style_struct_name="SVG" skip_longhands="${skip_svg_longhands}" skip_additionals="*"> <% impl_common_image_layer_properties("mask") %> <%self:simple_image_array_property name="mode" shorthand="mask" field_name="mMaskMode"> use properties::longhands::mask_mode::single_value::computed_value::T; match servo { T::alpha => structs::NS_STYLE_MASK_MODE_ALPHA as u8, T::luminance => structs::NS_STYLE_MASK_MODE_LUMINANCE as u8, T::match_source => structs::NS_STYLE_MASK_MODE_MATCH_SOURCE as u8, } <%self:simple_image_array_property name="composite" shorthand="mask" field_name="mComposite"> use properties::longhands::mask_composite::single_value::computed_value::T; match servo { T::add => structs::NS_STYLE_MASK_COMPOSITE_ADD as u8, T::subtract => structs::NS_STYLE_MASK_COMPOSITE_SUBTRACT as u8, T::intersect => structs::NS_STYLE_MASK_COMPOSITE_INTERSECT as u8, T::exclude => structs::NS_STYLE_MASK_COMPOSITE_EXCLUDE as u8, } <% impl_shape_source("clip_path", "mClipPath") %> <%self:impl_trait style_struct_name="InheritedSVG" skip_longhands="paint-order stroke-dasharray" skip_additionals="*"> pub fn set_paint_order(&mut self, v: longhands::paint_order::computed_value::T) { use self::longhands::paint_order; if v.0 == 0 { self.gecko.mPaintOrder = structs::NS_STYLE_PAINT_ORDER_NORMAL as u8; } else { let mut order = 0; for pos in 0..3 { let geckoval = match v.bits_at(pos) { paint_order::FILL => structs::NS_STYLE_PAINT_ORDER_FILL as u8, paint_order::STROKE => structs::NS_STYLE_PAINT_ORDER_STROKE as u8, paint_order::MARKERS => structs::NS_STYLE_PAINT_ORDER_MARKERS as u8, _ => unreachable!(), }; order |= geckoval << (pos * structs::NS_STYLE_PAINT_ORDER_BITWIDTH as u8); } self.gecko.mPaintOrder = order; } } ${impl_simple_copy('paint_order', 'mPaintOrder')} pub fn set_stroke_dasharray(&mut self, v: longhands::stroke_dasharray::computed_value::T) { unsafe { bindings::Gecko_nsStyleSVG_SetDashArrayLength(&mut self.gecko, v.0.len() as u32); } for (mut gecko, servo) in self.gecko.mStrokeDasharray.iter_mut().zip(v.0.into_iter()) { match servo { Either::First(number) => gecko.set_value(CoordDataValue::Factor(number)), Either::Second(lop) => gecko.set(lop), } } } pub fn copy_stroke_dasharray_from(&mut self, other: &Self) { unsafe { bindings::Gecko_nsStyleSVG_CopyDashArray(&mut self.gecko, &other.gecko); } } <%self:impl_trait style_struct_name="Color" skip_longhands="*"> pub fn set_color(&mut self, v: longhands::color::computed_value::T) { let result = convert_rgba_to_nscolor(&v); ${set_gecko_property("mColor", "result")} } <%call expr="impl_simple_copy('color', 'mColor')"> pub fn clone_color(&self) -> longhands::color::computed_value::T { let color = ${get_gecko_property("mColor")} as u32; convert_nscolor_to_rgba(color) } <%self:impl_trait style_struct_name="Pointing" skip_longhands="cursor caret-color"> pub fn set_cursor(&mut self, v: longhands::cursor::computed_value::T) { use properties::longhands::cursor::computed_value::Keyword; use style_traits::cursor::Cursor; self.gecko.mCursor = match v.keyword { Keyword::AutoCursor => structs::NS_STYLE_CURSOR_AUTO, Keyword::SpecifiedCursor(cursor) => match cursor { Cursor::None => structs::NS_STYLE_CURSOR_NONE, Cursor::Default => structs::NS_STYLE_CURSOR_DEFAULT, Cursor::Pointer => structs::NS_STYLE_CURSOR_POINTER, Cursor::ContextMenu => structs::NS_STYLE_CURSOR_CONTEXT_MENU, Cursor::Help => structs::NS_STYLE_CURSOR_HELP, Cursor::Progress => structs::NS_STYLE_CURSOR_DEFAULT, // Gecko doesn't support "progress" yet Cursor::Wait => structs::NS_STYLE_CURSOR_WAIT, Cursor::Cell => structs::NS_STYLE_CURSOR_CELL, Cursor::Crosshair => structs::NS_STYLE_CURSOR_CROSSHAIR, Cursor::Text => structs::NS_STYLE_CURSOR_TEXT, Cursor::VerticalText => structs::NS_STYLE_CURSOR_VERTICAL_TEXT, Cursor::Alias => structs::NS_STYLE_CURSOR_ALIAS, Cursor::Copy => structs::NS_STYLE_CURSOR_COPY, Cursor::Move => structs::NS_STYLE_CURSOR_MOVE, Cursor::NoDrop => structs::NS_STYLE_CURSOR_NO_DROP, Cursor::NotAllowed => structs::NS_STYLE_CURSOR_NOT_ALLOWED, Cursor::Grab => structs::NS_STYLE_CURSOR_GRAB, Cursor::Grabbing => structs::NS_STYLE_CURSOR_GRABBING, Cursor::EResize => structs::NS_STYLE_CURSOR_E_RESIZE, Cursor::NResize => structs::NS_STYLE_CURSOR_N_RESIZE, Cursor::NeResize => structs::NS_STYLE_CURSOR_NE_RESIZE, Cursor::NwResize => structs::NS_STYLE_CURSOR_NW_RESIZE, Cursor::SResize => structs::NS_STYLE_CURSOR_S_RESIZE, Cursor::SeResize => structs::NS_STYLE_CURSOR_SE_RESIZE, Cursor::SwResize => structs::NS_STYLE_CURSOR_SW_RESIZE, Cursor::WResize => structs::NS_STYLE_CURSOR_W_RESIZE, Cursor::EwResize => structs::NS_STYLE_CURSOR_EW_RESIZE, Cursor::NsResize => structs::NS_STYLE_CURSOR_NS_RESIZE, Cursor::NeswResize => structs::NS_STYLE_CURSOR_NESW_RESIZE, Cursor::NwseResize => structs::NS_STYLE_CURSOR_NWSE_RESIZE, Cursor::ColResize => structs::NS_STYLE_CURSOR_COL_RESIZE, Cursor::RowResize => structs::NS_STYLE_CURSOR_ROW_RESIZE, Cursor::AllScroll => structs::NS_STYLE_CURSOR_ALL_SCROLL, Cursor::ZoomIn => structs::NS_STYLE_CURSOR_ZOOM_IN, Cursor::ZoomOut => structs::NS_STYLE_CURSOR_ZOOM_OUT, } } as u8; unsafe { Gecko_SetCursorArrayLength(&mut self.gecko, v.images.len()); } for i in 0..v.images.len() { let image = &v.images[i]; unsafe { 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 // are re-used from the applicable declaration cache, and the Pointing struct // is an inherited struct. } } pub fn copy_cursor_from(&mut self, other: &Self) { self.gecko.mCursor = other.gecko.mCursor; unsafe { Gecko_CopyCursorArrayFrom(&mut self.gecko, &other.gecko); } } pub fn set_caret_color(&mut self, v: longhands::caret_color::computed_value::T){ use values::Either; match v { Either::First(color) => { self.gecko.mCaretColor = StyleComplexColor::from(color); } Either::Second(_auto) => { self.gecko.mCaretColor = StyleComplexColor::auto(); } } } pub fn copy_caret_color_from(&mut self, other: &Self){ self.gecko.mCaretColor = other.gecko.mCaretColor; } <%call expr="impl_color_clone('caret_color', 'mCaretColor')"> <%self:impl_trait style_struct_name="Column" skip_longhands="column-count column-rule-width"> #[allow(unused_unsafe)] pub fn set_column_count(&mut self, v: longhands::column_count::computed_value::T) { use gecko_bindings::structs::{NS_STYLE_COLUMN_COUNT_AUTO, nsStyleColumn_kMaxColumnCount}; self.gecko.mColumnCount = match v { Either::First(number) => unsafe { cmp::min(number as u32, nsStyleColumn_kMaxColumnCount) }, Either::Second(Auto) => NS_STYLE_COLUMN_COUNT_AUTO }; } ${impl_simple_copy('column_count', 'mColumnCount')} pub fn clone_column_count(&self) -> longhands::column_count::computed_value::T { use gecko_bindings::structs::NS_STYLE_COLUMN_COUNT_AUTO; if self.gecko.mColumnCount != NS_STYLE_COLUMN_COUNT_AUTO { debug_assert!((self.gecko.mColumnCount as i32) >= 0 && (self.gecko.mColumnCount as i32) < i32::max_value()); Either::First(self.gecko.mColumnCount as i32) } else { Either::Second(Auto) } } <% impl_app_units("column_rule_width", "mColumnRuleWidth", need_clone=True, round_to_pixels=True) %> <%self:impl_trait style_struct_name="Counters" 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; // Converts a string as utf16, and returns an owned, zero-terminated raw buffer. fn as_utf16_and_forget(s: &str) -> *mut u16 { use std::mem; let mut vec = s.encode_utf16().collect::>(); vec.push(0u16); let ptr = vec.as_mut_ptr(); mem::forget(vec); ptr } match v { T::none | T::normal => { // Ensure destructors run, otherwise we could leak. if !self.gecko.mContents.is_empty() { unsafe { Gecko_ClearAndResizeStyleContents(&mut self.gecko, 0); } } }, T::Content(items) => { unsafe { Gecko_ClearAndResizeStyleContents(&mut self.gecko, items.len() as u32); } for (i, item) in items.into_iter().enumerate() { // 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. unsafe { *self.gecko.mContents[i].mContent.mString.as_mut() = ptr::null_mut(); } match item { ContentItem::String(value) => { self.gecko.mContents[i].mType = eStyleContentType_String; unsafe { // NB: we share allocators, so doing this is fine. *self.gecko.mContents[i].mContent.mString.as_mut() = 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 => self.gecko.mContents[i].mType = eStyleContentType_CloseQuote, ContentItem::NoOpenQuote => self.gecko.mContents[i].mType = eStyleContentType_NoOpenQuote, ContentItem::NoCloseQuote => self.gecko.mContents[i].mType = eStyleContentType_NoCloseQuote, ContentItem::MozAltContent => self.gecko.mContents[i].mType = eStyleContentType_AltContent, 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()) } } } } } } } pub fn copy_content_from(&mut self, other: &Self) { use gecko_bindings::bindings::Gecko_CopyStyleContentsFrom; unsafe { 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, (name, value)) in v.0.into_iter().enumerate() { self.gecko.m${counter_property}s[i].mCounter.assign(name.0.as_slice()); self.gecko.m${counter_property}s[i].mValue = value; } } } 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="UI" skip_longhands="-moz-force-broken-image-icon"> #[allow(non_snake_case)] pub fn set__moz_force_broken_image_icon(&mut self, v: longhands::_moz_force_broken_image_icon::computed_value::T) { self.gecko.mForceBrokenImageIcon = v.0 as u8; } ${impl_simple_copy("_moz_force_broken_image_icon", "mForceBrokenImageIcon")} <%self:impl_trait style_struct_name="XUL" skip_longhands="-moz-stack-sizing -moz-box-ordinal-group"> #[allow(non_snake_case)] pub fn set__moz_stack_sizing(&mut self, v: longhands::_moz_stack_sizing::computed_value::T) { use properties::longhands::_moz_stack_sizing::computed_value::T; self.gecko.mStretchStack = v == T::stretch_to_fit; } ${impl_simple_copy('_moz_stack_sizing', 'mStretchStack')} #[allow(non_snake_case)] pub fn set__moz_box_ordinal_group(&mut self, v: i32) { self.gecko.mBoxOrdinal = v as u32; } ${impl_simple_copy("_moz_box_ordinal_group", "mBoxOrdinal")} <%def name="define_ffi_struct_accessor(style_struct)"> #[no_mangle] #[allow(non_snake_case, unused_variables)] pub unsafe extern "C" fn Servo_GetStyle${style_struct.gecko_name}(computed_values: ServoComputedValuesBorrowedOrNull) -> *const ${style_struct.gecko_ffi_name} { ComputedValues::arc_from_borrowed(&computed_values).unwrap().get_${style_struct.name_lower}().get_gecko() as *const ${style_struct.gecko_ffi_name} } % for style_struct in data.style_structs: ${declare_style_struct(style_struct)} ${impl_style_struct(style_struct)} % if not style_struct.name in data.manual_style_structs: <%self:raw_impl_trait style_struct="${style_struct}"> % endif ${define_ffi_struct_accessor(style_struct)} % endfor // This is only accessed from the Gecko main thread. static mut EMPTY_VARIABLES_STRUCT: Option = None; #[no_mangle] #[allow(non_snake_case)] pub unsafe extern "C" fn Servo_GetStyleVariables(_cv: ServoComputedValuesBorrowedOrNull) -> *const nsStyleVariables { EMPTY_VARIABLES_STRUCT.as_ref().unwrap() } pub fn initialize() { unsafe { EMPTY_VARIABLES_STRUCT = Some(zeroed()); Gecko_Construct_nsStyleVariables(EMPTY_VARIABLES_STRUCT.as_mut().unwrap()); } } pub fn shutdown() { unsafe { EMPTY_VARIABLES_STRUCT.take().as_mut().map(|v| Gecko_Destroy_nsStyleVariables(v)); } }