/* 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 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_CopyMozBindingFrom; 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_NewCSSShadowArray; use gecko_bindings::bindings::Gecko_SetListStyleImage; use gecko_bindings::bindings::Gecko_SetListStyleImageNone; use gecko_bindings::bindings::Gecko_SetListStyleType; use gecko_bindings::bindings::Gecko_SetMozBinding; 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; 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::longhands; use properties::{DeclaredValue, Importance, LonghandId}; use properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyDeclarationId}; use std::fmt::{self, Debug}; use std::mem::{transmute, zeroed}; use std::ptr; use std::sync::Arc; use std::cmp; use values::computed::ToComputedValue; 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>, shareable: bool, pub writing_mode: WritingMode, pub root_font_size: Au, } impl ComputedValues { pub fn inherit_from(parent: &Arc, default: &Arc) -> Arc { Arc::new(ComputedValues { custom_properties: parent.custom_properties.clone(), shareable: parent.shareable, writing_mode: parent.writing_mode, root_font_size: parent.root_font_size, % for style_struct in data.style_structs: % if style_struct.inherited: ${style_struct.ident}: parent.${style_struct.ident}.clone(), % else: ${style_struct.ident}: default.${style_struct.ident}.clone(), % endif % endfor }) } pub fn new(custom_properties: Option>, shareable: bool, writing_mode: WritingMode, root_font_size: Au, % for style_struct in data.style_structs: ${style_struct.ident}: Arc, % endfor ) -> Self { ComputedValues { custom_properties: custom_properties, shareable: shareable, writing_mode: writing_mode, root_font_size: root_font_size, % 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, shareable: true, writing_mode: WritingMode::empty(), // FIXME(bz): This seems dubious root_font_size: longhands::font_size::get_initial_value(), // FIXME(bz): Also seems dubious? % for style_struct in data.style_structs: ${style_struct.ident}: style_structs::${style_struct.name}::default(pres_context), % endfor }) } % 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} } #[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.as_ref().map(|x| x.clone()) } #[allow(non_snake_case)] pub fn has_moz_binding(&self) -> bool { !self.get_box().gecko.mBinding.mRawPtr.is_null() } pub fn root_font_size(&self) -> Au { self.root_font_size } pub fn set_root_font_size(&mut self, s: Au) { self.root_font_size = s; } pub fn set_writing_mode(&mut self, mode: WritingMode) { self.writing_mode = mode; } // 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 { declarations: vec![ (PropertyDeclaration::${prop.camel_case}(DeclaredValue::Value( longhands::${prop.ident}::SpecifiedValue::from_computed_value( &self.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}()))), Importance::Normal) ], important_count: 0 } }, % 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}, } <%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 ffi_name == "__LIST_STYLE_TYPE__": return "unsafe { Gecko_SetListStyleType(&mut self.gecko, %s as u32); }" % 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')"> #[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")} } <%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 } } <%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: use cssparser::Color; let result = match v { Color::RGBA(rgba) => convert_rgba_to_nscolor(&rgba), // FIXME #13547 Color::CurrentColor => 0, }; % 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: use cssparser::Color; 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_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_app_units(ident, gecko_ffi_name, need_clone, round_to_pixels=False)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { % if round_to_pixels: let au_per_device_px = Au(self.gecko.mTwipsPerPixel); self.gecko.${gecko_ffi_name} = round_border_to_device_pixels(v, au_per_device_px).0; % else: self.gecko.${gecko_ffi_name} = v.0; % endif } <%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_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 properties::longhands::${ident}::computed_value::T; use euclid::Size2D; 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}"); T(Size2D::new(width, height)) } % 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 live in an nsFont member in Gecko. Should be straightforward to do manually. force_stub += ["font-variant"] # These have unusual representations in gecko. force_stub += ["list-style-type"] # These are part of shorthands so we must include them in stylo builds, # but we haven't implemented the stylo glue for the longhand # so we generate a stub force_stub += ["flex-basis", # position # transition "transition-duration", "transition-timing-function", "transition-property", "transition-delay", ] # Types used with predefined_type()-defined properties that we can auto-generate. predefined_types = { "length::LengthOrAuto": impl_style_coord, "Length": impl_style_coord, "LengthOrPercentage": impl_style_coord, "LengthOrPercentageOrAuto": impl_style_coord, "LengthOrPercentageOrNone": impl_style_coord, "LengthOrNone": impl_style_coord, "Number": impl_simple, "Opacity": impl_simple, "CSSColor": impl_color, } 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) { if cfg!(debug_assertions) { println!("stylo: Unimplemented property setter: ${longhand.name}"); } } #[allow(non_snake_case)] pub fn copy_${longhand.ident}_from(&mut self, _: &Self) { if cfg!(debug_assertions) { println!("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]) %> <%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_additionals="*"> % for side in SIDES: <% impl_keyword("border_%s_style" % side.ident, "mBorderStyle[%s]" % side.index, border_style_keyword, need_clone=True) %> <% 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, need_clone=True, round_to_pixels=True) %> pub fn border_${side.ident}_has_nonzero_width(&self) -> bool { self.gecko.mComputedBorder.${side.ident} != 0 } % 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 { // TODO: We need to make border-image-source match with background-image // until then we are setting with_url to false self.gecko.mBorderImageSource.set(image, false, &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}; use properties::longhands::border_image_slice::computed_value::PercentageOrNumber; for (i, corner) in v.corners.iter().enumerate() { match *corner { PercentageOrNumber::Percentage(p) => { self.gecko.mBorderImageSlice.data_at_mut(i).set_value(CoordDataValue::Percent(p.0)) }, PercentageOrNumber::Number(n) => { self.gecko.mBorderImageSlice.data_at_mut(i).set_value(CoordDataValue::Factor(n)) }, } } 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"> % 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) { use properties::longhands::z_index::computed_value::T; match v { T::Auto => self.gecko.mZIndex.set_value(CoordDataValue::Auto), T::Number(n) => self.gecko.mZIndex.set_value(CoordDataValue::Integer(n)), } } 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 { use properties::longhands::z_index::computed_value::T; return match self.gecko.mZIndex.as_value() { CoordDataValue::Auto => T::Auto, CoordDataValue::Integer(n) => T::Number(n), _ => { debug_assert!(false); T::Number(0) } } } 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 nsstring::nsCString; use gecko_bindings::structs::{nsStyleGridLine_kMinLine, nsStyleGridLine_kMaxLine}; let ident = v.ident.unwrap_or(String::new()); self.gecko.${value.gecko}.mLineName.assign_utf8(&nsCString::from(&*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 <% 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="*"> <% impl_keyword("outline_style", "mOutlineStyle", border_style_keyword, need_clone=True) %> <% impl_app_units("outline_width", "mActualOutlineWidth", 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 } <%self:impl_trait style_struct_name="Font" skip_longhands="font-family font-size font-size-adjust font-weight font-synthesis" 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 name) => { unsafe { Gecko_FontFamilyList_AppendNamed(list, name.0.as_ptr()); } } FontFamily::Generic(ref name) => { let family_type = if name == &atom!("serif") { FontFamilyType::eFamily_serif } else if name == &atom!("sans-serif") { FontFamilyType::eFamily_sans_serif } else if name == &atom!("cursive") { FontFamilyType::eFamily_cursive } else if name == &atom!("fantasy") { FontFamilyType::eFamily_fantasy } else if name == &atom!("monospace") { FontFamilyType::eFamily_monospace } else { panic!("Unknown generic font family") }; 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; } pub fn copy_font_size_from(&mut self, other: &Self) { self.gecko.mFont.size = other.gecko.mFont.size; self.gecko.mSize = other.gecko.mSize; } 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) } } // This is used for PartialEq, which we don't implement for gecko style structs. pub fn compute_font_hash(&mut self) {} 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.0 as f32, } } 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; use values::specified::Number; match self.gecko.mFont.sizeAdjust { -1.0 => T::None, _ => T::Number(Number(self.gecko.mFont.sizeAdjust)), } } <%def name="impl_copy_animation_value(ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn copy_animation_${ident}_from(&mut self, other: &Self) { unsafe { self.gecko.mAnimations.ensure_len(other.gecko.mAnimations.len()) }; let count = other.gecko.mAnimation${gecko_ffi_name}Count; self.gecko.mAnimation${gecko_ffi_name}Count = 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.m${gecko_ffi_name} = other.gecko.mAnimations[index].m${gecko_ffi_name}; } } <%def name="impl_animation_count(ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn animation_${ident}_count(&self) -> usize { self.gecko.mAnimation${gecko_ffi_name}Count as usize } <%def name="impl_animation_time_value(ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn set_animation_${ident}(&mut self, v: longhands::animation_${ident}::computed_value::T) { assert!(v.0.len() > 0); unsafe { self.gecko.mAnimations.ensure_len(v.0.len()) }; self.gecko.mAnimation${gecko_ffi_name}Count = v.0.len() as u32; for (servo, gecko) in v.0.into_iter().zip(self.gecko.mAnimations.iter_mut()) { gecko.m${gecko_ffi_name} = servo.seconds() * 1000.; } } #[allow(non_snake_case)] pub fn animation_${ident}_at(&self, index: usize) -> longhands::animation_${ident}::computed_value::SingleComputedValue { use values::specified::Time; Time(self.gecko.mAnimations[index].m${gecko_ffi_name} / 1000.) } ${impl_animation_count(ident, gecko_ffi_name)} ${impl_copy_animation_value(ident, gecko_ffi_name)} <%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; assert!(v.0.len() > 0); unsafe { self.gecko.mAnimations.ensure_len(v.0.len()) }; self.gecko.mAnimation${gecko_ffi_name}Count = v.0.len() as u32; for (servo, gecko) in v.0.into_iter().zip(self.gecko.mAnimations.iter_mut()) { let result = match servo { % 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 -moz-binding page-break-before page-break-after scroll-snap-points-x scroll-snap-points-y transform scroll-snap-type-y perspective-origin transform-origin""" %> <%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; } pub fn copy_display_from(&mut self, other: &Self) { self.gecko.mDisplay = other.gecko.mDisplay; self.gecko.mOriginalDisplay = other.gecko.mOriginalDisplay; } <%call expr="impl_keyword_clone('display', 'mDisplay', display_keyword)"> // overflow-y is implemented as a newtype of overflow-x, so we need special handling. // We could generalize this if we run into other newtype keywords. <% 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.0 { % 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; use properties::longhands::overflow_y::computed_value::T as NewType; // 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)} => NewType(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')"> #[allow(non_snake_case)] pub fn set__moz_binding(&mut self, v: longhands::_moz_binding::computed_value::T) { use values::Either; match v { Either::Second(_none) => debug_assert!(self.gecko.mBinding.mRawPtr.is_null()), Either::First(ref url) => { let extra_data = url.extra_data(); let (ptr, len) = match url.as_slice_components() { Ok(value) => value, Err(_) => (ptr::null(), 0), }; unsafe { Gecko_SetMozBinding(&mut self.gecko, ptr, len as u32, extra_data.base.get(), extra_data.referrer.get(), extra_data.principal.get()); } } } } #[allow(non_snake_case)] pub fn copy__moz_binding_from(&mut self, other: &Self) { unsafe { Gecko_CopyMozBindingFrom(&mut self.gecko, &other.gecko); } } // 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')} <%def name="transform_function_arm(name, keyword, items)"> <% pattern = None if name == "matrix": # m11, m12, m13, .. indices = [str(i) + str(j) for i in range(1, 5) for j in range(1, 5)] # m11: number1, m12: number2, .. single_patterns = ["m%s: number%s" % (index, i + 1) for (i, index) in enumerate(indices)] pattern = "ComputedMatrix { %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" : "bindings::Gecko_CSSValue_SetAngle(%s, %s.0)", "number" : "bindings::Gecko_CSSValue_SetNumber(%s, %s)", } %> ComputedOperation::${name.title()}(${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 set_transform(&mut self, other: longhands::transform::computed_value::T) { use gecko_bindings::structs::nsCSSKeyword::*; use gecko_bindings::sugar::refptr::RefPtr; use properties::longhands::transform::computed_value::ComputedMatrix; use properties::longhands::transform::computed_value::ComputedOperation; let vec = if let Some(v) = other.0 { v } else { unsafe { self.gecko.mSpecifiedTransform.clear(); } return; }; let list = unsafe { RefPtr::from_addrefed(bindings::Gecko_NewCSSValueSharedList(vec.len() as u32)) }; let mut cur = list.mHead; let mut iter = vec.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("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 { self.gecko.mSpecifiedTransform.set_move(list) }; } 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" : "Angle(bindings::Gecko_CSSValue_GetAngle(%s))", "number" : "bindings::Gecko_CSSValue_GetNumber(%s)", } %> eCSSKeyword_${keyword} => { ComputedOperation::${name.title()}( % if name == "matrix": ComputedMatrix { % endif % for index, item in enumerate(items): % if name == "matrix": m${index / 4 + 1}${index % 4 + 1}: % endif ${css_value_getters[item] % ( "bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, %d)" % (index + 1) )}, % endfor % if name == "matrix": } % 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; use values::computed::Angle; 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)) } pub fn set_animation_name(&mut self, v: longhands::animation_name::computed_value::T) { use nsstring::nsCString; unsafe { self.gecko.mAnimations.ensure_len(v.0.len()) }; if v.0.len() > 0 { self.gecko.mAnimationNameCount = v.0.len() as u32; for (servo, gecko) in v.0.into_iter().zip(self.gecko.mAnimations.iter_mut()) { gecko.mName.assign_utf8(&nsCString::from(servo.0.to_string())); } } else { unsafe { self.gecko.mAnimations[0].mName.truncate(); } } } pub fn animation_name_at(&self, index: usize) -> longhands::animation_name::computed_value::SingleComputedValue { use Atom; use properties::longhands::animation_name::single_value::SpecifiedValue as AnimationName; // XXX: Is there any effective ways? AnimationName(Atom::from(String::from_utf16_lossy(&self.gecko.mAnimations[index].mName[..]))) } 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; assert!(v.0.len() > 0); unsafe { self.gecko.mAnimations.ensure_len(v.0.len()) }; self.gecko.mAnimationIterationCountCount = v.0.len() as u32; for (servo, gecko) in v.0.into_iter().zip(self.gecko.mAnimations.iter_mut()) { match servo { 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')} pub fn set_animation_timing_function(&mut self, v: longhands::animation_timing_function::computed_value::T) { assert!(v.0.len() > 0); unsafe { self.gecko.mAnimations.ensure_len(v.0.len()) }; self.gecko.mAnimationTimingFunctionCount = v.0.len() as u32; for (servo, gecko) in v.0.into_iter().zip(self.gecko.mAnimations.iter_mut()) { gecko.mTimingFunction = servo.into(); } } ${impl_animation_count('timing_function', 'TimingFunction')} ${impl_copy_animation_value('timing_function', 'TimingFunction')} pub fn animation_timing_function_at(&self, index: usize) -> longhands::animation_timing_function::computed_value::SingleComputedValue { self.gecko.mAnimations[index].mTimingFunction.into() } <% 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 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"), } } <%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::T; 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; let (repeat_x, repeat_y) = match servo { T::repeat_x => (NS_STYLE_IMAGELAYER_REPEAT_REPEAT, NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT), T::repeat_y => (NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT, NS_STYLE_IMAGELAYER_REPEAT_REPEAT), T::repeat => (NS_STYLE_IMAGELAYER_REPEAT_REPEAT, NS_STYLE_IMAGELAYER_REPEAT_REPEAT), T::space => (NS_STYLE_IMAGELAYER_REPEAT_SPACE, NS_STYLE_IMAGELAYER_REPEAT_SPACE), T::round => (NS_STYLE_IMAGELAYER_REPEAT_ROUND, NS_STYLE_IMAGELAYER_REPEAT_ROUND), T::no_repeat => (NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT, NS_STYLE_IMAGELAYER_REPEAT_NO_REPEAT), }; 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::Border, T::padding_box => StyleGeometryBox::Padding, T::content_box => StyleGeometryBox::Content, % if shorthand == "mask": T::fill_box => StyleGeometryBox::Fill, T::stroke_box => StyleGeometryBox::Stroke, T::view_box => StyleGeometryBox::View, 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::Border, T::padding_box => StyleGeometryBox::Padding, T::content_box => StyleGeometryBox::Content, % if shorthand == "mask": T::fill_box => StyleGeometryBox::Fill, T::stroke_box => StyleGeometryBox::Stroke, T::view_box => StyleGeometryBox::View, % 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::computed::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, true, cacheable) } % else: use properties::longhands::mask_image::single_value::computed_value::T; match image { T::Image(image) => geckoimage.mImage.set(image, false, cacheable), _ => () // we need to support url valeus } % 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 // XXXManishearth Gecko does an optimization here where it only // fills things in if any of the properties have been set 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" 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) => { let (ptr, len) = match url.as_slice_components() { Ok(value) | Err(value) => value }; let extra_data = url.extra_data(); unsafe { Gecko_SetListStyleImage(&mut self.gecko, ptr, len as u32, extra_data.base.get(), extra_data.referrer.get(), extra_data.principal.get()); } // 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); } } ${impl_keyword_setter("list_style_type", "__LIST_STYLE_TYPE__", data.longhands_by_name["list-style-type"].keyword)} 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; use nsstring::nsCString; 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(&nsCString::from(&*servo.0)); gecko.second.assign_utf8(&nsCString::from(&*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); } } <%self:impl_trait style_struct_name="Effects" skip_longhands="box-shadow filter"> pub fn set_box_shadow(&mut self, v: longhands::box_shadow::computed_value::T) { use cssparser::Color; 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 { use cssparser::Color; 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_filter(&mut self, v: longhands::filter::computed_value::T) { use cssparser::Color; 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::Radian(angle.radians()), 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, }; } } } } 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="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; } <%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_align_keyword = Keyword("text-align", "start end left right center justify -moz-center -moz-left " + "-moz-right match-parent") %> ${impl_keyword('text_align', 'mTextAlign', text_align_keyword, need_clone=False)} pub fn set_text_shadow(&mut self, v: longhands::text_shadow::computed_value::T) { use cssparser::Color; 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 { use cssparser::Color; 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) } } <%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)), } } <%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_style(&mut self, v: longhands::text_emphasis_style::computed_value::T) { use nsstring::nsCString; 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(&nsCString::from(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)"> <%self:impl_trait style_struct_name="Text" skip_longhands="text-decoration-line text-overflow" 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.underline { bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE as u8; } if v.overline { bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_OVERLINE as u8; } if v.line_through { bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH as u8; } if v.blink { bits |= structs::NS_STYLE_TEXT_DECORATION_LINE_BLINK 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) { use nsstring::nsCString; 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(&nsCString::from(&**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; } #[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 } <% 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, } pub fn set_clip_path(&mut self, v: longhands::clip_path::computed_value::T) { use gecko_bindings::bindings::{Gecko_NewBasicShape, Gecko_DestroyClipPath}; use gecko_bindings::structs::StyleGeometryBox; use gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType, StyleShapeSourceType}; use gecko_bindings::structs::{StyleClipPath, StyleFillRule}; use gecko::conversions::basic_shape::set_corners_from_radius; use gecko::values::GeckoStyleCoordConvertible; use values::computed::basic_shape::*; let ref mut clip_path = self.gecko.mClipPath; // clean up existing struct unsafe { Gecko_DestroyClipPath(clip_path) }; clip_path.mType = StyleShapeSourceType::None; match v { ShapeSource::Url(..) => println!("stylo: clip-path: url() not yet implemented"), ShapeSource::None => {} // don't change the type ShapeSource::Box(reference) => { clip_path.mReferenceBox = reference.into(); clip_path.mType = StyleShapeSourceType::Box; } ShapeSource::Shape(servo_shape, maybe_box) => { clip_path.mReferenceBox = maybe_box.map(Into::into) .unwrap_or(StyleGeometryBox::NoBox); clip_path.mType = StyleShapeSourceType::Shape; fn init_shape(clip_path: &mut StyleClipPath, ty: StyleBasicShapeType) -> &mut StyleBasicShape { unsafe { // We have to be very careful to avoid a copy here! let ref mut union = clip_path.__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(clip_path, 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(clip_path, 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(clip_path, 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(clip_path, 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_clip_path_from(&mut self, other: &Self) { use gecko_bindings::bindings::Gecko_CopyClipPathValueFrom; unsafe { Gecko_CopyClipPathValueFrom(&mut self.gecko.mClipPath, &other.gecko.mClipPath); } } pub fn clone_clip_path(&self) -> longhands::clip_path::computed_value::T { use gecko_bindings::structs::StyleShapeSourceType; use gecko_bindings::structs::StyleGeometryBox; use values::computed::basic_shape::*; let ref clip_path = self.gecko.mClipPath; match clip_path.mType { StyleShapeSourceType::None => ShapeSource::None, StyleShapeSourceType::Box => { ShapeSource::Box(clip_path.mReferenceBox.into()) } StyleShapeSourceType::URL => { warn!("stylo: clip-path: url() not implemented yet"); Default::default() } StyleShapeSourceType::Shape => { let reference = if let StyleGeometryBox::NoBox = clip_path.mReferenceBox { None } else { Some(clip_path.mReferenceBox.into()) }; let union = clip_path.__bindgen_anon_1; let shape = unsafe { &**union.mBasicShape.as_ref() }; ShapeSource::Shape(shape.into(), reference) } } } <%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"> 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]; let extra_data = image.url.extra_data(); let (ptr, len) = match image.url.as_slice_components() { Ok(value) | Err(value) => value, }; unsafe { Gecko_SetCursorImage(&mut self.gecko.mCursorImages[i], ptr, len as u32, extra_data.base.get(), extra_data.referrer.get(), extra_data.principal.get()); } // 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); } } <%self:impl_trait style_struct_name="Column" skip_longhands="column-count column-gap 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.0 { Some(number) => unsafe { cmp::min(number, nsStyleColumn_kMaxColumnCount) }, None => NS_STYLE_COLUMN_COUNT_AUTO }; } ${impl_simple_copy('column_count', 'mColumnCount')} pub fn set_column_gap(&mut self, v: longhands::column_gap::computed_value::T) { use values::Either; match v { Either::First(len) => self.gecko.mColumnGap.set(len), Either::Second(_normal) => self.gecko.mColumnGap.set_value(CoordDataValue::Normal), } } <%call expr="impl_coord_copy('column_gap', 'mColumnGap')"> <% impl_app_units("column_rule_width", "mColumnRuleWidth", need_clone=True, round_to_pixels=True) %> <%self:impl_trait style_struct_name="Counters" skip_longhands="content"> 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 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() { // TODO: Servo lacks support for attr(), and URIs. // NB: Gecko compares the mString value if type is not image // or URI independently of whatever gets there. In the quote // cases, they set it to null, so do the same here. 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::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::Counter(..) | ContentItem::Counters(..) => self.gecko.mContents[i].mType = eStyleContentType_Uninitialized, } } } } } pub fn copy_content_from(&mut self, other: &Self) { use gecko_bindings::bindings::Gecko_CopyStyleContentsFrom; unsafe { Gecko_CopyStyleContentsFrom(&mut self.gecko, &other.gecko) } } <%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 lazy_static! { static ref EMPTY_VARIABLES_STRUCT: nsStyleVariables = { unsafe { let mut variables: nsStyleVariables = unsafe { zeroed() }; Gecko_Construct_nsStyleVariables(&mut variables); variables } }; } #[no_mangle] #[allow(non_snake_case)] pub unsafe extern "C" fn Servo_GetStyleVariables(_cv: ServoComputedValuesBorrowedOrNull) -> *const nsStyleVariables { &*EMPTY_VARIABLES_STRUCT }