/* 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, to_camel_case_lower from data import Keyword %> <%namespace name="helpers" file="/helpers.mako.rs" /> use app_units::Au; use custom_properties::CustomPropertiesMap; 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_CopyCounterStyle; 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_EnsureImageLayersLength; use gecko_bindings::bindings::Gecko_SetCursorArrayLength; use gecko_bindings::bindings::Gecko_SetCursorImageValue; use gecko_bindings::bindings::Gecko_StyleTransition_SetUnsupportedProperty; use gecko_bindings::bindings::Gecko_NewCSSShadowArray; use gecko_bindings::bindings::Gecko_nsStyleFont_SetLang; use gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom; use gecko_bindings::bindings::Gecko_SetListStyleImageNone; use gecko_bindings::bindings::Gecko_SetListStyleImageImageValue; use gecko_bindings::bindings::Gecko_SetNullImageValue; use gecko_bindings::bindings::{Gecko_ResetFilters, Gecko_CopyFiltersFrom}; use gecko_bindings::bindings::RawGeckoPresContextBorrowed; use gecko_bindings::structs; use gecko_bindings::structs::nsCSSPropertyID; use gecko_bindings::structs::mozilla::CSSPseudoElementType; use gecko_bindings::structs::mozilla::CSSPseudoElementType_InheritingAnonBox; use gecko_bindings::structs::root::NS_STYLE_CONTEXT_TYPE_SHIFT; use gecko_bindings::sugar::ns_style_coord::{CoordDataValue, CoordData, CoordDataMut}; 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 media_queries::Device; use properties::animated_properties::TransitionProperty; use properties::computed_value_flags::*; use properties::{longhands, Importance, LonghandId}; use properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyDeclarationId}; use rule_tree::StrongRuleNode; use selector_parser::PseudoElement; use servo_arc::{Arc, RawOffsetArc}; use std::mem::{forget, uninitialized, transmute, zeroed}; use std::{cmp, ops, ptr}; use values::{self, Auto, CustomIdent, Either, KeyframesName, None_}; use values::computed::{NonNegativeLength, ToComputedValue, Percentage}; use values::computed::effects::{BoxShadow, Filter, SimpleShadow}; use computed_values::border_style; pub mod style_structs { % for style_struct in data.style_structs: pub use super::${style_struct.gecko_struct_name} as ${style_struct.name}; % endfor } /// FIXME(emilio): This is completely duplicated with the other properties code. pub type ComputedValuesInner = ::gecko_bindings::structs::ServoComputedData; #[derive(Debug)] #[repr(C)] pub struct ComputedValues(::gecko_bindings::structs::mozilla::ServoStyleContext); impl ComputedValues { pub fn new( device: &Device, parent: Option<<&ComputedValues>, pseudo: Option<<&PseudoElement>, custom_properties: Option>, writing_mode: WritingMode, flags: ComputedValueFlags, rules: Option, visited_style: Option>, % for style_struct in data.style_structs: ${style_struct.ident}: Arc, % endfor ) -> Arc { ComputedValuesInner::new( custom_properties, writing_mode, flags, rules, visited_style, % for style_struct in data.style_structs: ${style_struct.ident}, % endfor ).to_outer( device.pres_context(), parent, pseudo.map(|p| p.pseudo_info()) ) } pub fn default_values(pres_context: RawGeckoPresContextBorrowed) -> Arc { ComputedValuesInner::new( /* custom_properties = */ None, /* writing_mode = */ WritingMode::empty(), // FIXME(bz): This seems dubious ComputedValueFlags::empty(), /* rules = */ None, /* visited_style = */ None, % for style_struct in data.style_structs: style_structs::${style_struct.name}::default(pres_context), % endfor ).to_outer(pres_context, None, None) } pub fn pseudo(&self) -> Option { use string_cache::Atom; let atom = (self.0)._base.mPseudoTag.mRawPtr; if atom.is_null() { return None; } let atom = Atom::from(atom); PseudoElement::from_atom(&atom) } fn get_pseudo_type(&self) -> CSSPseudoElementType { let bits = (self.0)._base.mBits; let our_type = bits >> NS_STYLE_CONTEXT_TYPE_SHIFT; unsafe { transmute(our_type as u8) } } pub fn is_anon_box(&self) -> bool { let our_type = self.get_pseudo_type(); return our_type == CSSPseudoElementType_InheritingAnonBox || our_type == CSSPseudoElementType::NonInheritingAnonBox; } } impl Drop for ComputedValues { fn drop(&mut self) { unsafe { bindings::Gecko_ServoStyleContext_Destroy(&mut self.0); } } } unsafe impl Sync for ComputedValues {} unsafe impl Send for ComputedValues {} impl Clone for ComputedValues { fn clone(&self) -> Self { unreachable!() } } impl Clone for ComputedValuesInner { fn clone(&self) -> Self { ComputedValuesInner { % for style_struct in data.style_structs: ${style_struct.gecko_name}: self.${style_struct.gecko_name}.clone(), % endfor custom_properties: self.custom_properties.clone(), writing_mode: self.writing_mode.clone(), flags: self.flags.clone(), rules: self.rules.clone(), visited_style: self.visited_style.clone(), } } } type PseudoInfo = (*mut structs::nsAtom, structs::CSSPseudoElementType); type ParentStyleContextInfo<'a> = Option< &'a ComputedValues>; impl ComputedValuesInner { pub fn new(custom_properties: Option>, writing_mode: WritingMode, flags: ComputedValueFlags, rules: Option, visited_style: Option>, % for style_struct in data.style_structs: ${style_struct.ident}: Arc, % endfor ) -> Self { ComputedValuesInner { custom_properties: custom_properties, writing_mode: writing_mode, rules: rules, visited_style: visited_style.map(|x| Arc::into_raw_offset(x)), flags: flags, % for style_struct in data.style_structs: ${style_struct.gecko_name}: Arc::into_raw_offset(${style_struct.ident}), % endfor } } fn to_outer( self, pres_context: RawGeckoPresContextBorrowed, parent: ParentStyleContextInfo, info: Option ) -> Arc { let (tag, ty) = if let Some(info) = info { info } else { (ptr::null_mut(), structs::CSSPseudoElementType::NotPseudo) }; unsafe { self.to_outer_helper(pres_context, parent, ty, tag) } } unsafe fn to_outer_helper( self, pres_context: bindings::RawGeckoPresContextBorrowed, parent: ParentStyleContextInfo, pseudo_ty: structs::CSSPseudoElementType, pseudo_tag: *mut structs::nsAtom ) -> Arc { let arc = unsafe { let arc: Arc = Arc::new(uninitialized()); bindings::Gecko_ServoStyleContext_Init(&arc.0 as *const _ as *mut _, parent, pres_context, &self, pseudo_ty, pseudo_tag); // We're simulating a move by having C++ do a memcpy and then forgetting // it on this end. forget(self); arc }; arc } } impl ops::Deref for ComputedValues { type Target = ComputedValuesInner; fn deref(&self) -> &ComputedValuesInner { &self.0.mSource } } impl ops::DerefMut for ComputedValues { fn deref_mut(&mut self) -> &mut ComputedValuesInner { &mut self.0.mSource } } impl ComputedValuesInner { /// Clone the visited style. Used for inheriting parent styles in /// StyleBuilder::for_derived_style. pub fn clone_visited_style(&self) -> Option> { self.visited_style.as_ref().map(|x| x.clone_arc()) } #[inline] pub fn is_display_contents(&self) -> bool { self.get_box().clone_display() == longhands::display::computed_value::T::contents } /// Returns true if the value of the `content` property would make a /// pseudo-element not rendered. #[inline] pub fn ineffective_content_property(&self) -> bool { self.get_counters().ineffective_content_property() } % for style_struct in data.style_structs: #[inline] pub fn clone_${style_struct.name_lower}(&self) -> Arc { Arc::from_raw_offset(self.${style_struct.gecko_name}.clone()) } #[inline] pub fn get_${style_struct.name_lower}(&self) -> &style_structs::${style_struct.name} { &self.${style_struct.gecko_name} } pub fn ${style_struct.name_lower}_arc(&self) -> &RawOffsetArc { &self.${style_struct.gecko_name} } #[inline] pub fn mutate_${style_struct.name_lower}(&mut self) -> &mut style_structs::${style_struct.name} { RawOffsetArc::make_mut(&mut self.${style_struct.gecko_name}) } % endfor /// Gets the raw visited style. Useful for memory reporting. pub fn get_raw_visited_style(&self) -> &Option> { &self.visited_style } #[allow(non_snake_case)] pub fn has_moz_binding(&self) -> bool { !self.get_box().gecko.mBinding.mPtr.mRawPtr.is_null() } // FIXME(bholley): Implement this properly. #[inline] pub fn is_multicol(&self) -> bool { false } pub fn to_declaration_block(&self, property: PropertyDeclarationId) -> PropertyDeclarationBlock { let value = match property { % for prop in data.longhands: % if prop.animatable: PropertyDeclarationId::Longhand(LonghandId::${prop.camel_case}) => { PropertyDeclaration::${prop.camel_case}( % if prop.boxed: Box::new( % endif longhands::${prop.ident}::SpecifiedValue::from_computed_value( &self.get_${prop.style_struct.ident.strip("_")}().clone_${prop.ident}()) % if prop.boxed: ) % endif ) }, % endif % endfor PropertyDeclarationId::Custom(_name) => unimplemented!(), _ => unimplemented!() }; PropertyDeclarationBlock::with_one(value, Importance::Normal) } } <%def name="declare_style_struct(style_struct)"> pub use ::gecko_bindings::structs::mozilla::Gecko${style_struct.gecko_name} as ${style_struct.gecko_struct_name}; impl ${style_struct.gecko_struct_name} { pub fn gecko(&self) -> &${style_struct.gecko_ffi_name} { &self.gecko } pub fn gecko_mut(&mut self) -> &mut ${style_struct.gecko_ffi_name} { &mut self.gecko } } <%def name="impl_simple_setter(ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { ${set_gecko_property(gecko_ffi_name, "From::from(v)")} } <%def name="impl_simple_clone(ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { From::from(self.gecko.${gecko_ffi_name}) } <%def name="impl_simple_copy(ident, gecko_ffi_name, on_set=None, *kwargs)"> #[allow(non_snake_case)] pub fn copy_${ident}_from(&mut self, other: &Self) { self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name}; % if on_set: self.${on_set}(); % endif } #[allow(non_snake_case)] pub fn reset_${ident}(&mut self, other: &Self) { self.copy_${ident}_from(other) } <%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}); } #[allow(non_snake_case)] pub fn reset_${ident}(&mut self, other: &Self) { self.copy_${ident}_from(other) } <%! def get_gecko_property(ffi_name, self_param = "self"): if "mBorderColor" in ffi_name: return ffi_name.replace("mBorderColor", "unsafe { *%s.gecko.__bindgen_anon_1.mBorderColor.as_ref() }" % self_param) return "%s.gecko.%s" % (self_param, ffi_name) def set_gecko_property(ffi_name, expr): if "mBorderColor" in ffi_name: ffi_name = ffi_name.replace("mBorderColor", "*self.gecko.__bindgen_anon_1.mBorderColor.as_mut()") return "unsafe { %s = %s };" % (ffi_name, expr) return "self.gecko.%s = %s;" % (ffi_name, expr) %> <%def name="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type='u8', on_set=None)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { use properties::longhands::${ident}::computed_value::T as Keyword; // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts let result = match v { % for value in keyword.values_for('gecko'): Keyword::${to_rust_ident(value)} => structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast(cast_type)}, % endfor }; ${set_gecko_property(gecko_ffi_name, "result")} % if on_set: self.${on_set}(); % endif } <%def name="impl_keyword_clone(ident, gecko_ffi_name, keyword, cast_type='u8')"> // FIXME: We introduced non_upper_case_globals for -moz-appearance only // since the prefix of Gecko value starts with ThemeWidgetType_NS_THEME. // We should remove this after fix bug 1371809. #[allow(non_snake_case, non_upper_case_globals)] 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 // Some constant macros in the gecko are defined as negative integer(e.g. font-stretch). // And they are convert to signed integer in Rust bindings. We need to cast then // as signed type when we have both signed/unsigned integer in order to use them // as match's arms. // Also, to use same implementation here we use casted constant if we have only singed values. % if keyword.gecko_enum_prefix is None: % for value in keyword.values_for('gecko'): const ${keyword.casted_constant_name(value, cast_type)} : ${cast_type} = structs::${keyword.gecko_constant(value)} as ${cast_type}; % endfor match ${get_gecko_property(gecko_ffi_name)} as ${cast_type} { % for value in keyword.values_for('gecko'): ${keyword.casted_constant_name(value, cast_type)} => Keyword::${to_rust_ident(value)}, % endfor % if keyword.gecko_inexhaustive: x => panic!("Found unexpected value in style struct for ${ident} property: {:?}", x), % endif } % else: match ${get_gecko_property(gecko_ffi_name)} { % 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 } % endif } <%def name="impl_color_setter(ident, gecko_ffi_name)"> #[allow(unreachable_code)] #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { ${set_gecko_property(gecko_ffi_name, "v.into()")} } <%def name="impl_color_copy(ident, gecko_ffi_name)"> #[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")}; } #[allow(non_snake_case)] pub fn reset_${ident}(&mut self, other: &Self) { self.copy_${ident}_from(other) } <%def name="impl_color_clone(ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { ${get_gecko_property(gecko_ffi_name)}.into() } <%def name="impl_keyword(ident, gecko_ffi_name, keyword, cast_type='u8', **kwargs)"> <%call expr="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type, **kwargs)"> <%call expr="impl_simple_copy(ident, gecko_ffi_name, **kwargs)"> <%call expr="impl_keyword_clone(ident, gecko_ffi_name, keyword, cast_type)"> <%def name="impl_simple(ident, gecko_ffi_name)"> <%call expr="impl_simple_setter(ident, gecko_ffi_name)"> <%call expr="impl_simple_copy(ident, gecko_ffi_name)"> <%call expr="impl_simple_clone(ident, gecko_ffi_name)"> <%def name="impl_absolute_length(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.to_i32_au()")} } <%call expr="impl_simple_copy(ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { Au(self.gecko.${gecko_ffi_name}).into() } <%def name="impl_position(ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { ${set_gecko_property("%s.mXPosition" % gecko_ffi_name, "v.horizontal.into()")} ${set_gecko_property("%s.mYPosition" % gecko_ffi_name, "v.vertical.into()")} } <%call expr="impl_simple_copy(ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { longhands::${ident}::computed_value::T { horizontal: self.gecko.${gecko_ffi_name}.mXPosition.into(), vertical: self.gecko.${gecko_ffi_name}.mYPosition.into(), } } <%def name="impl_color(ident, gecko_ffi_name)"> <%call expr="impl_color_setter(ident, gecko_ffi_name)"> <%call expr="impl_color_copy(ident, gecko_ffi_name)"> <%call expr="impl_color_clone(ident, gecko_ffi_name)"> <%def name="impl_rgba_color(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, "convert_rgba_to_nscolor(&v)")} } <%call expr="impl_simple_copy(ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { convert_nscolor_to_rgba(${get_gecko_property(gecko_ffi_name)}) } <%def name="impl_svg_length(ident, gecko_ffi_name)"> // When context-value is used on an SVG length, the corresponding flag is // set on mContextFlags, and the length field is set to the initial value. pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { use values::generics::svg::{SVGLength, SvgLengthOrPercentageOrNumber}; use gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE; let length = match v { SVGLength::Length(length) => { self.gecko.mContextFlags &= !CONTEXT_VALUE; length } SVGLength::ContextValue => { self.gecko.mContextFlags |= CONTEXT_VALUE; match longhands::${ident}::get_initial_value() { SVGLength::Length(length) => length, _ => unreachable!("Initial value should not be context-value"), } } }; match length { SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop) => self.gecko.${gecko_ffi_name}.set(lop), SvgLengthOrPercentageOrNumber::Number(num) => self.gecko.${gecko_ffi_name}.set_value(CoordDataValue::Factor(num.into())), } } pub fn copy_${ident}_from(&mut self, other: &Self) { use gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE; self.gecko.${gecko_ffi_name}.copy_from(&other.gecko.${gecko_ffi_name}); self.gecko.mContextFlags = (self.gecko.mContextFlags & !CONTEXT_VALUE) | (other.gecko.mContextFlags & CONTEXT_VALUE); } pub fn reset_${ident}(&mut self, other: &Self) { self.copy_${ident}_from(other) } pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { use values::generics::svg::{SVGLength, SvgLengthOrPercentageOrNumber}; use values::computed::LengthOrPercentage; use gecko_bindings::structs::nsStyleSVG_${ident.upper()}_CONTEXT as CONTEXT_VALUE; if (self.gecko.mContextFlags & CONTEXT_VALUE) != 0 { return SVGLength::ContextValue; } let length = match self.gecko.${gecko_ffi_name}.as_value() { CoordDataValue::Factor(number) => SvgLengthOrPercentageOrNumber::Number(number), CoordDataValue::Coord(coord) => SvgLengthOrPercentageOrNumber::LengthOrPercentage( LengthOrPercentage::Length(Au(coord).into())), CoordDataValue::Percent(p) => SvgLengthOrPercentageOrNumber::LengthOrPercentage( LengthOrPercentage::Percentage(Percentage(p))), CoordDataValue::Calc(calc) => SvgLengthOrPercentageOrNumber::LengthOrPercentage( LengthOrPercentage::Calc(calc.into())), _ => unreachable!("Unexpected coordinate {:?} in ${ident}", self.gecko.${gecko_ffi_name}.as_value()), }; SVGLength::Length(length.into()) } <%def name="impl_svg_opacity(ident, gecko_ffi_name)"> <% source_prefix = ident.split("_")[0].upper() + "_OPACITY_SOURCE" %> pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { use gecko_bindings::structs::nsStyleSVG_${source_prefix}_MASK as MASK; use gecko_bindings::structs::nsStyleSVG_${source_prefix}_SHIFT as SHIFT; use gecko_bindings::structs::nsStyleSVGOpacitySource::*; use values::generics::svg::SVGOpacity; self.gecko.mContextFlags &= !MASK; match v { SVGOpacity::Opacity(opacity) => { self.gecko.mContextFlags |= (eStyleSVGOpacitySource_Normal as u8) << SHIFT; self.gecko.${gecko_ffi_name} = opacity; } SVGOpacity::ContextFillOpacity => { self.gecko.mContextFlags |= (eStyleSVGOpacitySource_ContextFillOpacity as u8) << SHIFT; self.gecko.${gecko_ffi_name} = 1.; } SVGOpacity::ContextStrokeOpacity => { self.gecko.mContextFlags |= (eStyleSVGOpacitySource_ContextStrokeOpacity as u8) << SHIFT; self.gecko.${gecko_ffi_name} = 1.; } } } pub fn copy_${ident}_from(&mut self, other: &Self) { use gecko_bindings::structs::nsStyleSVG_${source_prefix}_MASK as MASK; self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name}; self.gecko.mContextFlags = (self.gecko.mContextFlags & !MASK) | (other.gecko.mContextFlags & MASK); } pub fn reset_${ident}(&mut self, other: &Self) { self.copy_${ident}_from(other) } pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { use gecko_bindings::structs::nsStyleSVG_${source_prefix}_MASK as MASK; use gecko_bindings::structs::nsStyleSVG_${source_prefix}_SHIFT as SHIFT; use gecko_bindings::structs::nsStyleSVGOpacitySource::*; use values::generics::svg::SVGOpacity; let source = (self.gecko.mContextFlags & MASK) >> SHIFT; if source == eStyleSVGOpacitySource_Normal as u8 { return SVGOpacity::Opacity(self.gecko.${gecko_ffi_name}); } else { debug_assert_eq!(self.gecko.${gecko_ffi_name}, 1.0); if source == eStyleSVGOpacitySource_ContextFillOpacity as u8 { SVGOpacity::ContextFillOpacity } else { debug_assert_eq!(source, eStyleSVGOpacitySource_ContextStrokeOpacity as u8); SVGOpacity::ContextStrokeOpacity } } } <%def name="impl_svg_paint(ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, mut v: longhands::${ident}::computed_value::T) { use values::generics::svg::SVGPaintKind; use self::structs::nsStyleSVGPaintType; use self::structs::nsStyleSVGFallbackType; let ref mut paint = ${get_gecko_property(gecko_ffi_name)}; unsafe { bindings::Gecko_nsStyleSVGPaint_Reset(paint); } let fallback = v.fallback.take(); match v.kind { SVGPaintKind::None => return, SVGPaintKind::ContextFill => { paint.mType = nsStyleSVGPaintType::eStyleSVGPaintType_ContextFill; } SVGPaintKind::ContextStroke => { paint.mType = nsStyleSVGPaintType::eStyleSVGPaintType_ContextStroke; } SVGPaintKind::PaintServer(url) => { unsafe { bindings::Gecko_nsStyleSVGPaint_SetURLValue(paint, url.for_ffi()); } } SVGPaintKind::Color(color) => { paint.mType = nsStyleSVGPaintType::eStyleSVGPaintType_Color; unsafe { *paint.mPaint.mColor.as_mut() = convert_rgba_to_nscolor(&color); } } } paint.mFallbackType = match fallback { Some(Either::First(color)) => { paint.mFallbackColor = convert_rgba_to_nscolor(&color); nsStyleSVGFallbackType::eStyleSVGFallbackType_Color }, Some(Either::Second(_)) => { nsStyleSVGFallbackType::eStyleSVGFallbackType_None }, None => nsStyleSVGFallbackType::eStyleSVGFallbackType_NotSet }; } #[allow(non_snake_case)] pub fn copy_${ident}_from(&mut self, other: &Self) { unsafe { bindings::Gecko_nsStyleSVGPaint_CopyFrom( &mut ${get_gecko_property(gecko_ffi_name)}, & ${get_gecko_property(gecko_ffi_name, "other")} ); } } #[allow(non_snake_case)] pub fn reset_${ident}(&mut self, other: &Self) { self.copy_${ident}_from(other) } #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { use values::generics::svg::{SVGPaint, SVGPaintKind}; use values::specified::url::SpecifiedUrl; use self::structs::nsStyleSVGPaintType; use self::structs::nsStyleSVGFallbackType; let ref paint = ${get_gecko_property(gecko_ffi_name)}; let fallback = match paint.mFallbackType { nsStyleSVGFallbackType::eStyleSVGFallbackType_Color => { Some(Either::First(convert_nscolor_to_rgba(paint.mFallbackColor))) }, nsStyleSVGFallbackType::eStyleSVGFallbackType_None => { Some(Either::Second(None_)) }, nsStyleSVGFallbackType::eStyleSVGFallbackType_NotSet => None, }; let kind = match paint.mType { nsStyleSVGPaintType::eStyleSVGPaintType_None => SVGPaintKind::None, nsStyleSVGPaintType::eStyleSVGPaintType_ContextFill => SVGPaintKind::ContextFill, nsStyleSVGPaintType::eStyleSVGPaintType_ContextStroke => SVGPaintKind::ContextStroke, nsStyleSVGPaintType::eStyleSVGPaintType_Server => { unsafe { SVGPaintKind::PaintServer( SpecifiedUrl::from_url_value_data( &(**paint.mPaint.mPaintServer.as_ref())._base ).unwrap() ) } } nsStyleSVGPaintType::eStyleSVGPaintType_Color => { unsafe { SVGPaintKind::Color(convert_nscolor_to_rgba(*paint.mPaint.mColor.as_ref())) } } }; SVGPaint { kind: kind, fallback: fallback, } } <%def name="impl_non_negative_length(ident, gecko_ffi_name, inherit_from=None, round_to_pixels=False)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { let value = { % if round_to_pixels: let au_per_device_px = Au(self.gecko.mTwipsPerPixel); round_border_to_device_pixels(Au::from(v), au_per_device_px).0 % else: v.0.to_i32_au() % endif }; % if inherit_from: self.gecko.${inherit_from} = value; % endif self.gecko.${gecko_ffi_name} = value; } #[allow(non_snake_case)] pub fn copy_${ident}_from(&mut self, other: &Self) { % if inherit_from: self.gecko.${inherit_from} = other.gecko.${inherit_from}; // NOTE: This is needed to easily handle the `unset` and `initial` // keywords, which are implemented calling this function. // // In practice, this means that we may have an incorrect value here, but // we'll adjust that properly in the style fixup phase. // // FIXME(emilio): We could clean this up a bit special-casing the reset_ // function below. self.gecko.${gecko_ffi_name} = other.gecko.${inherit_from}; % else: self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name}; % endif } #[allow(non_snake_case)] pub fn reset_${ident}(&mut self, other: &Self) { self.copy_${ident}_from(other) } #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { Au(self.gecko.${gecko_ffi_name}).into() } <%def name="impl_split_style_coord(ident, gecko_ffi_name, index)"> #[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})); } #[allow(non_snake_case)] pub fn reset_${ident}(&mut self, other: &Self) { self.copy_${ident}_from(other) } #[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") } <%def name="impl_style_coord(ident, gecko_ffi_name)"> #[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}); } #[allow(non_snake_case)] pub fn reset_${ident}(&mut self, other: &Self) { self.copy_${ident}_from(other) } #[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") } <%def name="impl_style_sides(ident)"> <% gecko_ffi_name = "m" + to_camel_case(ident) %> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { v.to_gecko_rect(&mut self.gecko.${gecko_ffi_name}); } <%self:copy_sides_style_coord ident="${ident}"> #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { longhands::${ident}::computed_value::T::from_gecko_rect(&self.gecko.${gecko_ffi_name}) .expect("clone for ${ident} failed") } <%def name="copy_sides_style_coord(ident)"> <% gecko_ffi_name = "m" + to_camel_case(ident) %> #[allow(non_snake_case)] pub fn copy_${ident}_from(&mut self, other: &Self) { % for side in SIDES: self.gecko.${gecko_ffi_name}.data_at_mut(${side.index}) .copy_from(&other.gecko.${gecko_ffi_name}.data_at(${side.index})); % endfor ${ caller.body() } } #[allow(non_snake_case)] pub fn reset_${ident}(&mut self, other: &Self) { self.copy_${ident}_from(other) } <%def name="impl_corner_style_coord(ident, gecko_ffi_name, x_index, y_index)"> #[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})); } #[allow(non_snake_case)] pub fn reset_${ident}(&mut self, other: &Self) { self.copy_${ident}_from(other) } #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { use values::computed::border::BorderCornerRadius; 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}"); BorderCornerRadius::new(width, height) } <%def name="impl_css_url(ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { use gecko_bindings::sugar::refptr::RefPtr; match v { Either::First(url) => { let refptr = unsafe { let ptr = bindings::Gecko_NewURLValue(url.for_ffi()); if ptr.is_null() { self.gecko.${gecko_ffi_name}.clear(); return; } RefPtr::from_addrefed(ptr) }; self.gecko.${gecko_ffi_name}.set_move(refptr) } Either::Second(_none) => { unsafe { self.gecko.${gecko_ffi_name}.clear(); } } } } #[allow(non_snake_case)] pub fn copy_${ident}_from(&mut self, other: &Self) { unsafe { self.gecko.${gecko_ffi_name}.set(&other.gecko.${gecko_ffi_name}); } } #[allow(non_snake_case)] pub fn reset_${ident}(&mut self, other: &Self) { self.copy_${ident}_from(other) } #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { use values::specified::url::SpecifiedUrl; use values::None_; if self.gecko.${gecko_ffi_name}.mRawPtr.is_null() { Either::Second(None_) } else { unsafe { let ref gecko_url_value = *self.gecko.${gecko_ffi_name}.mRawPtr; Either::First(SpecifiedUrl::from_url_value_data(&gecko_url_value._base) .expect("${gecko_ffi_name} could not convert to SpecifiedUrl")) } } } <%def name="impl_logical(name, **kwargs)"> ${helpers.logical_setter(name)} <%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 } } } <%def name="impl_simple_type_with_conversion(ident, gecko_ffi_name=None)"> <% if gecko_ffi_name is None: gecko_ffi_name = "m" + to_camel_case(ident) %> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { self.gecko.${gecko_ffi_name} = From::from(v) } <% impl_simple_copy(ident, gecko_ffi_name) %> #[allow(non_snake_case)] pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { From::from(self.gecko.${gecko_ffi_name}) } <%def name="impl_font_settings(ident, tag_type)"> <% gecko_ffi_name = to_camel_case_lower(ident) %> pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { use values::generics::FontSettings; let current_settings = &mut self.gecko.mFont.${gecko_ffi_name}; current_settings.clear_pod(); match v { FontSettings::Normal => (), // do nothing, length is already 0 FontSettings::Tag(other_settings) => { unsafe { current_settings.set_len_pod(other_settings.len() as u32) }; for (current, other) in current_settings.iter_mut().zip(other_settings) { current.mTag = other.tag; current.mValue = other.value.0; } } }; } pub fn copy_${ident}_from(&mut self, other: &Self) { let current_settings = &mut self.gecko.mFont.${gecko_ffi_name}; let other_settings = &other.gecko.mFont.${gecko_ffi_name}; let settings_length = other_settings.len() as u32; current_settings.clear_pod(); unsafe { current_settings.set_len_pod(settings_length) }; for (current, other) in current_settings.iter_mut().zip(other_settings.iter()) { current.mTag = other.mTag; current.mValue = other.mValue; } } pub fn reset_${ident}(&mut self, other: &Self) { self.copy_${ident}_from(other) } pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { use values::generics::{FontSettings, FontSettingTag, ${tag_type}} ; if self.gecko.mFont.${gecko_ffi_name}.len() == 0 { FontSettings::Normal } else { FontSettings::Tag( self.gecko.mFont.${gecko_ffi_name}.iter().map(|gecko_font_setting| { FontSettingTag { tag: gecko_font_setting.mTag, value: ${tag_type}(gecko_font_setting.mValue), } }).collect() ) } } <%def name="impl_trait(style_struct_name, skip_longhands='')"> <% style_struct = next(x for x in data.style_structs if x.name == style_struct_name) longhands = [x for x in style_struct.longhands if not (skip_longhands == "*" or x.name in skip_longhands.split())] # Types used with predefined_type()-defined properties that we can auto-generate. predefined_types = { "length::LengthOrAuto": impl_style_coord, "length::LengthOrNormal": impl_style_coord, "length::NonNegativeLengthOrAuto": impl_style_coord, "length::NonNegativeLengthOrNormal": impl_style_coord, "GreaterThanOrEqualToOneNumber": impl_simple, "Length": impl_absolute_length, "Position": impl_position, "LengthOrPercentage": impl_style_coord, "LengthOrPercentageOrAuto": impl_style_coord, "LengthOrPercentageOrNone": impl_style_coord, "LengthOrNone": impl_style_coord, "LengthOrNormal": impl_style_coord, "MaxLength": impl_style_coord, "MozLength": impl_style_coord, "MozScriptMinSize": impl_absolute_length, "NonNegativeLengthOrPercentage": impl_style_coord, "NonNegativeNumber": impl_simple, "Number": impl_simple, "Integer": impl_simple, "Opacity": impl_simple, "Color": impl_color, "RGBAColor": impl_rgba_color, "SVGLength": impl_svg_length, "SVGOpacity": impl_svg_opacity, "SVGPaint": impl_svg_paint, "SVGWidth": impl_svg_length, "UrlOrNone": impl_css_url, } def longhand_method(longhand): args = dict(ident=longhand.ident, gecko_ffi_name=longhand.gecko_ffi_name) # 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] method(**args) picked_longhands = [] for x in longhands: if x.keyword or x.predefined_type in predefined_types or x.logical: picked_longhands.append(x) %> impl ${style_struct.gecko_struct_name} { /* * Manually-Implemented Methods. */ ${caller.body().strip()} /* * Auto-Generated Methods. */ <% for longhand in picked_longhands: longhand_method(longhand) %> } <%! class Side(object): def __init__(self, name, index): self.name = name self.ident = name.lower() self.index = index class Corner(object): def __init__(self, vert, horiz, index): self.x_name = "HalfCorner::eCorner" + vert + horiz + "X" self.y_name = "HalfCorner::eCorner" + vert + horiz + "Y" self.ident = (vert + "_" + horiz).lower() self.x_index = 2 * index self.y_index = 2 * index + 1 class GridLine(object): def __init__(self, name): self.ident = "grid-" + name.lower() self.name = self.ident.replace('-', '_') self.gecko = "m" + to_camel_case(self.ident) SIDES = [Side("Top", 0), Side("Right", 1), Side("Bottom", 2), Side("Left", 3)] CORNERS = [Corner("Top", "Left", 0), Corner("Top", "Right", 1), Corner("Bottom", "Right", 2), Corner("Bottom", "Left", 3)] GRID_LINES = map(GridLine, ["row-start", "row-end", "column-start", "column-end"]) %> #[allow(dead_code)] fn static_assert() { unsafe { % for corner in CORNERS: transmute::<_, [u32; ${corner.x_index}]>([1; structs::${corner.x_name} as usize]); transmute::<_, [u32; ${corner.y_index}]>([1; structs::${corner.y_name} as usize]); % endfor } // Note: using the above technique with an enum hits a rust bug when |structs| is in a different crate. % for side in SIDES: { const DETAIL: u32 = [0][(structs::Side::eSide${side.name} as usize != ${side.index}) as usize]; let _ = DETAIL; } % endfor } <% border_style_keyword = Keyword("border-style", "none solid double dotted dashed hidden groove ridge inset outset") %> <% skip_border_longhands = " ".join(["border-{0}-{1}".format(x.ident, y) for x in SIDES for y in ["color", "style", "width"]] + ["border-{0}-radius".format(x.ident.replace("_", "-")) for x in CORNERS]) %> <% skip_moz_border_color_longhands = " ".join("-moz-border-{0}-colors".format(x.ident) for x in SIDES) %> <%self:impl_trait style_struct_name="Border" skip_longhands="${skip_border_longhands} border-image-source border-image-outset border-image-repeat border-image-width border-image-slice ${skip_moz_border_color_longhands}"> fn set_moz_border_colors(&mut self, side: structs::Side, v: Option>) { match v { None => { let ptr = self.gecko.mBorderColors.mPtr; if let Some(colors) = unsafe { ptr.as_mut() } { unsafe { colors.mColors[side as usize].clear() }; } } Some(ref colors) => { unsafe { bindings::Gecko_EnsureMozBorderColors(&mut self.gecko) }; let border_colors = unsafe { self.gecko.mBorderColors.mPtr.as_mut().unwrap() }; let dest_colors = &mut border_colors.mColors[side as usize]; unsafe { dest_colors.set_len_pod(colors.len() as u32) }; for (dst, src) in dest_colors.iter_mut().zip(colors.into_iter()) { *dst = convert_rgba_to_nscolor(src); } } } } fn copy_moz_border_colors_from(&mut self, other: &Self, side: structs::Side) { if let Some(dest) = unsafe { self.gecko.mBorderColors.mPtr.as_mut() } { dest.mColors[side as usize].clear_pod(); } if let Some(src) = unsafe { other.gecko.mBorderColors.mPtr.as_ref() } { let src = &src.mColors[side as usize]; if !src.is_empty() { unsafe { bindings::Gecko_EnsureMozBorderColors(&mut self.gecko) }; let dest = unsafe { self.gecko.mBorderColors.mPtr.as_mut().unwrap() }; let dest = &mut dest.mColors[side as usize]; unsafe { dest.set_len_pod(src.len() as u32) }; dest.copy_from_slice(&src); } } } fn clone_moz_border_colors(&self, side: structs::Side) -> Option> { unsafe { self.gecko.mBorderColors.mPtr.as_ref() }.map(|colors| { colors.mColors[side as usize].iter() .map(|color| convert_nscolor_to_rgba(*color)) .collect() }) } % for side in SIDES: <% impl_keyword("border_%s_style" % side.ident, "mBorderStyle[%s]" % side.index, border_style_keyword, on_set="update_border_%s" % side.ident) %> // This is needed because the initial mComputedBorder value is set to zero. // // In order to compute stuff, we start from the initial struct, and keep // going down the tree applying properties. // // That means, effectively, that when we set border-style to something // non-hidden, we should use the initial border instead. // // Servo stores the initial border-width in the initial struct, and then // adjusts as needed in the fixup phase. This means that the initial struct // is technically not valid without fixups, and that you lose pretty much // any sharing of the initial struct, which is kind of unfortunate. // // Gecko has two fields for this, one that stores the "specified" border, // and other that stores the actual computed one. That means that when we // set border-style, border-width may change and we need to sync back to the // specified one. This is what this function does. // // Note that this doesn't impose any dependency in the order of computation // of the properties. This is only relevant if border-style is specified, // but border-width isn't. If border-width is specified at some point, the // two mBorder and mComputedBorder fields would be the same already. // // Once we're here, we know that we'll run style fixups, so it's fine to // just copy the specified border here, we'll adjust it if it's incorrect // later. fn update_border_${side.ident}(&mut self) { self.gecko.mComputedBorder.${side.ident} = self.gecko.mBorder.${side.ident}; } <% impl_color("border_%s_color" % side.ident, "(mBorderColor)[%s]" % side.index) %> <% impl_non_negative_length("border_%s_width" % side.ident, "mComputedBorder.%s" % side.ident, inherit_from="mBorder.%s" % side.ident, round_to_pixels=True) %> pub fn border_${side.ident}_has_nonzero_width(&self) -> bool { self.gecko.mComputedBorder.${side.ident} != 0 } #[allow(non_snake_case)] pub fn set__moz_border_${side.ident}_colors(&mut self, v: longhands::_moz_border_${side.ident}_colors::computed_value::T) { self.set_moz_border_colors(structs::Side::eSide${to_camel_case(side.ident)}, v.0); } #[allow(non_snake_case)] pub fn copy__moz_border_${side.ident}_colors_from(&mut self, other: &Self) { self.copy_moz_border_colors_from(other, structs::Side::eSide${to_camel_case(side.ident)}); } #[allow(non_snake_case)] pub fn reset__moz_border_${side.ident}_colors(&mut self, other: &Self) { self.copy__moz_border_${side.ident}_colors_from(other) } #[allow(non_snake_case)] pub fn clone__moz_border_${side.ident}_colors(&self) -> longhands::_moz_border_${side.ident}_colors::computed_value::T { use self::longhands::_moz_border_${side.ident}_colors::computed_value::T; T(self.clone_moz_border_colors(structs::Side::eSide${to_camel_case(side.ident)})) } % endfor % for corner in CORNERS: <% impl_corner_style_coord("border_%s_radius" % corner.ident, "mBorderRadius", corner.x_index, corner.y_index) %> % endfor pub fn set_border_image_source(&mut self, image: 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 Either::Second(image) = image { self.gecko.mBorderImageSource.set(image); } } pub fn copy_border_image_source_from(&mut self, other: &Self) { unsafe { Gecko_CopyImageValueFrom(&mut self.gecko.mBorderImageSource, &other.gecko.mBorderImageSource); } } pub fn reset_border_image_source(&mut self, other: &Self) { self.copy_border_image_source_from(other) } pub fn clone_border_image_source(&self) -> longhands::border_image_source::computed_value::T { use values::None_; match unsafe { self.gecko.mBorderImageSource.into_image() } { Some(image) => Either::Second(image), None => Either::First(None_), } } <% impl_style_sides("border_image_outset") %> <% border_image_repeat_keywords = ["Stretch", "Repeat", "Round", "Space"] %> 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; % for i, side in enumerate(["H", "V"]): let k = match v.${i} { % for keyword in border_image_repeat_keywords: RepeatKeyword::${keyword} => structs::NS_STYLE_BORDER_IMAGE_REPEAT_${keyword.upper()}, % endfor }; 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 reset_border_image_repeat(&mut self, other: &Self) { self.copy_border_image_repeat_from(other) } pub fn clone_border_image_repeat(&self) -> longhands::border_image_repeat::computed_value::T { use properties::longhands::border_image_repeat::computed_value::RepeatKeyword; use gecko_bindings::structs; % for side in ["H", "V"]: let servo_${side.lower()} = match self.gecko.mBorderImageRepeat${side} as u32 { % for keyword in border_image_repeat_keywords: structs::NS_STYLE_BORDER_IMAGE_REPEAT_${keyword.upper()} => RepeatKeyword::${keyword}, % endfor x => panic!("Found unexpected value in mBorderImageRepeat${side}: {:?}", x), }; % endfor longhands::border_image_repeat::computed_value::T(servo_h, servo_v) } <% impl_style_sides("border_image_width") %> 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}; v.offsets.to_gecko_rect(&mut self.gecko.mBorderImageSlice); let fill = if v.fill { NS_STYLE_BORDER_IMAGE_SLICE_FILL } else { NS_STYLE_BORDER_IMAGE_SLICE_NOFILL }; self.gecko.mBorderImageFill = fill as u8; } <%self:copy_sides_style_coord ident="border_image_slice"> self.gecko.mBorderImageFill = other.gecko.mBorderImageFill; pub fn clone_border_image_slice(&self) -> longhands::border_image_slice::computed_value::T { use gecko_bindings::structs::NS_STYLE_BORDER_IMAGE_SLICE_FILL; use values::computed::{BorderImageSlice, NumberOrPercentage}; type NumberOrPercentageRect = ::values::generics::rect::Rect; BorderImageSlice { offsets: NumberOrPercentageRect::from_gecko_rect(&self.gecko.mBorderImageSlice) .expect("mBorderImageSlice[${side}] could not convert to NumberOrPercentageRect"), fill: self.gecko.mBorderImageFill as u32 == NS_STYLE_BORDER_IMAGE_SLICE_FILL } } <% 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) %> % 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) %> % 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 order align-content justify-content align-self justify-self align-items justify-items grid-auto-rows grid-auto-columns grid-auto-flow grid-template-areas grid-template-rows grid-template-columns"> % for side in SIDES: <% impl_split_style_coord("%s" % side.ident, "mOffset", side.index) %> % endfor pub fn set_z_index(&mut self, v: longhands::z_index::computed_value::T) { match v { Either::First(n) => self.gecko.mZIndex.set_value(CoordDataValue::Integer(n)), Either::Second(Auto) => self.gecko.mZIndex.set_value(CoordDataValue::Auto), } } pub fn copy_z_index_from(&mut self, other: &Self) { use gecko_bindings::structs::nsStyleUnit; // z-index is never a calc(). If it were, we'd be leaking here, so // assert that it isn't. debug_assert!(self.gecko.mZIndex.unit() != nsStyleUnit::eStyleUnit_Calc); unsafe { self.gecko.mZIndex.copy_from_unchecked(&other.gecko.mZIndex); } } pub fn reset_z_index(&mut self, other: &Self) { self.copy_z_index_from(other) } pub fn clone_z_index(&self) -> longhands::z_index::computed_value::T { return match self.gecko.mZIndex.as_value() { CoordDataValue::Integer(n) => Either::First(n), CoordDataValue::Auto => Either::Second(Auto), _ => { debug_assert!(false); Either::First(0) } } } % for kind in ["align", "justify"]: ${impl_simple_type_with_conversion(kind + "_content")} ${impl_simple_type_with_conversion(kind + "_self")} % endfor ${impl_simple_type_with_conversion("align_items")} pub fn set_justify_items(&mut self, v: longhands::justify_items::computed_value::T) { self.gecko.mSpecifiedJustifyItems = v.specified.into(); self.set_computed_justify_items(v.computed); } pub fn set_computed_justify_items(&mut self, v: values::specified::JustifyItems) { debug_assert!(v.0 != ::values::specified::align::AlignFlags::AUTO); self.gecko.mJustifyItems = v.into(); } pub fn reset_justify_items(&mut self, reset_style: &Self) { self.gecko.mJustifyItems = reset_style.gecko.mJustifyItems; self.gecko.mSpecifiedJustifyItems = reset_style.gecko.mSpecifiedJustifyItems; } pub fn copy_justify_items_from(&mut self, other: &Self) { self.gecko.mJustifyItems = other.gecko.mJustifyItems; self.gecko.mSpecifiedJustifyItems = other.gecko.mJustifyItems; } pub fn clone_justify_items(&self) -> longhands::justify_items::computed_value::T { longhands::justify_items::computed_value::T { computed: self.gecko.mJustifyItems.into(), specified: self.gecko.mSpecifiedJustifyItems.into(), } } pub fn set_order(&mut self, v: longhands::order::computed_value::T) { self.gecko.mOrder = v; } pub fn clone_order(&self) -> longhands::order::computed_value::T { self.gecko.mOrder } ${impl_simple_copy('order', 'mOrder')} % for value in GRID_LINES: pub fn set_${value.name}(&mut self, v: longhands::${value.name}::computed_value::T) { use gecko_bindings::structs::{nsStyleGridLine_kMinLine, nsStyleGridLine_kMaxLine}; let ident = v.ident.as_ref().map_or(&[] as &[_], |ident| ident.0.as_slice()); self.gecko.${value.gecko}.mLineName.assign(ident); self.gecko.${value.gecko}.mHasSpan = v.is_span; if let Some(integer) = v.line_num { // clamping the integer between a range self.gecko.${value.gecko}.mInteger = cmp::max(nsStyleGridLine_kMinLine, cmp::min(integer, nsStyleGridLine_kMaxLine)); } } 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); } pub fn reset_${value.name}(&mut self, other: &Self) { self.copy_${value.name}_from(other) } pub fn clone_${value.name}(&self) -> longhands::${value.name}::computed_value::T { use gecko_bindings::structs::{nsStyleGridLine_kMinLine, nsStyleGridLine_kMaxLine}; use string_cache::Atom; longhands::${value.name}::computed_value::T { is_span: self.gecko.${value.gecko}.mHasSpan, ident: { let name = self.gecko.${value.gecko}.mLineName.to_string(); if name.len() == 0 { None } else { Some(CustomIdent(Atom::from(name))) } }, line_num: if self.gecko.${value.gecko}.mInteger == 0 { None } else { debug_assert!(nsStyleGridLine_kMinLine <= self.gecko.${value.gecko}.mInteger); debug_assert!(self.gecko.${value.gecko}.mInteger <= nsStyleGridLine_kMaxLine); Some(self.gecko.${value.gecko}.mInteger) }, } } % endfor % for kind in ["rows", "columns"]: pub fn set_grid_auto_${kind}(&mut self, v: longhands::grid_auto_${kind}::computed_value::T) { v.to_gecko_style_coords(&mut self.gecko.mGridAuto${kind.title()}Min, &mut self.gecko.mGridAuto${kind.title()}Max) } pub fn copy_grid_auto_${kind}_from(&mut self, other: &Self) { self.gecko.mGridAuto${kind.title()}Min.copy_from(&other.gecko.mGridAuto${kind.title()}Min); self.gecko.mGridAuto${kind.title()}Max.copy_from(&other.gecko.mGridAuto${kind.title()}Max); } pub fn reset_grid_auto_${kind}(&mut self, other: &Self) { self.copy_grid_auto_${kind}_from(other) } pub fn clone_grid_auto_${kind}(&self) -> longhands::grid_auto_${kind}::computed_value::T { ::values::generics::grid::TrackSize::from_gecko_style_coords(&self.gecko.mGridAuto${kind.title()}Min, &self.gecko.mGridAuto${kind.title()}Max) } pub fn set_grid_template_${kind}(&mut self, v: longhands::grid_template_${kind}::computed_value::T) { <% self_grid = "self.gecko.mGridTemplate%s" % kind.title() %> use gecko_bindings::structs::{nsTArray, nsStyleGridLine_kMaxLine}; use nsstring::nsStringRepr; use std::usize; use values::CustomIdent; use values::generics::grid::TrackListType::Auto; use values::generics::grid::{GridTemplateComponent, RepeatCount}; #[inline] fn set_line_names(servo_names: &[CustomIdent], gecko_names: &mut nsTArray) { unsafe { bindings::Gecko_ResizeTArrayForStrings(gecko_names, servo_names.len() as u32); } for (servo_name, gecko_name) in servo_names.iter().zip(gecko_names.iter_mut()) { gecko_name.assign(servo_name.0.as_slice()); } } let max_lines = nsStyleGridLine_kMaxLine as usize - 1; // for accounting the final let result = match v { GridTemplateComponent::None => ptr::null_mut(), GridTemplateComponent::TrackList(track) => { let mut num_values = track.values.len(); if let Auto(_) = track.list_type { num_values += 1; } num_values = cmp::min(num_values, max_lines); let value = unsafe { bindings::Gecko_CreateStyleGridTemplate(num_values as u32, (num_values + 1) as u32).as_mut().unwrap() }; let mut auto_idx = usize::MAX; let mut auto_track_size = None; if let Auto(idx) = track.list_type { auto_idx = idx as usize; let auto_repeat = track.auto_repeat.as_ref().expect("expected value"); if auto_repeat.count == RepeatCount::AutoFill { value.set_mIsAutoFill(true); } value.mRepeatAutoIndex = idx as i16; // NOTE: Gecko supports only one set of values in // i.e., it can only take repeat(auto-fill, [a] 10px [b]), and no more. set_line_names(&auto_repeat.line_names[0], &mut value.mRepeatAutoLineNameListBefore); set_line_names(&auto_repeat.line_names[1], &mut value.mRepeatAutoLineNameListAfter); auto_track_size = Some(auto_repeat.track_sizes.get(0).unwrap().clone()); } else { unsafe { bindings::Gecko_ResizeTArrayForStrings( &mut value.mRepeatAutoLineNameListBefore, 0); bindings::Gecko_ResizeTArrayForStrings( &mut value.mRepeatAutoLineNameListAfter, 0); } } let mut line_names = track.line_names.into_iter(); let mut values_iter = track.values.into_iter(); { let min_max_iter = value.mMinTrackSizingFunctions.iter_mut() .zip(value.mMaxTrackSizingFunctions.iter_mut()); for (i, (gecko_min, gecko_max)) in min_max_iter.enumerate().take(max_lines) { let name_list = line_names.next().expect("expected line-names"); set_line_names(&name_list, &mut value.mLineNameLists[i]); if i == auto_idx { let track_size = auto_track_size.take() .expect("expected for "); track_size.to_gecko_style_coords(gecko_min, gecko_max); continue } let track_size = values_iter.next().expect("expected value"); track_size.to_gecko_style_coords(gecko_min, gecko_max); } } let final_names = line_names.next().unwrap(); set_line_names(&final_names, value.mLineNameLists.last_mut().unwrap()); value }, GridTemplateComponent::Subgrid(list) => { let names_length = match list.fill_idx { Some(_) => list.names.len() - 1, None => list.names.len(), }; let num_values = cmp::min(names_length, max_lines + 1); let value = unsafe { bindings::Gecko_CreateStyleGridTemplate(0, num_values as u32).as_mut().unwrap() }; value.set_mIsSubgrid(true); let mut names = list.names.into_vec(); if let Some(idx) = list.fill_idx { value.set_mIsAutoFill(true); value.mRepeatAutoIndex = idx as i16; set_line_names(&names.swap_remove(idx as usize), &mut value.mRepeatAutoLineNameListBefore); } for (servo_names, gecko_names) in names.iter().zip(value.mLineNameLists.iter_mut()) { set_line_names(servo_names, gecko_names); } value }, }; unsafe { bindings::Gecko_SetStyleGridTemplate(&mut ${self_grid}, result); } } pub fn copy_grid_template_${kind}_from(&mut self, other: &Self) { unsafe { bindings::Gecko_CopyStyleGridTemplateValues(&mut ${self_grid}, other.gecko.mGridTemplate${kind.title()}.mPtr); } } pub fn reset_grid_template_${kind}(&mut self, other: &Self) { self.copy_grid_template_${kind}_from(other) } pub fn clone_grid_template_${kind}(&self) -> longhands::grid_template_${kind}::computed_value::T { <% self_grid = "self.gecko.mGridTemplate%s" % kind.title() %> use Atom; use gecko_bindings::structs::nsTArray; use nsstring::nsStringRepr; use values::CustomIdent; use values::generics::grid::{GridTemplateComponent, LineNameList, RepeatCount}; use values::generics::grid::{TrackList, TrackListType, TrackListValue, TrackRepeat, TrackSize}; let value = match unsafe { ${self_grid}.mPtr.as_ref() } { None => return GridTemplateComponent::None, Some(value) => value, }; #[inline] fn to_boxed_customident_slice(gecko_names: &nsTArray) -> Box<[CustomIdent]> { let idents: Vec = gecko_names.iter().map(|gecko_name| { CustomIdent(Atom::from(gecko_name.to_string())) }).collect(); idents.into_boxed_slice() } #[inline] fn to_line_names_vec(gecko_line_names: &nsTArray>) -> Vec> { gecko_line_names.iter().map(|gecko_names| { to_boxed_customident_slice(gecko_names) }).collect() } let repeat_auto_index = value.mRepeatAutoIndex as usize; if value.mIsSubgrid() { let mut names_vec = to_line_names_vec(&value.mLineNameLists); let fill_idx = if value.mIsAutoFill() { names_vec.insert( repeat_auto_index, to_boxed_customident_slice(&value.mRepeatAutoLineNameListBefore)); Some(repeat_auto_index as u32) } else { None }; let names = names_vec.into_boxed_slice(); GridTemplateComponent::Subgrid(LineNameList{names, fill_idx}) } else { let mut auto_repeat = None; let mut list_type = TrackListType::Normal; let line_names = to_line_names_vec(&value.mLineNameLists).into_boxed_slice(); let mut values = Vec::with_capacity(value.mMinTrackSizingFunctions.len()); let min_max_iter = value.mMinTrackSizingFunctions.iter() .zip(value.mMaxTrackSizingFunctions.iter()); for (i, (gecko_min, gecko_max)) in min_max_iter.enumerate() { let track_size = TrackSize::from_gecko_style_coords(gecko_min, gecko_max); if i == repeat_auto_index { list_type = TrackListType::Auto(repeat_auto_index as u16); let count = if value.mIsAutoFill() { RepeatCount::AutoFill } else { RepeatCount::AutoFit }; let line_names = { let mut vec: Vec> = Vec::with_capacity(2); vec.push(to_boxed_customident_slice( &value.mRepeatAutoLineNameListBefore)); vec.push(to_boxed_customident_slice( &value.mRepeatAutoLineNameListAfter)); vec.into_boxed_slice() }; let track_sizes = vec!(track_size); auto_repeat = Some(TrackRepeat{count, line_names, track_sizes}); } else { values.push(TrackListValue::TrackSize(track_size)); } } GridTemplateComponent::TrackList(TrackList{list_type, values, line_names, auto_repeat}) } } % endfor ${impl_simple_type_with_conversion("grid_auto_flow")} pub fn set_grid_template_areas(&mut self, v: longhands::grid_template_areas::computed_value::T) { use gecko_bindings::bindings::Gecko_NewGridTemplateAreasValue; use gecko_bindings::sugar::refptr::UniqueRefPtr; let v = match v { Either::First(areas) => areas, Either::Second(_) => { unsafe { self.gecko.mGridTemplateAreas.clear() } return; }, }; let mut refptr = unsafe { UniqueRefPtr::from_addrefed( Gecko_NewGridTemplateAreasValue(v.areas.len() as u32, v.strings.len() as u32, v.width)) }; for (servo, gecko) in v.areas.into_iter().zip(refptr.mNamedAreas.iter_mut()) { gecko.mName.assign_utf8(&*servo.name); gecko.mColumnStart = servo.columns.start; gecko.mColumnEnd = servo.columns.end; gecko.mRowStart = servo.rows.start; gecko.mRowEnd = servo.rows.end; } for (servo, gecko) in v.strings.into_iter().zip(refptr.mTemplates.iter_mut()) { gecko.assign_utf8(&*servo); } unsafe { self.gecko.mGridTemplateAreas.set_move(refptr.get()) } } pub fn copy_grid_template_areas_from(&mut self, other: &Self) { unsafe { self.gecko.mGridTemplateAreas.set(&other.gecko.mGridTemplateAreas) } } pub fn reset_grid_template_areas(&mut self, other: &Self) { self.copy_grid_template_areas_from(other) } pub fn clone_grid_template_areas(&self) -> longhands::grid_template_areas::computed_value::T { use properties::longhands::grid_template_areas::{NamedArea, TemplateAreas}; use std::ops::Range; use values::None_; if self.gecko.mGridTemplateAreas.mRawPtr.is_null() { return Either::Second(None_); } let gecko_grid_template_areas = self.gecko.mGridTemplateAreas.mRawPtr; let areas = unsafe { let vec: Vec = (*gecko_grid_template_areas).mNamedAreas.iter().map(|gecko_name_area| { let name = gecko_name_area.mName.to_string().into_boxed_str(); let rows = Range { start: gecko_name_area.mRowStart, end: gecko_name_area.mRowEnd }; let columns = Range { start: gecko_name_area.mColumnStart, end: gecko_name_area.mColumnEnd }; NamedArea{ name, rows, columns } }).collect(); vec.into_boxed_slice() }; let strings = unsafe { let vec: Vec> = (*gecko_grid_template_areas).mTemplates.iter().map(|gecko_template| { gecko_template.to_string().into_boxed_str() }).collect(); vec.into_boxed_slice() }; let width = unsafe { (*gecko_grid_template_areas).mNColumns }; Either::First(TemplateAreas{ areas, strings, width }) } <% 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}"> #[allow(non_snake_case)] pub fn set_outline_style(&mut self, v: longhands::outline_style::computed_value::T) { // FIXME(bholley): Align binary representations and ditch |match| for // cast + static_asserts let result = match v { % for value in border_style_keyword.values_for('gecko'): Either::Second(border_style::T::${to_rust_ident(value)}) => structs::${border_style_keyword.gecko_constant(value)} ${border_style_keyword.maybe_cast("u8")}, % endfor Either::First(Auto) => structs::${border_style_keyword.gecko_constant('auto')} ${border_style_keyword.maybe_cast("u8")}, }; ${set_gecko_property("mOutlineStyle", "result")} // NB: This is needed to correctly handling the initial value of // outline-width when outline-style changes, see the // update_border_${side.ident} comment for more details. self.gecko.mActualOutlineWidth = self.gecko.mOutlineWidth; } #[allow(non_snake_case)] pub fn copy_outline_style_from(&mut self, other: &Self) { self.gecko.mOutlineStyle = other.gecko.mOutlineStyle; } #[allow(non_snake_case)] pub fn reset_outline_style(&mut self, other: &Self) { self.copy_outline_style_from(other) } #[allow(non_snake_case)] pub fn clone_outline_style(&self) -> longhands::outline_style::computed_value::T { // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts match ${get_gecko_property("mOutlineStyle")} ${border_style_keyword.maybe_cast("u32")} { % for value in border_style_keyword.values_for('gecko'): structs::${border_style_keyword.gecko_constant(value)} => Either::Second(border_style::T::${value}), % endfor structs::${border_style_keyword.gecko_constant('auto')} => Either::First(Auto), % if border_style_keyword.gecko_inexhaustive: x => panic!("Found unexpected value in style struct for outline_style property: {:?}", x), % endif } } <% impl_non_negative_length("outline_width", "mActualOutlineWidth", inherit_from="mOutlineWidth", round_to_pixels=True) %> % for corner in CORNERS: <% impl_corner_style_coord("_moz_outline_radius_%s" % corner.ident.replace("_", ""), "mOutlineRadius", corner.x_index, corner.y_index) %> % endfor pub fn outline_has_nonzero_width(&self) -> bool { self.gecko.mActualOutlineWidth != 0 } <% skip_font_longhands = """font-family font-size font-size-adjust font-weight font-synthesis -x-lang font-variant-alternates font-variant-east-asian font-variant-ligatures font-variant-numeric font-language-override font-feature-settings font-variation-settings -moz-min-font-size-ratio -x-text-zoom""" %> <%self:impl_trait style_struct_name="Font" skip_longhands="${skip_font_longhands}"> <% impl_font_settings("font_feature_settings", "FontSettingTagInt") %> <% impl_font_settings("font_variation_settings", "FontSettingTagFloat") %> pub fn fixup_none_generic(&mut self, device: &Device) { self.gecko.mFont.systemFont = false; unsafe { bindings::Gecko_nsStyleFont_FixupNoneGeneric(&mut self.gecko, device.pres_context()) } } pub fn fixup_system(&mut self, default_font_type: structs::FontFamilyType) { self.gecko.mFont.systemFont = true; self.gecko.mGenericID = structs::kGenericFont_NONE; self.gecko.mFont.fontlist.mDefaultFontType = default_font_type; } pub fn set_font_family(&mut self, v: longhands::font_family::computed_value::T) { self.gecko.mGenericID = structs::kGenericFont_NONE; if let Some(generic) = v.0.single_generic() { self.gecko.mGenericID = generic; } self.gecko.mFont.fontlist.mFontlist.mBasePtr.set_move((v.0).0.clone()); } 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); } self.gecko.mGenericID = other.gecko.mGenericID; self.gecko.mFont.systemFont = other.gecko.mFont.systemFont; } pub fn reset_font_family(&mut self, other: &Self) { self.copy_font_family_from(other) } pub fn clone_font_family(&self) -> longhands::font_family::computed_value::T { use gecko_bindings::structs::FontFamilyType; use properties::longhands::font_family::computed_value; use properties::longhands::font_family::computed_value::FontFamily; use properties::longhands::font_family::computed_value::FontFamilyList; let fontlist = &self.gecko.mFont.fontlist; let shared_fontlist = unsafe { fontlist.mFontlist.mBasePtr.to_safe() }; if shared_fontlist.mNames.is_empty() { let default = match fontlist.mDefaultFontType { FontFamilyType::eFamily_serif => { FontFamily::Generic(atom!("serif")) } FontFamilyType::eFamily_sans_serif => { FontFamily::Generic(atom!("sans-serif")) } _ => panic!("Default generic must be serif or sans-serif"), }; computed_value::T(FontFamilyList::new(vec![default])) } else { computed_value::T(FontFamilyList(shared_fontlist)) } } pub fn unzoom_fonts(&mut self, device: &Device) { self.gecko.mSize = device.unzoom_text(Au(self.gecko.mSize)).0; self.gecko.mScriptUnconstrainedSize = device.unzoom_text(Au(self.gecko.mScriptUnconstrainedSize)).0; self.gecko.mFont.size = device.unzoom_text(Au(self.gecko.mFont.size)).0; } pub fn set_font_size(&mut self, v: longhands::font_size::computed_value::T) { use values::specified::font::KeywordSize; self.gecko.mSize = v.size().0; self.gecko.mScriptUnconstrainedSize = v.size().0; if let Some(info) = v.keyword_info { self.gecko.mFontSizeKeyword = match info.kw { KeywordSize::XXSmall => structs::NS_STYLE_FONT_SIZE_XXSMALL, KeywordSize::XSmall => structs::NS_STYLE_FONT_SIZE_XSMALL, KeywordSize::Small => structs::NS_STYLE_FONT_SIZE_SMALL, KeywordSize::Medium => structs::NS_STYLE_FONT_SIZE_MEDIUM, KeywordSize::Large => structs::NS_STYLE_FONT_SIZE_LARGE, KeywordSize::XLarge => structs::NS_STYLE_FONT_SIZE_XLARGE, KeywordSize::XXLarge => structs::NS_STYLE_FONT_SIZE_XXLARGE, KeywordSize::XXXLarge => structs::NS_STYLE_FONT_SIZE_XXXLARGE, } as u8; self.gecko.mFontSizeFactor = info.factor; self.gecko.mFontSizeOffset = info.offset.0.to_i32_au(); } else { self.gecko.mFontSizeKeyword = structs::NS_STYLE_FONT_SIZE_NO_KEYWORD as u8; self.gecko.mFontSizeFactor = 1.; self.gecko.mFontSizeOffset = 0; } } /// Set font size, taking into account scriptminsize and scriptlevel /// Returns Some(size) if we have to recompute the script unconstrained size pub fn apply_font_size(&mut self, v: longhands::font_size::computed_value::T, parent: &Self, device: &Device) -> Option { let (adjusted_size, adjusted_unconstrained_size) = self.calculate_script_level_size(parent, device); // In this case, we have been unaffected by scriptminsize, ignore it if parent.gecko.mSize == parent.gecko.mScriptUnconstrainedSize && adjusted_size == adjusted_unconstrained_size { self.set_font_size(v); self.fixup_font_min_size(device); None } else { self.gecko.mSize = v.size().0; self.fixup_font_min_size(device); Some(Au(parent.gecko.mScriptUnconstrainedSize).into()) } } pub fn fixup_font_min_size(&mut self, device: &Device) { unsafe { bindings::Gecko_nsStyleFont_FixupMinFontSize(&mut self.gecko, device.pres_context()) } } pub fn apply_unconstrained_font_size(&mut self, v: NonNegativeLength) { self.gecko.mScriptUnconstrainedSize = v.0.to_i32_au(); } /// Calculates the constrained and unconstrained font sizes to be inherited /// from the parent. /// /// See ComputeScriptLevelSize in Gecko's nsRuleNode.cpp /// /// scriptlevel is a property that affects how font-size is inherited. If scriptlevel is /// +1, for example, it will inherit as the script size multiplier times /// the parent font. This does not affect cases where the font-size is /// explicitly set. /// /// However, this transformation is not allowed to reduce the size below /// scriptminsize. If this inheritance will reduce it to below /// scriptminsize, it will be set to scriptminsize or the parent size, /// whichever is smaller (the parent size could be smaller than the min size /// because it was explicitly specified). /// /// Now, within a node that has inherited a font-size which was /// crossing scriptminsize once the scriptlevel was applied, a negative /// scriptlevel may be used to increase the size again. /// /// This should work, however if we have already been capped by the /// scriptminsize multiple times, this can lead to a jump in the size. /// /// For example, if we have text of the form: /// /// huge large medium small tiny reallytiny tiny small medium huge /// /// which is represented by progressive nesting and scriptlevel values of /// +1 till the center after which the scriptlevel is -1, the "tiny"s should /// be the same size, as should be the "small"s and "medium"s, etc. /// /// However, if scriptminsize kicked it at around "medium", then /// medium/tiny/reallytiny will all be the same size (the min size). /// A -1 scriptlevel change after this will increase the min size by the /// multiplier, making the second tiny larger than medium. /// /// Instead, we wish for the second "tiny" to still be capped by the script /// level, and when we reach the second "large", it should be the same size /// as the original one. /// /// We do this by cascading two separate font sizes. The font size (mSize) /// is the actual displayed font size. The unconstrained font size /// (mScriptUnconstrainedSize) is the font size in the situation where /// scriptminsize never applied. /// /// We calculate the proposed inherited font size based on scriptlevel and /// the parent unconstrained size, instead of using the parent font size. /// This is stored in the node's unconstrained size and will also be stored /// in the font size provided that it is above the min size. /// /// All of this only applies when inheriting. When the font size is /// manually set, scriptminsize does not apply, and both the real and /// unconstrained size are set to the explicit value. However, if the font /// size is manually set to an em or percent unit, the unconstrained size /// will be set to the value of that unit computed against the parent /// unconstrained size, whereas the font size will be set computing against /// the parent font size. pub fn calculate_script_level_size(&self, parent: &Self, device: &Device) -> (Au, Au) { use std::cmp; let delta = self.gecko.mScriptLevel.saturating_sub(parent.gecko.mScriptLevel); let parent_size = Au(parent.gecko.mSize); let parent_unconstrained_size = Au(parent.gecko.mScriptUnconstrainedSize); if delta == 0 { return (parent_size, parent_unconstrained_size) } let mut min = Au(parent.gecko.mScriptMinSize); if self.gecko.mAllowZoom { min = device.zoom_text(min); } let scale = (parent.gecko.mScriptSizeMultiplier as f32).powi(delta as i32); let new_size = parent_size.scale_by(scale); let new_unconstrained_size = parent_unconstrained_size.scale_by(scale); if scale < 1. { // The parent size can be smaller than scriptminsize, // e.g. if it was specified explicitly. Don't scale // in this case, but we don't want to set it to scriptminsize // either since that will make it larger. if parent_size < min { (parent_size, new_unconstrained_size) } else { (cmp::max(min, new_size), new_unconstrained_size) } } else { // If the new unconstrained size is larger than the min size, // this means we have escaped the grasp of scriptminsize // and can revert to using the unconstrained size. // However, if the new size is even larger (perhaps due to usage // of em units), use that instead. (cmp::min(new_size, cmp::max(new_unconstrained_size, min)), new_unconstrained_size) } } /// This function will also handle scriptminsize and scriptlevel /// so should not be called when you just want the font sizes to be copied. /// Hence the different name. pub fn inherit_font_size_from(&mut self, parent: &Self, kw_inherited_size: Option, device: &Device) { let (adjusted_size, adjusted_unconstrained_size) = self.calculate_script_level_size(parent, device); if adjusted_size.0 != parent.gecko.mSize || adjusted_unconstrained_size.0 != parent.gecko.mScriptUnconstrainedSize { // This is incorrect. When there is both a keyword size being inherited // and a scriptlevel change, we must handle the keyword size the same // way we handle em units. This complicates things because we now have // to keep track of the adjusted and unadjusted ratios in the kw font size. // This only affects the use case of a generic font being used in MathML. // // If we were to fix this I would prefer doing it by removing the // ruletree walk on the Gecko side in nsRuleNode::SetGenericFont // and instead using extra bookkeeping in the mSize and mScriptUnconstrainedSize // values, and reusing those instead of font_size_keyword. // In the case that MathML has given us an adjusted size, apply it. // Keep track of the unconstrained adjusted size. self.gecko.mSize = adjusted_size.0; // Technically the MathML constrained size may also be keyword-derived // but we ignore this since it would be too complicated // to correctly track and it's mostly unnecessary. self.gecko.mFontSizeKeyword = structs::NS_STYLE_FONT_SIZE_NO_KEYWORD as u8; self.gecko.mFontSizeFactor = 1.; self.gecko.mFontSizeOffset = 0; self.gecko.mScriptUnconstrainedSize = adjusted_unconstrained_size.0; } else if let Some(size) = kw_inherited_size { // Parent element was a keyword-derived size. self.gecko.mSize = size.0.to_i32_au(); // Copy keyword info over. self.gecko.mFontSizeFactor = parent.gecko.mFontSizeFactor; self.gecko.mFontSizeOffset = parent.gecko.mFontSizeOffset; self.gecko.mFontSizeKeyword = parent.gecko.mFontSizeKeyword; // MathML constraints didn't apply here, so we can ignore this. self.gecko.mScriptUnconstrainedSize = size.0.to_i32_au(); } else { // MathML isn't affecting us, and our parent element does not // have a keyword-derived size. Set things normally. self.gecko.mSize = parent.gecko.mSize; // copy keyword info over self.gecko.mFontSizeKeyword = structs::NS_STYLE_FONT_SIZE_NO_KEYWORD as u8; self.gecko.mFontSizeFactor = 1.; self.gecko.mFontSizeOffset = 0; self.gecko.mScriptUnconstrainedSize = parent.gecko.mScriptUnconstrainedSize; } self.fixup_font_min_size(device); } pub fn clone_font_size(&self) -> longhands::font_size::computed_value::T { use values::computed::font::KeywordInfo; use values::specified::font::KeywordSize; let size = Au(self.gecko.mSize).into(); let kw = match self.gecko.mFontSizeKeyword as u32 { structs::NS_STYLE_FONT_SIZE_XXSMALL => KeywordSize::XXSmall, structs::NS_STYLE_FONT_SIZE_XSMALL => KeywordSize::XSmall, structs::NS_STYLE_FONT_SIZE_SMALL => KeywordSize::Small, structs::NS_STYLE_FONT_SIZE_MEDIUM => KeywordSize::Medium, structs::NS_STYLE_FONT_SIZE_LARGE => KeywordSize::Large, structs::NS_STYLE_FONT_SIZE_XLARGE => KeywordSize::XLarge, structs::NS_STYLE_FONT_SIZE_XXLARGE => KeywordSize::XXLarge, structs::NS_STYLE_FONT_SIZE_XXXLARGE => KeywordSize::XXXLarge, structs::NS_STYLE_FONT_SIZE_NO_KEYWORD => { return longhands::font_size::computed_value::T { size: size, keyword_info: None, } } _ => unreachable!("mFontSizeKeyword should be an absolute keyword or NO_KEYWORD") }; longhands::font_size::computed_value::T { size: size, keyword_info: Some(KeywordInfo { kw: kw, factor: self.gecko.mFontSizeFactor, offset: Au(self.gecko.mFontSizeOffset).into() }) } } pub fn set_font_weight(&mut self, v: longhands::font_weight::computed_value::T) { self.gecko.mFont.weight = v.0; } ${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 <= ::std::u16::MAX); longhands::font_weight::computed_value::T(self.gecko.mFont.weight) } ${impl_simple_type_with_conversion("font_synthesis", "mFont.synthesis")} pub fn set_font_size_adjust(&mut self, v: longhands::font_size_adjust::computed_value::T) { use properties::longhands::font_size_adjust::computed_value::T; match v { T::None => self.gecko.mFont.sizeAdjust = -1.0 as f32, T::Number(n) => self.gecko.mFont.sizeAdjust = n, } } pub fn copy_font_size_adjust_from(&mut self, other: &Self) { self.gecko.mFont.sizeAdjust = other.gecko.mFont.sizeAdjust; } pub fn reset_font_size_adjust(&mut self, other: &Self) { self.copy_font_size_adjust_from(other) } pub fn clone_font_size_adjust(&self) -> longhands::font_size_adjust::computed_value::T { use properties::longhands::font_size_adjust::computed_value::T; T::from_gecko_adjust(self.gecko.mFont.sizeAdjust) } #[allow(non_snake_case)] pub fn set__x_lang(&mut self, v: longhands::_x_lang::computed_value::T) { let ptr = v.0.as_ptr(); forget(v); unsafe { Gecko_nsStyleFont_SetLang(&mut self.gecko, ptr); } } #[allow(non_snake_case)] pub fn copy__x_lang_from(&mut self, other: &Self) { unsafe { Gecko_nsStyleFont_CopyLangFrom(&mut self.gecko, &other.gecko); } } #[allow(non_snake_case)] pub fn set__x_text_zoom(&mut self, v: longhands::_x_text_zoom::computed_value::T) { self.gecko.mAllowZoom = v.0; } #[allow(non_snake_case)] pub fn copy__x_text_zoom_from(&mut self, other: &Self) { self.gecko.mAllowZoom = other.gecko.mAllowZoom; } #[allow(non_snake_case)] pub fn reset__x_text_zoom(&mut self, other: &Self) { self.copy__x_text_zoom_from(other) } #[allow(non_snake_case)] pub fn reset__x_lang(&mut self, other: &Self) { self.copy__x_lang_from(other) } <% impl_simple_type_with_conversion("font_language_override", "mFont.languageOverride") %> pub fn set_font_variant_alternates(&mut self, v: longhands::font_variant_alternates::computed_value::T, device: &Device) { use gecko_bindings::bindings::{Gecko_ClearAlternateValues, Gecko_AppendAlternateValues}; use gecko_bindings::bindings::Gecko_nsFont_ResetFontFeatureValuesLookup; use gecko_bindings::bindings::Gecko_nsFont_SetFontFeatureValuesLookup; % for value in "normal swash stylistic ornaments annotation styleset character_variant historical".split(): use gecko_bindings::structs::NS_FONT_VARIANT_ALTERNATES_${value.upper()}; % endfor use self::longhands::font_variant_alternates::VariantAlternates; unsafe { Gecko_ClearAlternateValues(&mut self.gecko.mFont, v.len()); } if v.0.is_empty() { self.gecko.mFont.variantAlternates = NS_FONT_VARIANT_ALTERNATES_NORMAL as u16; unsafe { Gecko_nsFont_ResetFontFeatureValuesLookup(&mut self.gecko.mFont); } return; } for val in v.0.iter() { match *val { % for value in "Swash Stylistic Ornaments Annotation".split(): VariantAlternates::${value}(ref ident) => { self.gecko.mFont.variantAlternates |= NS_FONT_VARIANT_ALTERNATES_${value.upper()} as u16; unsafe { Gecko_AppendAlternateValues(&mut self.gecko.mFont, NS_FONT_VARIANT_ALTERNATES_${value.upper()}, ident.0.as_ptr()); } }, % endfor % for value in "styleset character_variant".split(): VariantAlternates::${to_camel_case(value)}(ref slice) => { self.gecko.mFont.variantAlternates |= NS_FONT_VARIANT_ALTERNATES_${value.upper()} as u16; for ident in slice.iter() { unsafe { Gecko_AppendAlternateValues(&mut self.gecko.mFont, NS_FONT_VARIANT_ALTERNATES_${value.upper()}, ident.0.as_ptr()); } } }, % endfor VariantAlternates::HistoricalForms => { self.gecko.mFont.variantAlternates |= NS_FONT_VARIANT_ALTERNATES_HISTORICAL as u16; } } } unsafe { Gecko_nsFont_SetFontFeatureValuesLookup(&mut self.gecko.mFont, device.pres_context()); } } #[allow(non_snake_case)] pub fn copy_font_variant_alternates_from(&mut self, other: &Self) { use gecko_bindings::bindings::Gecko_CopyAlternateValuesFrom; self.gecko.mFont.variantAlternates = other.gecko.mFont.variantAlternates; unsafe { Gecko_CopyAlternateValuesFrom(&mut self.gecko.mFont, &other.gecko.mFont); } } pub fn reset_font_variant_alternates(&mut self, other: &Self) { self.copy_font_variant_alternates_from(other) } pub fn clone_font_variant_alternates(&self) -> longhands::font_variant_alternates::computed_value::T { use Atom; % for value in "normal swash stylistic ornaments annotation styleset character_variant historical".split(): use gecko_bindings::structs::NS_FONT_VARIANT_ALTERNATES_${value.upper()}; % endfor use properties::longhands::font_variant_alternates::VariantAlternates; use properties::longhands::font_variant_alternates::VariantAlternatesList; use values::CustomIdent; if self.gecko.mFont.variantAlternates == NS_FONT_VARIANT_ALTERNATES_NORMAL as u16 { return VariantAlternatesList(vec![].into_boxed_slice()); } let mut alternates = Vec::with_capacity(self.gecko.mFont.alternateValues.len()); if self.gecko.mFont.variantAlternates & (NS_FONT_VARIANT_ALTERNATES_HISTORICAL as u16) != 0 { alternates.push(VariantAlternates::HistoricalForms); } <% property_need_ident_list = "styleset character_variant".split() %> % for value in property_need_ident_list: let mut ${value}_list = Vec::new(); % endfor for gecko_alternate_value in self.gecko.mFont.alternateValues.iter() { let ident = Atom::from(gecko_alternate_value.value.to_string()); match gecko_alternate_value.alternate { % for value in "Swash Stylistic Ornaments Annotation".split(): NS_FONT_VARIANT_ALTERNATES_${value.upper()} => { alternates.push(VariantAlternates::${value}(CustomIdent(ident))); }, % endfor % for value in property_need_ident_list: NS_FONT_VARIANT_ALTERNATES_${value.upper()} => { ${value}_list.push(CustomIdent(ident)); }, % endfor x => { panic!("Found unexpected value for font-variant-alternates: {:?}", x); } } } % for value in property_need_ident_list: if !${value}_list.is_empty() { alternates.push(VariantAlternates::${to_camel_case(value)}(${value}_list.into_boxed_slice())); } % endfor VariantAlternatesList(alternates.into_boxed_slice()) } ${impl_simple_type_with_conversion("font_variant_ligatures", "mFont.variantLigatures")} ${impl_simple_type_with_conversion("font_variant_east_asian", "mFont.variantEastAsian")} ${impl_simple_type_with_conversion("font_variant_numeric", "mFont.variantNumeric")} #[allow(non_snake_case)] pub fn set__moz_min_font_size_ratio(&mut self, v: longhands::_moz_min_font_size_ratio::computed_value::T) { let scaled = v.0 * 100.; let percentage = if scaled > 255. { 255. } else if scaled < 0. { 0. } else { scaled }; self.gecko.mMinFontSizeRatio = percentage as u8; } ${impl_simple_copy('_moz_min_font_size_ratio', 'mMinFontSizeRatio')} <%def name="impl_copy_animation_or_transition_value(type, ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn copy_${type}_${ident}_from(&mut self, other: &Self) { unsafe { self.gecko.m${type.capitalize()}s.ensure_len(other.gecko.m${type.capitalize()}s.len()) }; let count = other.gecko.m${type.capitalize()}${gecko_ffi_name}Count; self.gecko.m${type.capitalize()}${gecko_ffi_name}Count = count; // The length of mTransitions or mAnimations is often greater than m{Transition|Animation}XXCount, // don't copy values over the count. for (index, gecko) in self.gecko.m${type.capitalize()}s.iter_mut().enumerate().take(count as usize) { gecko.m${gecko_ffi_name} = other.gecko.m${type.capitalize()}s[index].m${gecko_ffi_name}; } } #[allow(non_snake_case)] pub fn reset_${type}_${ident}(&mut self, other: &Self) { self.copy_${type}_${ident}_from(other) } <%def name="impl_animation_or_transition_count(type, ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn ${type}_${ident}_count(&self) -> usize { self.gecko.m${type.capitalize()}${gecko_ffi_name}Count as usize } <%def name="impl_animation_or_transition_time_value(type, ident, gecko_ffi_name)"> #[allow(non_snake_case)] pub fn set_${type}_${ident}(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator + Clone { let v = v.into_iter(); debug_assert!(v.len() != 0); let input_len = v.len(); unsafe { self.gecko.m${type.capitalize()}s.ensure_len(input_len) }; self.gecko.m${type.capitalize()}${gecko_ffi_name}Count = input_len as u32; for (gecko, servo) in self.gecko.m${type.capitalize()}s.iter_mut().zip(v.cycle()) { gecko.m${gecko_ffi_name} = servo.seconds() * 1000.; } } #[allow(non_snake_case)] pub fn ${type}_${ident}_at(&self, index: usize) -> longhands::${type}_${ident}::computed_value::SingleComputedValue { use values::computed::Time; Time::from_seconds(self.gecko.m${type.capitalize()}s[index].m${gecko_ffi_name} / 1000.) } ${impl_animation_or_transition_count(type, ident, gecko_ffi_name)} ${impl_copy_animation_or_transition_value(type, ident, gecko_ffi_name)} <%def name="impl_animation_or_transition_timing_function(type)"> pub fn set_${type}_timing_function(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator + Clone { let v = v.into_iter(); debug_assert!(v.len() != 0); let input_len = v.len(); unsafe { self.gecko.m${type.capitalize()}s.ensure_len(input_len) }; self.gecko.m${type.capitalize()}TimingFunctionCount = input_len as u32; for (gecko, servo) in self.gecko.m${type.capitalize()}s.iter_mut().zip(v.cycle()) { gecko.mTimingFunction = servo.into(); } } ${impl_animation_or_transition_count(type, 'timing_function', 'TimingFunction')} ${impl_copy_animation_or_transition_value(type, 'timing_function', 'TimingFunction')} pub fn ${type}_timing_function_at(&self, index: usize) -> longhands::${type}_timing_function::computed_value::SingleComputedValue { self.gecko.m${type.capitalize()}s[index].mTimingFunction.into() } <%def name="impl_transition_time_value(ident, gecko_ffi_name)"> ${impl_animation_or_transition_time_value('transition', ident, gecko_ffi_name)} <%def name="impl_transition_count(ident, gecko_ffi_name)"> ${impl_animation_or_transition_count('transition', ident, gecko_ffi_name)} <%def name="impl_copy_animation_value(ident, gecko_ffi_name)"> ${impl_copy_animation_or_transition_value('animation', ident, gecko_ffi_name)} <%def name="impl_transition_timing_function()"> ${impl_animation_or_transition_timing_function('transition')} <%def name="impl_animation_count(ident, gecko_ffi_name)"> ${impl_animation_or_transition_count('animation', ident, gecko_ffi_name)} <%def name="impl_animation_time_value(ident, gecko_ffi_name)"> ${impl_animation_or_transition_time_value('animation', ident, gecko_ffi_name)} <%def name="impl_animation_timing_function()"> ${impl_animation_or_transition_timing_function('animation')} <%def name="impl_animation_keyword(ident, gecko_ffi_name, keyword, cast_type='u8')"> #[allow(non_snake_case)] pub fn set_animation_${ident}(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator + Clone { use properties::longhands::animation_${ident}::single_value::computed_value::T as Keyword; use gecko_bindings::structs; let v = v.into_iter(); debug_assert!(v.len() != 0); let input_len = v.len(); unsafe { self.gecko.mAnimations.ensure_len(input_len) }; self.gecko.mAnimation${gecko_ffi_name}Count = input_len as u32; for (gecko, servo) in self.gecko.mAnimations.iter_mut().zip(v.cycle()) { 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 transition-duration transition-delay transition-timing-function transition-property page-break-before page-break-after scroll-snap-points-x scroll-snap-points-y transform scroll-snap-type-x scroll-snap-type-y scroll-snap-coordinate perspective-origin transform-origin -moz-binding will-change shape-outside contain touch-action""" %> <%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}"> // We manually-implement the |display| property until we get general // infrastructure for preffing certain values. <% display_keyword = Keyword("display", "inline block inline-block table inline-table table-row-group " + "table-header-group table-footer-group table-row table-column-group " + "table-column table-cell table-caption list-item flex none " + "inline-flex grid inline-grid ruby ruby-base ruby-base-container " + "ruby-text ruby-text-container contents flow-root -webkit-box " + "-webkit-inline-box -moz-box -moz-inline-box -moz-grid -moz-inline-grid " + "-moz-grid-group -moz-grid-line -moz-stack -moz-inline-stack -moz-deck " + "-moz-popup -moz-groupbox", gecko_enum_prefix="StyleDisplay", gecko_strip_moz_prefix=False) %> pub fn set_display(&mut self, v: longhands::display::computed_value::T) { use properties::longhands::display::computed_value::T as Keyword; // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts let result = match v { % for value in display_keyword.values_for('gecko'): Keyword::${to_rust_ident(value)} => structs::${display_keyword.gecko_constant(value)}, % endfor }; self.gecko.mDisplay = result; self.gecko.mOriginalDisplay = result; } /// Set the display value from the style adjustment code. This is pretty /// much like set_display, but without touching the mOriginalDisplay field, /// which we want to keep. pub fn set_adjusted_display(&mut self, v: longhands::display::computed_value::T, _is_item_or_root: bool) { use properties::longhands::display::computed_value::T as Keyword; let result = match v { % for value in display_keyword.values_for('gecko'): Keyword::${to_rust_ident(value)} => structs::${display_keyword.gecko_constant(value)}, % endfor }; self.gecko.mDisplay = result; } pub fn copy_display_from(&mut self, other: &Self) { self.gecko.mDisplay = other.gecko.mDisplay; self.gecko.mOriginalDisplay = other.gecko.mDisplay; } pub fn reset_display(&mut self, other: &Self) { self.copy_display_from(other) } <%call expr="impl_keyword_clone('display', 'mDisplay', display_keyword)"> <% overflow_x = data.longhands_by_name["overflow-x"] %> pub fn set_overflow_y(&mut self, v: longhands::overflow_y::computed_value::T) { use properties::longhands::overflow_x::computed_value::T as BaseType; // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts self.gecko.mOverflowY = match v { % for value in overflow_x.keyword.values_for('gecko'): BaseType::${to_rust_ident(value)} => structs::${overflow_x.keyword.gecko_constant(value)} as u8, % endfor }; } ${impl_simple_copy('overflow_y', 'mOverflowY')} pub fn clone_overflow_y(&self) -> longhands::overflow_y::computed_value::T { use properties::longhands::overflow_x::computed_value::T as BaseType; // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts match self.gecko.mOverflowY as u32 { % for value in overflow_x.keyword.values_for('gecko'): structs::${overflow_x.keyword.gecko_constant(value)} => BaseType::${to_rust_ident(value)}, % endfor x => panic!("Found unexpected value in style struct for overflow_y property: {}", x), } } pub fn set_vertical_align(&mut self, v: longhands::vertical_align::computed_value::T) { use values::generics::box_::VerticalAlign; let value = match v { VerticalAlign::Baseline => structs::NS_STYLE_VERTICAL_ALIGN_BASELINE, VerticalAlign::Sub => structs::NS_STYLE_VERTICAL_ALIGN_SUB, VerticalAlign::Super => structs::NS_STYLE_VERTICAL_ALIGN_SUPER, VerticalAlign::Top => structs::NS_STYLE_VERTICAL_ALIGN_TOP, VerticalAlign::TextTop => structs::NS_STYLE_VERTICAL_ALIGN_TEXT_TOP, VerticalAlign::Middle => structs::NS_STYLE_VERTICAL_ALIGN_MIDDLE, VerticalAlign::Bottom => structs::NS_STYLE_VERTICAL_ALIGN_BOTTOM, VerticalAlign::TextBottom => structs::NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM, VerticalAlign::MozMiddleWithBaseline => { structs::NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE }, VerticalAlign::Length(length) => { self.gecko.mVerticalAlign.set(length); return; }, }; self.gecko.mVerticalAlign.set_value(CoordDataValue::Enumerated(value)); } pub fn clone_vertical_align(&self) -> longhands::vertical_align::computed_value::T { use values::computed::LengthOrPercentage; use values::generics::box_::VerticalAlign; let gecko = &self.gecko.mVerticalAlign; match gecko.as_value() { CoordDataValue::Enumerated(value) => VerticalAlign::from_gecko_keyword(value), _ => { VerticalAlign::Length( LengthOrPercentage::from_gecko_style_coord(gecko).expect( "expected for vertical-align", ), ) }, } } <%call expr="impl_coord_copy('vertical_align', 'mVerticalAlign')"> % for kind in ["before", "after"]: // 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_${kind}(&mut self, v: longhands::page_break_${kind}::computed_value::T) { use computed_values::page_break_${kind}::T; let result = match v { T::auto => false, T::always => true, T::avoid => false, T::left => true, T::right => true }; self.gecko.mBreak${kind.title()} = result; } ${impl_simple_copy('page_break_' + kind, 'mBreak' + kind.title())} // Temp fix for Bugzilla bug 24000. // See set_page_break_before/after for detail. pub fn clone_page_break_${kind}(&self) -> longhands::page_break_${kind}::computed_value::T { use computed_values::page_break_${kind}::T; match self.gecko.mBreak${kind.title()} { true => T::always, false => T::auto, } } % endfor ${impl_style_coord("scroll_snap_points_x", "mScrollSnapPointsX")} ${impl_style_coord("scroll_snap_points_y", "mScrollSnapPointsY")} pub fn set_scroll_snap_coordinate(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator { let v = v.into_iter(); unsafe { self.gecko.mScrollSnapCoordinate.set_len_pod(v.len() as u32); } for (gecko, servo) in self.gecko.mScrollSnapCoordinate .iter_mut() .zip(v) { gecko.mXPosition = servo.horizontal.into(); gecko.mYPosition = servo.vertical.into(); } } pub fn copy_scroll_snap_coordinate_from(&mut self, other: &Self) { unsafe { self.gecko.mScrollSnapCoordinate .set_len_pod(other.gecko.mScrollSnapCoordinate.len() as u32); } for (this, that) in self.gecko.mScrollSnapCoordinate .iter_mut() .zip(other.gecko.mScrollSnapCoordinate.iter()) { *this = *that; } } pub fn reset_scroll_snap_coordinate(&mut self, other: &Self) { self.copy_scroll_snap_coordinate_from(other) } pub fn clone_scroll_snap_coordinate(&self) -> longhands::scroll_snap_coordinate::computed_value::T { let vec = self.gecko.mScrollSnapCoordinate.iter().map(|f| f.into()).collect(); longhands::scroll_snap_coordinate::computed_value::T(vec) } ${impl_css_url('_moz_binding', 'mBinding.mPtr')} <%def name="transform_function_arm(name, keyword, items)"> <% pattern = None if keyword == "matrix3d": # m11: number1, m12: number2, .. single_patterns = ["m%s: %s" % (str(a / 4 + 1) + str(a % 4 + 1), b + str(a + 1)) for (a, b) in enumerate(items)] if name == "Matrix": pattern = "(ComputedMatrix { %s })" % ", ".join(single_patterns) else: pattern = "(ComputedMatrixWithPercents { %s })" % ", ".join(single_patterns) elif keyword == "interpolatematrix": pattern = " { from_list: ref list1, to_list: ref list2, progress: percentage3 }" elif keyword == "accumulatematrix": pattern = " { from_list: ref list1, to_list: ref list2, count: integer_to_percentage3 }" else: # Generate contents of pattern from items pattern = "(%s)" % ", ".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_SetPixelLength(%s, %s.px())", "percentage" : "bindings::Gecko_CSSValue_SetPercentage(%s, %s.0)", # Note: This is an integer type, but we use it as a percentage value in Gecko, so # need to cast it to f32. "integer_to_percentage" : "bindings::Gecko_CSSValue_SetPercentage(%s, %s as f32)", "lop" : "%s.set_lop(%s)", "angle" : "%s.set_angle(%s)", "number" : "bindings::Gecko_CSSValue_SetNumber(%s, %s)", # Note: We use nsCSSValueSharedList here, instead of nsCSSValueList_heap # because this function is not called on the main thread and # nsCSSValueList_heap is not thread safe. "list" : "%s.set_shared_list(%s.0.as_ref().unwrap().into_iter().map(&convert_to_ns_css_value));", } %> longhands::transform::computed_value::ComputedOperation::${name}${pattern} => { bindings::Gecko_CSSValue_SetFunction(gecko_value, ${len(items) + 1}); bindings::Gecko_CSSValue_SetKeyword( bindings::Gecko_CSSValue_GetArrayItem(gecko_value, 0), structs::nsCSSKeyword::eCSSKeyword_${keyword} ); % for index, item in enumerate(items): % if item == "list": debug_assert!(${item}${index + 1}.0.is_some()); % endif ${css_value_setters[item] % ( "bindings::Gecko_CSSValue_GetArrayItem(gecko_value, %d)" % (index + 1), item + str(index + 1) )}; % endfor } fn set_single_transform_function(servo_value: &longhands::transform::computed_value::ComputedOperation, gecko_value: &mut structs::nsCSSValue /* output */) { use properties::longhands::transform::computed_value::ComputedMatrix; use properties::longhands::transform::computed_value::ComputedMatrixWithPercents; use properties::longhands::transform::computed_value::ComputedOperation; let convert_to_ns_css_value = |item: &ComputedOperation| -> structs::nsCSSValue { let mut value = structs::nsCSSValue::null(); Self::set_single_transform_function(item, &mut value); value }; unsafe { match *servo_value { ${transform_function_arm("Matrix", "matrix3d", ["number"] * 16)} ${transform_function_arm("MatrixWithPercents", "matrix3d", ["number"] * 12 + ["lop"] * 2 + ["length"] + ["number"])} ${transform_function_arm("Skew", "skew", ["angle"] * 2)} ${transform_function_arm("Translate", "translate3d", ["lop", "lop", "length"])} ${transform_function_arm("Scale", "scale3d", ["number"] * 3)} ${transform_function_arm("Rotate", "rotate3d", ["number"] * 3 + ["angle"])} ${transform_function_arm("Perspective", "perspective", ["length"])} ${transform_function_arm("InterpolateMatrix", "interpolatematrix", ["list"] * 2 + ["percentage"])} ${transform_function_arm("AccumulateMatrix", "accumulatematrix", ["list"] * 2 + ["integer_to_percentage"])} } } } pub fn convert_transform(input: &[longhands::transform::computed_value::ComputedOperation], output: &mut structs::root::RefPtr) { use gecko_bindings::sugar::refptr::RefPtr; unsafe { output.clear() }; let list = unsafe { RefPtr::from_addrefed(bindings::Gecko_NewCSSValueSharedList(input.len() as u32)) }; let value_list = unsafe { list.mHead.as_mut() }; if let Some(value_list) = value_list { for (gecko, servo) in value_list.into_iter().zip(input.into_iter()) { Self::set_single_transform_function(servo, gecko); } } unsafe { output.set_move(list) }; } pub fn set_transform(&mut self, other: longhands::transform::computed_value::T) { let vec = if let Some(v) = other.0 { v } else { unsafe { self.gecko.mSpecifiedTransform.clear(); } return; }; Self::convert_transform(&vec, &mut self.gecko.mSpecifiedTransform); } pub fn copy_transform_from(&mut self, other: &Self) { unsafe { self.gecko.mSpecifiedTransform.set(&other.gecko.mSpecifiedTransform); } } pub fn reset_transform(&mut self, other: &Self) { self.copy_transform_from(other) } <%def name="computed_operation_arm(name, keyword, items)"> <% # %s is substituted with the call to GetArrayItem. css_value_getters = { "length" : "Length::new(bindings::Gecko_CSSValue_GetNumber(%s))", "lop" : "%s.get_lop()", "angle" : "%s.get_angle()", "number" : "bindings::Gecko_CSSValue_GetNumber(%s)", "percentage" : "Percentage(bindings::Gecko_CSSValue_GetPercentage(%s))", "percentage_to_integer" : "bindings::Gecko_CSSValue_GetPercentage(%s) as i32", "list" : "TransformList(Some(convert_shared_list_to_operations(%s)))", } pre_symbols = "(" post_symbols = ")" if keyword == "interpolatematrix" or keyword == "accumulatematrix": # We generate this like: "ComputedOperation::InterpolateMatrix {", so the space is # between "InterpolateMatrix"/"AccumulateMatrix" and '{' pre_symbols = " {" post_symbols = "}" elif keyword == "matrix3d": pre_symbols = "(ComputedMatrix {" post_symbols = "})" field_names = None if keyword == "interpolatematrix": field_names = ["from_list", "to_list", "progress"] elif keyword == "accumulatematrix": field_names = ["from_list", "to_list", "count"] %> structs::nsCSSKeyword::eCSSKeyword_${keyword} => { ComputedOperation::${name}${pre_symbols} % for index, item in enumerate(items): % if keyword == "matrix3d": m${index / 4 + 1}${index % 4 + 1}: % elif keyword == "interpolatematrix" or keyword == "accumulatematrix": ${field_names[index]}: % endif ${css_value_getters[item] % ( "bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, %d)" % (index + 1) )}, % endfor ${post_symbols} }, fn clone_single_transform_function(gecko_value: &structs::nsCSSValue) -> longhands::transform::computed_value::ComputedOperation { use properties::longhands::transform::computed_value::ComputedMatrix; use properties::longhands::transform::computed_value::ComputedOperation; use properties::longhands::transform::computed_value::T as TransformList; use values::computed::{Length, Percentage}; let convert_shared_list_to_operations = |value: &structs::nsCSSValue| -> Vec { debug_assert!(value.mUnit == structs::nsCSSUnit::eCSSUnit_SharedList); let value_list = unsafe { value.mValue.mSharedList.as_ref() .as_mut().expect("List pointer should be non-null").mHead.as_ref() }; debug_assert!(value_list.is_some(), "An empty shared list is not allowed"); value_list.unwrap().into_iter() .map(|item| Self::clone_single_transform_function(item)) .collect() }; let transform_function = unsafe { bindings::Gecko_CSSValue_GetKeyword(bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 0)) }; unsafe { use gecko_bindings::structs::nsCSSKeyword; use values::computed::Angle; let get_array_angle = || -> Angle { bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, 1).get_angle() }; 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"])} ${computed_operation_arm("InterpolateMatrix", "interpolatematrix", ["list"] * 2 + ["percentage"])} ${computed_operation_arm("AccumulateMatrix", "accumulatematrix", ["list"] * 2 + ["percentage_to_integer"])} // FIXME: Bug 1391145 will introduce new types for these keywords. For now, we // temporarily don't use |computed_operation_arm| because these are special cases // for compositor animations when we use Gecko style backend on the main thread, // and I don't want to add too many special cases in |computed_operation_arm|. // // Note: Gecko only converts translate and scale into the corresponding primitive // functions, so we still need to handle the following functions. nsCSSKeyword::eCSSKeyword_skewx => { ComputedOperation::Skew(get_array_angle(), Angle::zero()) }, nsCSSKeyword::eCSSKeyword_skewy => { ComputedOperation::Skew(Angle::zero(), get_array_angle()) }, nsCSSKeyword::eCSSKeyword_rotatex => { ComputedOperation::Rotate(1.0, 0.0, 0.0, get_array_angle()) }, nsCSSKeyword::eCSSKeyword_rotatey => { ComputedOperation::Rotate(0.0, 1.0, 0.0, get_array_angle()) }, nsCSSKeyword::eCSSKeyword_rotatez | nsCSSKeyword::eCSSKeyword_rotate => { ComputedOperation::Rotate(0.0, 0.0, 1.0, get_array_angle()) }, _ => panic!("{:?} is not an acceptable transform function", transform_function), } } } pub fn clone_transform(&self) -> longhands::transform::computed_value::T { if self.gecko.mSpecifiedTransform.mRawPtr.is_null() { return longhands::transform::computed_value::T(None); } let list = unsafe { (*self.gecko.mSpecifiedTransform.to_safe().get()).mHead.as_ref() }; Self::clone_transform_from_list(list) } pub fn clone_transform_from_list(list: Option< &structs::root::nsCSSValueList>) -> longhands::transform::computed_value::T { let result = match list { Some(list) => { let vec: Vec<_> = list .into_iter() .filter_map(|value| { // Handle none transform. if value.is_none() { None } else { Some(Self::clone_single_transform_function(value)) } }) .collect(); if !vec.is_empty() { Some(vec) } else { None } }, _ => None, }; longhands::transform::computed_value::T(result) } ${impl_transition_time_value('delay', 'Delay')} ${impl_transition_time_value('duration', 'Duration')} ${impl_transition_timing_function()} pub fn transition_combined_duration_at(&self, index: usize) -> f32 { // https://drafts.csswg.org/css-transitions/#transition-combined-duration self.gecko.mTransitions[index].mDuration.max(0.0) + self.gecko.mTransitions[index].mDelay } pub fn set_transition_property(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator { use gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_no_properties; let v = v.into_iter(); if v.len() != 0 { unsafe { self.gecko.mTransitions.ensure_len(v.len()) }; self.gecko.mTransitionPropertyCount = v.len() as u32; for (servo, gecko) in v.zip(self.gecko.mTransitions.iter_mut()) { match servo { TransitionProperty::Unsupported(ref ident) => unsafe { Gecko_StyleTransition_SetUnsupportedProperty(gecko, ident.0.as_ptr()) }, _ => gecko.mProperty = servo.to_nscsspropertyid().unwrap(), } } } else { // In gecko |none| is represented by eCSSPropertyExtra_no_properties. self.gecko.mTransitionPropertyCount = 1; self.gecko.mTransitions[0].mProperty = eCSSPropertyExtra_no_properties; } } /// Returns whether there are any transitions specified. pub fn specifies_transitions(&self) -> bool { use gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_all_properties; if self.gecko.mTransitionPropertyCount == 1 && self.gecko.mTransitions[0].mProperty == eCSSPropertyExtra_all_properties && self.gecko.mTransitions[0].mDuration.max(0.0) + self.gecko.mTransitions[0].mDelay <= 0.0f32 { return false; } self.gecko.mTransitionPropertyCount > 0 } pub fn transition_property_at(&self, index: usize) -> longhands::transition_property::computed_value::SingleComputedValue { use gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_no_properties; use gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable; use gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN; let property = self.gecko.mTransitions[index].mProperty; if property == eCSSProperty_UNKNOWN || property == eCSSPropertyExtra_variable { let atom = self.gecko.mTransitions[index].mUnknownProperty.mRawPtr; debug_assert!(!atom.is_null()); TransitionProperty::Unsupported(CustomIdent(atom.into())) } else if property == eCSSPropertyExtra_no_properties { // Actually, we don't expect TransitionProperty::Unsupported also represents "none", // but if the caller wants to convert it, it is fine. Please use it carefully. TransitionProperty::Unsupported(CustomIdent(atom!("none"))) } else { property.into() } } pub fn transition_nscsspropertyid_at(&self, index: usize) -> nsCSSPropertyID { self.gecko.mTransitions[index].mProperty } pub fn copy_transition_property_from(&mut self, other: &Self) { use gecko_bindings::structs::nsCSSPropertyID::eCSSPropertyExtra_variable; use gecko_bindings::structs::nsCSSPropertyID::eCSSProperty_UNKNOWN; unsafe { self.gecko.mTransitions.ensure_len(other.gecko.mTransitions.len()) }; let count = other.gecko.mTransitionPropertyCount; self.gecko.mTransitionPropertyCount = count; for (index, transition) in self.gecko.mTransitions.iter_mut().enumerate().take(count as usize) { transition.mProperty = other.gecko.mTransitions[index].mProperty; if transition.mProperty == eCSSProperty_UNKNOWN || transition.mProperty == eCSSPropertyExtra_variable { let atom = other.gecko.mTransitions[index].mUnknownProperty.mRawPtr; debug_assert!(!atom.is_null()); unsafe { Gecko_StyleTransition_SetUnsupportedProperty(transition, atom) }; } } } pub fn reset_transition_property(&mut self, other: &Self) { self.copy_transition_property_from(other) } ${impl_transition_count('property', 'Property')} pub fn animations_equals(&self, other: &Self) -> bool { unsafe { bindings::Gecko_StyleAnimationsEquals(&self.gecko.mAnimations, &other.gecko.mAnimations) } } pub fn set_animation_name(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator { let v = v.into_iter(); debug_assert!(v.len() != 0); unsafe { self.gecko.mAnimations.ensure_len(v.len()) }; self.gecko.mAnimationNameCount = v.len() as u32; for (servo, gecko) in v.zip(self.gecko.mAnimations.iter_mut()) { let atom = match servo.0 { None => atom!(""), Some(ref name) => name.as_atom().clone(), }; unsafe { bindings::Gecko_SetAnimationName(gecko, atom.into_addrefed()); } } } pub fn animation_name_at(&self, index: usize) -> longhands::animation_name::computed_value::SingleComputedValue { use properties::longhands::animation_name::single_value::SpecifiedValue as AnimationName; let atom = self.gecko.mAnimations[index].mName.mRawPtr; if atom == atom!("").as_ptr() { AnimationName(None) } else { AnimationName(Some(KeyframesName::from_atom(atom.into()))) } } pub fn copy_animation_name_from(&mut self, other: &Self) { self.gecko.mAnimationNameCount = other.gecko.mAnimationNameCount; unsafe { bindings::Gecko_CopyAnimationNames(&mut self.gecko.mAnimations, &other.gecko.mAnimations); } } pub fn reset_animation_name(&mut self, other: &Self) { self.copy_animation_name_from(other) } ${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: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator + Clone { use std::f32; use values::generics::box_::AnimationIterationCount; let v = v.into_iter(); debug_assert_ne!(v.len(), 0); let input_len = v.len(); unsafe { self.gecko.mAnimations.ensure_len(input_len) }; self.gecko.mAnimationIterationCountCount = input_len as u32; for (gecko, servo) in self.gecko.mAnimations.iter_mut().zip(v.cycle()) { match servo { AnimationIterationCount::Number(n) => gecko.mIterationCount = n, AnimationIterationCount::Infinite => gecko.mIterationCount = f32::INFINITY, } } } pub fn animation_iteration_count_at( &self, index: usize, ) -> values::computed::AnimationIterationCount { use values::generics::box_::AnimationIterationCount; if self.gecko.mAnimations[index].mIterationCount.is_infinite() { AnimationIterationCount::Infinite } else { AnimationIterationCount::Number(self.gecko.mAnimations[index].mIterationCount) } } ${impl_animation_count('iteration_count', 'IterationCount')} ${impl_copy_animation_value('iteration_count', 'IterationCount')} ${impl_animation_timing_function()} <% scroll_snap_type_keyword = Keyword("scroll-snap-type", "None Mandatory Proximity") %> ${impl_keyword('scroll_snap_type_y', 'mScrollSnapTypeY', scroll_snap_type_keyword)} ${impl_keyword('scroll_snap_type_x', 'mScrollSnapTypeX', scroll_snap_type_keyword)} 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 reset_perspective_origin(&mut self, other: &Self) { self.copy_perspective_origin_from(other) } pub fn clone_perspective_origin(&self) -> longhands::perspective_origin::computed_value::T { use properties::longhands::perspective_origin::computed_value::T; use values::computed::LengthOrPercentage; T { horizontal: LengthOrPercentage::from_gecko_style_coord(&self.gecko.mPerspectiveOrigin[0]) .expect("Expected length or percentage for horizontal value of perspective-origin"), vertical: LengthOrPercentage::from_gecko_style_coord(&self.gecko.mPerspectiveOrigin[1]) .expect("Expected length or percentage for vertical value of perspective-origin"), } } pub fn set_transform_origin(&mut self, v: longhands::transform_origin::computed_value::T) { self.gecko.mTransformOrigin[0].set(v.horizontal); self.gecko.mTransformOrigin[1].set(v.vertical); self.gecko.mTransformOrigin[2].set(v.depth); } pub fn copy_transform_origin_from(&mut self, other: &Self) { self.gecko.mTransformOrigin[0].copy_from(&other.gecko.mTransformOrigin[0]); self.gecko.mTransformOrigin[1].copy_from(&other.gecko.mTransformOrigin[1]); self.gecko.mTransformOrigin[2].copy_from(&other.gecko.mTransformOrigin[2]); } pub fn reset_transform_origin(&mut self, other: &Self) { self.copy_transform_origin_from(other) } pub fn clone_transform_origin(&self) -> longhands::transform_origin::computed_value::T { use properties::longhands::transform_origin::computed_value::T; use values::computed::{Length, 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: Length::from_gecko_style_coord(&self.gecko.mTransformOrigin[2]) .expect("clone for Length failed"), } } pub fn set_will_change(&mut self, v: longhands::will_change::computed_value::T) { use gecko_bindings::bindings::{Gecko_AppendWillChange, Gecko_ClearWillChange}; use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_OPACITY; use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_SCROLL; use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_TRANSFORM; use properties::PropertyId; use properties::longhands::will_change::computed_value::T; fn will_change_bitfield_from_prop_flags(prop: &LonghandId) -> u8 { use properties::PropertyFlags; use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_ABSPOS_CB; use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_FIXPOS_CB; use gecko_bindings::structs::NS_STYLE_WILL_CHANGE_STACKING_CONTEXT; let servo_flags = prop.flags(); let mut bitfield = 0; if servo_flags.contains(PropertyFlags::CREATES_STACKING_CONTEXT) { bitfield |= NS_STYLE_WILL_CHANGE_STACKING_CONTEXT; } if servo_flags.contains(PropertyFlags::FIXPOS_CB) { bitfield |= NS_STYLE_WILL_CHANGE_FIXPOS_CB; } if servo_flags.contains(PropertyFlags::ABSPOS_CB) { bitfield |= NS_STYLE_WILL_CHANGE_ABSPOS_CB; } bitfield as u8 } self.gecko.mWillChangeBitField = 0; match v { T::AnimateableFeatures(features) => { unsafe { Gecko_ClearWillChange(&mut self.gecko, features.len()); } for feature in features.iter() { if feature.0 == atom!("scroll-position") { self.gecko.mWillChangeBitField |= NS_STYLE_WILL_CHANGE_SCROLL as u8; } else if feature.0 == atom!("opacity") { self.gecko.mWillChangeBitField |= NS_STYLE_WILL_CHANGE_OPACITY as u8; } else if feature.0 == atom!("transform") { self.gecko.mWillChangeBitField |= NS_STYLE_WILL_CHANGE_TRANSFORM as u8; } unsafe { Gecko_AppendWillChange(&mut self.gecko, feature.0.as_ptr()); } if let Ok(prop_id) = PropertyId::parse(&feature.0.to_string(), None) { match prop_id.as_shorthand() { Ok(shorthand) => { for longhand in shorthand.longhands() { self.gecko.mWillChangeBitField |= will_change_bitfield_from_prop_flags(longhand); } }, Err(longhand_or_custom) => { if let PropertyDeclarationId::Longhand(longhand) = longhand_or_custom { self.gecko.mWillChangeBitField |= will_change_bitfield_from_prop_flags(&longhand); } }, } } } }, T::Auto => { unsafe { Gecko_ClearWillChange(&mut self.gecko, 0); } }, }; } pub fn copy_will_change_from(&mut self, other: &Self) { use gecko_bindings::bindings::Gecko_CopyWillChangeFrom; self.gecko.mWillChangeBitField = other.gecko.mWillChangeBitField; unsafe { Gecko_CopyWillChangeFrom(&mut self.gecko, &other.gecko as *const _ as *mut _); } } pub fn reset_will_change(&mut self, other: &Self) { self.copy_will_change_from(other) } pub fn clone_will_change(&self) -> longhands::will_change::computed_value::T { use properties::longhands::will_change::computed_value::T; use gecko_bindings::structs::nsAtom; use values::CustomIdent; if self.gecko.mWillChange.len() == 0 { T::Auto } else { T::AnimateableFeatures( self.gecko.mWillChange.iter().map(|gecko_atom| { CustomIdent((gecko_atom.mRawPtr as *mut nsAtom).into()) }).collect() ) } } <% impl_shape_source("shape_outside", "mShapeOutside") %> pub fn set_contain(&mut self, v: longhands::contain::computed_value::T) { use gecko_bindings::structs::NS_STYLE_CONTAIN_NONE; use gecko_bindings::structs::NS_STYLE_CONTAIN_STRICT; use gecko_bindings::structs::NS_STYLE_CONTAIN_LAYOUT; use gecko_bindings::structs::NS_STYLE_CONTAIN_STYLE; use gecko_bindings::structs::NS_STYLE_CONTAIN_PAINT; use gecko_bindings::structs::NS_STYLE_CONTAIN_ALL_BITS; use properties::longhands::contain::SpecifiedValue; if v.is_empty() { self.gecko.mContain = NS_STYLE_CONTAIN_NONE as u8; return; } if v.contains(SpecifiedValue::STRICT) { self.gecko.mContain = (NS_STYLE_CONTAIN_STRICT | NS_STYLE_CONTAIN_ALL_BITS) as u8; return; } let mut bitfield = 0; if v.contains(SpecifiedValue::LAYOUT) { bitfield |= NS_STYLE_CONTAIN_LAYOUT; } if v.contains(SpecifiedValue::STYLE) { bitfield |= NS_STYLE_CONTAIN_STYLE; } if v.contains(SpecifiedValue::PAINT) { bitfield |= NS_STYLE_CONTAIN_PAINT; } self.gecko.mContain = bitfield as u8; } pub fn clone_contain(&self) -> longhands::contain::computed_value::T { use gecko_bindings::structs::NS_STYLE_CONTAIN_STRICT; use gecko_bindings::structs::NS_STYLE_CONTAIN_LAYOUT; use gecko_bindings::structs::NS_STYLE_CONTAIN_STYLE; use gecko_bindings::structs::NS_STYLE_CONTAIN_PAINT; use gecko_bindings::structs::NS_STYLE_CONTAIN_ALL_BITS; use properties::longhands::contain::{self, SpecifiedValue}; let mut servo_flags = contain::computed_value::T::empty(); let gecko_flags = self.gecko.mContain; if gecko_flags & (NS_STYLE_CONTAIN_STRICT as u8) != 0 && gecko_flags & (NS_STYLE_CONTAIN_ALL_BITS as u8) != 0 { servo_flags.insert(SpecifiedValue::STRICT | SpecifiedValue::STRICT_BITS); return servo_flags; } if gecko_flags & (NS_STYLE_CONTAIN_LAYOUT as u8) != 0 { servo_flags.insert(SpecifiedValue::LAYOUT); } if gecko_flags & (NS_STYLE_CONTAIN_STYLE as u8) != 0{ servo_flags.insert(SpecifiedValue::STYLE); } if gecko_flags & (NS_STYLE_CONTAIN_PAINT as u8) != 0 { servo_flags.insert(SpecifiedValue::PAINT); } return servo_flags; } ${impl_simple_copy("contain", "mContain")} ${impl_simple_type_with_conversion("touch_action")} <%def name="simple_image_array_property(name, shorthand, field_name)"> <% image_layers_field = "mImage" if shorthand == "background" else "mMask" copy_simple_image_array_property(name, shorthand, image_layers_field, field_name) %> pub fn set_${shorthand}_${name}(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator { use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; let v = v.into_iter(); unsafe { Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, v.len(), LayerType::${shorthand.title()}); } self.gecko.${image_layers_field}.${field_name}Count = v.len() as u32; for (servo, geckolayer) in v.zip(self.gecko.${image_layers_field}.mLayers.iter_mut()) { geckolayer.${field_name} = { ${caller.body()} }; } } <%def name="copy_simple_image_array_property(name, shorthand, layers_field_name, field_name)"> pub fn copy_${shorthand}_${name}_from(&mut self, other: &Self) { use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; let count = other.gecko.${layers_field_name}.${field_name}Count; unsafe { Gecko_EnsureImageLayersLength(&mut self.gecko.${layers_field_name}, count as usize, LayerType::${shorthand.title()}); } for (layer, other) in self.gecko.${layers_field_name}.mLayers.iter_mut() .zip(other.gecko.${layers_field_name}.mLayers.iter()) .take(count as usize) { layer.${field_name} = other.${field_name}; } self.gecko.${layers_field_name}.${field_name}Count = count; } pub fn reset_${shorthand}_${name}(&mut self, other: &Self) { self.copy_${shorthand}_${name}_from(other) } <%def name="impl_simple_image_array_property(name, shorthand, layer_field_name, field_name, struct_name)"> <% ident = "%s_%s" % (shorthand, name) style_struct = next(x for x in data.style_structs if x.name == struct_name) longhand = next(x for x in style_struct.longhands if x.ident == ident) keyword = longhand.keyword %> <% copy_simple_image_array_property(name, shorthand, layer_field_name, field_name) %> pub fn set_${ident}(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator { use properties::longhands::${ident}::single_value::computed_value::T as Keyword; use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; let v = v.into_iter(); unsafe { Gecko_EnsureImageLayersLength(&mut self.gecko.${layer_field_name}, v.len(), LayerType::${shorthand.title()}); } self.gecko.${layer_field_name}.${field_name}Count = v.len() as u32; for (servo, geckolayer) in v.zip(self.gecko.${layer_field_name}.mLayers.iter_mut()) { geckolayer.${field_name} = { match servo { % for value in keyword.values_for("gecko"): Keyword::${to_rust_ident(value)} => structs::${keyword.gecko_constant(value)} ${keyword.maybe_cast('u8')}, % endfor } }; } } pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { use properties::longhands::${ident}::single_value::computed_value::T as Keyword; % if keyword.needs_cast(): % for value in keyword.values_for('gecko'): const ${keyword.casted_constant_name(value, "u8")} : u8 = structs::${keyword.gecko_constant(value)} as u8; % endfor % endif longhands::${ident}::computed_value::T ( self.gecko.${layer_field_name}.mLayers.iter() .take(self.gecko.${layer_field_name}.${field_name}Count as usize) .map(|ref layer| { match layer.${field_name} { % for value in longhand.keyword.values_for("gecko"): % if keyword.needs_cast(): ${keyword.casted_constant_name(value, "u8")} % else: structs::${keyword.gecko_constant(value)} % endif => Keyword::${to_rust_ident(value)}, % endfor x => panic!("Found unexpected value in style struct for ${ident} property: {:?}", x), } }).collect() ) } <%def name="impl_common_image_layer_properties(shorthand)"> <% if shorthand == "background": image_layers_field = "mImage" struct_name = "Background" else: image_layers_field = "mMask" struct_name = "SVG" %> <%self:simple_image_array_property name="repeat" shorthand="${shorthand}" field_name="mRepeat"> use values::specified::background::RepeatKeyword; use gecko_bindings::structs::nsStyleImageLayers_Repeat; use gecko_bindings::structs::StyleImageLayerRepeat; fn to_ns(repeat: RepeatKeyword) -> StyleImageLayerRepeat { match repeat { RepeatKeyword::Repeat => StyleImageLayerRepeat::Repeat, RepeatKeyword::Space => StyleImageLayerRepeat::Space, RepeatKeyword::Round => StyleImageLayerRepeat::Round, RepeatKeyword::NoRepeat => StyleImageLayerRepeat::NoRepeat, } } let repeat_x = to_ns(servo.0); let repeat_y = to_ns(servo.1); nsStyleImageLayers_Repeat { mXRepeat: repeat_x, mYRepeat: repeat_y, } pub fn clone_${shorthand}_repeat(&self) -> longhands::${shorthand}_repeat::computed_value::T { use properties::longhands::${shorthand}_repeat::single_value::computed_value::T; use values::specified::background::RepeatKeyword; use gecko_bindings::structs::StyleImageLayerRepeat; fn to_servo(repeat: StyleImageLayerRepeat) -> RepeatKeyword { match repeat { StyleImageLayerRepeat::Repeat => RepeatKeyword::Repeat, StyleImageLayerRepeat::Space => RepeatKeyword::Space, StyleImageLayerRepeat::Round => RepeatKeyword::Round, StyleImageLayerRepeat::NoRepeat => RepeatKeyword::NoRepeat, x => panic!("Found unexpected value in style struct for ${shorthand}_repeat property: {:?}", x), } } longhands::${shorthand}_repeat::computed_value::T ( self.gecko.${image_layers_field}.mLayers.iter() .take(self.gecko.${image_layers_field}.mRepeatCount as usize) .map(|ref layer| { T(to_servo(layer.mRepeat.mXRepeat), to_servo(layer.mRepeat.mYRepeat)) }).collect() ) } <% impl_simple_image_array_property("clip", shorthand, image_layers_field, "mClip", struct_name) %> <% impl_simple_image_array_property("origin", shorthand, image_layers_field, "mOrigin", struct_name) %> % for orientation in ["x", "y"]: pub fn copy_${shorthand}_position_${orientation}_from(&mut self, other: &Self) { use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; let count = other.gecko.${image_layers_field}.mPosition${orientation.upper()}Count; unsafe { Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, count as usize, LayerType::${shorthand.capitalize()}); } for (layer, other) in self.gecko.${image_layers_field}.mLayers.iter_mut() .zip(other.gecko.${image_layers_field}.mLayers.iter()) .take(count as usize) { layer.mPosition.m${orientation.upper()}Position = other.mPosition.m${orientation.upper()}Position; } self.gecko.${image_layers_field}.mPosition${orientation.upper()}Count = count; } pub fn reset_${shorthand}_position_${orientation}(&mut self, other: &Self) { self.copy_${shorthand}_position_${orientation}_from(other) } pub fn clone_${shorthand}_position_${orientation}(&self) -> longhands::${shorthand}_position_${orientation}::computed_value::T { longhands::${shorthand}_position_${orientation}::computed_value::T( self.gecko.${image_layers_field}.mLayers.iter() .take(self.gecko.${image_layers_field}.mPosition${orientation.upper()}Count as usize) .map(|position| position.mPosition.m${orientation.upper()}Position.into()) .collect() ) } pub fn set_${shorthand}_position_${orientation[0]}(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator { use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; let v = v.into_iter(); unsafe { Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, v.len(), LayerType::${shorthand.capitalize()}); } self.gecko.${image_layers_field}.mPosition${orientation[0].upper()}Count = v.len() as u32; for (servo, geckolayer) in v.zip(self.gecko.${image_layers_field} .mLayers.iter_mut()) { geckolayer.mPosition.m${orientation[0].upper()}Position = servo.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 values::generics::background::BackgroundSize; let mut width = nsStyleCoord_CalcValue::new(); let mut height = nsStyleCoord_CalcValue::new(); let (w_type, h_type) = match servo { BackgroundSize::Explicit { width: explicit_width, height: explicit_height } => { let mut w_type = nsStyleImageLayers_Size_DimensionType::eAuto; let mut h_type = nsStyleImageLayers_Size_DimensionType::eAuto; if let Some(w) = explicit_width.to_calc_value() { width = w; w_type = nsStyleImageLayers_Size_DimensionType::eLengthPercentage; } if let Some(h) = explicit_height.to_calc_value() { height = h; h_type = nsStyleImageLayers_Size_DimensionType::eLengthPercentage; } (w_type, h_type) } BackgroundSize::Cover => { ( nsStyleImageLayers_Size_DimensionType::eCover, nsStyleImageLayers_Size_DimensionType::eCover, ) }, BackgroundSize::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 values::computed::LengthOrPercentageOrAuto; use values::generics::background::BackgroundSize; fn to_servo(value: CalcValue, ty: u8) -> LengthOrPercentageOrAuto { if ty == DimensionType::eAuto as u8 { LengthOrPercentageOrAuto::Auto } else { debug_assert!(ty == DimensionType::eLengthPercentage as u8); 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 BackgroundSize::Cover } if DimensionType::eContain as u8 == layer.mSize.mWidthType { debug_assert!(layer.mSize.mHeightType == DimensionType::eContain as u8); return BackgroundSize::Contain } BackgroundSize::Explicit { 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) { use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; unsafe { let count = other.gecko.${image_layers_field}.mImageCount; unsafe { Gecko_EnsureImageLayersLength(&mut self.gecko.${image_layers_field}, count as usize, LayerType::${shorthand.capitalize()}); } for (layer, other) in self.gecko.${image_layers_field}.mLayers.iter_mut() .zip(other.gecko.${image_layers_field}.mLayers.iter()) .take(count as usize) { Gecko_CopyImageValueFrom(&mut layer.mImage, &other.mImage); } self.gecko.${image_layers_field}.mImageCount = count; } } pub fn reset_${shorthand}_image(&mut self, other: &Self) { self.copy_${shorthand}_image_from(other) } #[allow(unused_variables)] pub fn set_${shorthand}_image(&mut self, images: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator { use gecko_bindings::structs::nsStyleImageLayers_LayerType as LayerType; let images = images.into_iter(); 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.len(), LayerType::${shorthand.title()}); } self.gecko.${image_layers_field}.mImageCount = images.len() as u32; for (image, geckoimage) in images.zip(self.gecko.${image_layers_field} .mLayers.iter_mut()) { if let Either::Second(image) = image { geckoimage.mImage.set(image) } } } pub fn clone_${shorthand}_image(&self) -> longhands::${shorthand}_image::computed_value::T { use values::None_; longhands::${shorthand}_image::computed_value::T( self.gecko.${image_layers_field}.mLayers.iter() .take(self.gecko.${image_layers_field}.mImageCount as usize) .map(|ref layer| { match unsafe { layer.mImage.into_image() } { Some(image) => Either::Second(image), None => Either::First(None_), } }).collect() ) } <% fill_fields = "mRepeat mClip mOrigin mPositionX mPositionY mImage mSize" if shorthand == "background": fill_fields += " mAttachment mBlendMode" else: # mSourceURI uses mImageCount fill_fields += " mMaskMode mComposite" %> pub fn fill_arrays(&mut self) { use gecko_bindings::bindings::Gecko_FillAll${shorthand.title()}Lists; use std::cmp; let mut max_len = 1; % for member in fill_fields.split(): max_len = cmp::max(max_len, self.gecko.${image_layers_field}.${member}Count); % endfor unsafe { // While we could do this manually, we'd need to also manually // run all the copy constructors, so we just delegate to gecko Gecko_FillAll${shorthand.title()}Lists(&mut self.gecko.${image_layers_field}, max_len); } } // TODO: Gecko accepts lists in most background-related properties. We just use // the first element (which is the common case), but at some point we want to // add support for parsing these lists in servo and pushing to nsTArray's. <% skip_background_longhands = """background-repeat background-image background-clip background-origin background-attachment background-size background-position background-blend-mode background-position-x background-position-y""" %> <%self:impl_trait style_struct_name="Background" skip_longhands="${skip_background_longhands}"> <% impl_common_image_layer_properties("background") %> <% impl_simple_image_array_property("attachment", "background", "mImage", "mAttachment", "Background") %> <% impl_simple_image_array_property("blend_mode", "background", "mImage", "mBlendMode", "Background") %> <%self:impl_trait style_struct_name="List" skip_longhands="list-style-image list-style-type quotes -moz-image-region"> pub fn set_list_style_image(&mut self, image: longhands::list_style_image::computed_value::T) { use values::Either; match image { longhands::list_style_image::computed_value::T(Either::Second(_none)) => { unsafe { Gecko_SetListStyleImageNone(&mut self.gecko); } } longhands::list_style_image::computed_value::T(Either::First(ref url)) => { unsafe { Gecko_SetListStyleImageImageValue(&mut self.gecko, url.image_value.clone().unwrap().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); } } pub fn reset_list_style_image(&mut self, other: &Self) { self.copy_list_style_image_from(other) } pub fn clone_list_style_image(&self) -> longhands::list_style_image::computed_value::T { use values::specified::url::SpecifiedUrl; use values::{Either, None_}; longhands::list_style_image::computed_value::T( match self.gecko.mListStyleImage.mRawPtr.is_null() { true => Either::Second(None_), false => { unsafe { let ref gecko_image_request = *self.gecko.mListStyleImage.mRawPtr; Either::First(SpecifiedUrl::from_image_request(gecko_image_request) .expect("mListStyleImage could not convert to SpecifiedUrl")) } } } ) } pub fn set_list_style_type(&mut self, v: longhands::list_style_type::computed_value::T, device: &Device) { use gecko_bindings::bindings::Gecko_SetCounterStyleToString; use nsstring::{nsACString, nsCStr}; use self::longhands::list_style_type::computed_value::T; match v { T::CounterStyle(s) => s.to_gecko_value(&mut self.gecko.mCounterStyle, device), T::String(s) => unsafe { Gecko_SetCounterStyleToString(&mut self.gecko.mCounterStyle, &nsCStr::from(&s) as &nsACString) } } } pub fn copy_list_style_type_from(&mut self, other: &Self) { unsafe { Gecko_CopyCounterStyle(&mut self.gecko.mCounterStyle, &other.gecko.mCounterStyle); } } pub fn reset_list_style_type(&mut self, other: &Self) { self.copy_list_style_type_from(other) } pub fn clone_list_style_type(&self) -> longhands::list_style_type::computed_value::T { use self::longhands::list_style_type::computed_value::T; use values::Either; use values::generics::CounterStyleOrNone; let result = CounterStyleOrNone::from_gecko_value(&self.gecko.mCounterStyle); match result { Either::First(counter_style) => T::CounterStyle(counter_style), Either::Second(string) => T::String(string), } } pub fn set_quotes(&mut self, other: longhands::quotes::computed_value::T) { use gecko_bindings::bindings::Gecko_NewStyleQuoteValues; use gecko_bindings::sugar::refptr::UniqueRefPtr; let mut refptr = unsafe { UniqueRefPtr::from_addrefed(Gecko_NewStyleQuoteValues(other.0.len() as u32)) }; for (servo, gecko) in other.0.into_iter().zip(refptr.mQuotePairs.iter_mut()) { gecko.first.assign_utf8(&servo.0); gecko.second.assign_utf8(&servo.1); } unsafe { self.gecko.mQuotes.set_move(refptr.get()) } } pub fn copy_quotes_from(&mut self, other: &Self) { unsafe { self.gecko.mQuotes.set(&other.gecko.mQuotes); } } pub fn reset_quotes(&mut self, other: &Self) { self.copy_quotes_from(other) } pub fn clone_quotes(&self) -> longhands::quotes::computed_value::T { unsafe { let ref gecko_quote_values = *self.gecko.mQuotes.mRawPtr; longhands::quotes::computed_value::T( gecko_quote_values.mQuotePairs.iter().map(|gecko_pair| { (gecko_pair.first.to_string(), gecko_pair.second.to_string()) }).collect() ) } } #[allow(non_snake_case)] pub fn set__moz_image_region(&mut self, v: longhands::_moz_image_region::computed_value::T) { use values::Either; match v { Either::Second(_auto) => { self.gecko.mImageRegion.x = 0; self.gecko.mImageRegion.y = 0; self.gecko.mImageRegion.width = 0; self.gecko.mImageRegion.height = 0; } Either::First(rect) => { self.gecko.mImageRegion.x = rect.left.map(Au::from).unwrap_or(Au(0)).0; self.gecko.mImageRegion.y = rect.top.map(Au::from).unwrap_or(Au(0)).0; self.gecko.mImageRegion.height = match rect.bottom { Some(value) => (Au::from(value) - Au(self.gecko.mImageRegion.y)).0, None => 0, }; self.gecko.mImageRegion.width = match rect.right { Some(value) => (Au::from(value) - Au(self.gecko.mImageRegion.x)).0, None => 0, }; } } } #[allow(non_snake_case)] pub fn clone__moz_image_region(&self) -> longhands::_moz_image_region::computed_value::T { use values::{Auto, Either}; use values::computed::ClipRect; // There is no ideal way to detect auto type for structs::nsRect and its components, so // if all components are zero, we use Auto. if self.gecko.mImageRegion.x == 0 && self.gecko.mImageRegion.y == 0 && self.gecko.mImageRegion.width == 0 && self.gecko.mImageRegion.height == 0 { return Either::Second(Auto); } Either::First(ClipRect { top: Some(Au(self.gecko.mImageRegion.y).into()), right: Some(Au(self.gecko.mImageRegion.width + self.gecko.mImageRegion.x).into()), bottom: Some(Au(self.gecko.mImageRegion.height + self.gecko.mImageRegion.y).into()), left: Some(Au(self.gecko.mImageRegion.x).into()), }) } ${impl_simple_copy('_moz_image_region', 'mImageRegion')} <%self:impl_trait style_struct_name="Table" skip_longhands="-x-span"> #[allow(non_snake_case)] pub fn set__x_span(&mut self, v: longhands::_x_span::computed_value::T) { self.gecko.mSpan = v.0 } ${impl_simple_copy('_x_span', 'mSpan')} <%self:impl_trait style_struct_name="Effects" skip_longhands="box-shadow clip filter"> pub fn set_box_shadow(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator { let v = v.into_iter(); self.gecko.mBoxShadow.replace_with_new(v.len() as u32); for (servo, gecko_shadow) in v.zip(self.gecko.mBoxShadow.iter_mut()) { gecko_shadow.set_from_box_shadow(servo); } } pub fn copy_box_shadow_from(&mut self, other: &Self) { self.gecko.mBoxShadow.copy_from(&other.gecko.mBoxShadow); } pub fn reset_box_shadow(&mut self, other: &Self) { self.copy_box_shadow_from(other) } pub fn clone_box_shadow(&self) -> longhands::box_shadow::computed_value::T { let buf = self.gecko.mBoxShadow.iter().map(|v| v.to_box_shadow()).collect(); longhands::box_shadow::computed_value::T(buf) } pub fn set_clip(&mut self, v: longhands::clip::computed_value::T) { use gecko_bindings::structs::NS_STYLE_CLIP_AUTO; use gecko_bindings::structs::NS_STYLE_CLIP_RECT; use gecko_bindings::structs::NS_STYLE_CLIP_LEFT_AUTO; use gecko_bindings::structs::NS_STYLE_CLIP_TOP_AUTO; use gecko_bindings::structs::NS_STYLE_CLIP_RIGHT_AUTO; use gecko_bindings::structs::NS_STYLE_CLIP_BOTTOM_AUTO; use values::Either; match v { Either::First(rect) => { self.gecko.mClipFlags = NS_STYLE_CLIP_RECT as u8; if let Some(left) = rect.left { self.gecko.mClip.x = left.to_i32_au(); } else { self.gecko.mClip.x = 0; self.gecko.mClipFlags |= NS_STYLE_CLIP_LEFT_AUTO as u8; } if let Some(top) = rect.top { self.gecko.mClip.y = top.to_i32_au(); } else { self.gecko.mClip.y = 0; self.gecko.mClipFlags |= NS_STYLE_CLIP_TOP_AUTO as u8; } if let Some(bottom) = rect.bottom { self.gecko.mClip.height = (Au::from(bottom) - Au(self.gecko.mClip.y)).0; } else { self.gecko.mClip.height = 1 << 30; // NS_MAXSIZE self.gecko.mClipFlags |= NS_STYLE_CLIP_BOTTOM_AUTO as u8; } if let Some(right) = rect.right { self.gecko.mClip.width = (Au::from(right) - Au(self.gecko.mClip.x)).0; } else { self.gecko.mClip.width = 1 << 30; // NS_MAXSIZE self.gecko.mClipFlags |= NS_STYLE_CLIP_RIGHT_AUTO as u8; } }, Either::Second(_auto) => { self.gecko.mClipFlags = NS_STYLE_CLIP_AUTO as u8; self.gecko.mClip.x = 0; self.gecko.mClip.y = 0; self.gecko.mClip.width = 0; self.gecko.mClip.height = 0; } } } pub fn copy_clip_from(&mut self, other: &Self) { self.gecko.mClip = other.gecko.mClip; self.gecko.mClipFlags = other.gecko.mClipFlags; } pub fn reset_clip(&mut self, other: &Self) { self.copy_clip_from(other) } pub fn clone_clip(&self) -> longhands::clip::computed_value::T { use gecko_bindings::structs::NS_STYLE_CLIP_AUTO; use gecko_bindings::structs::NS_STYLE_CLIP_BOTTOM_AUTO; use gecko_bindings::structs::NS_STYLE_CLIP_LEFT_AUTO; use gecko_bindings::structs::NS_STYLE_CLIP_RIGHT_AUTO; use gecko_bindings::structs::NS_STYLE_CLIP_TOP_AUTO; use values::computed::{ClipRect, ClipRectOrAuto}; use values::Either; if self.gecko.mClipFlags == NS_STYLE_CLIP_AUTO as u8 { ClipRectOrAuto::auto() } else { let left = if self.gecko.mClipFlags & NS_STYLE_CLIP_LEFT_AUTO as u8 != 0 { debug_assert!(self.gecko.mClip.x == 0); None } else { Some(Au(self.gecko.mClip.x).into()) }; let top = if self.gecko.mClipFlags & NS_STYLE_CLIP_TOP_AUTO as u8 != 0 { debug_assert!(self.gecko.mClip.y == 0); None } else { Some(Au(self.gecko.mClip.y).into()) }; let bottom = if self.gecko.mClipFlags & NS_STYLE_CLIP_BOTTOM_AUTO as u8 != 0 { debug_assert!(self.gecko.mClip.height == 1 << 30); // NS_MAXSIZE None } else { Some(Au(self.gecko.mClip.y + self.gecko.mClip.height).into()) }; let right = if self.gecko.mClipFlags & NS_STYLE_CLIP_RIGHT_AUTO as u8 != 0 { debug_assert!(self.gecko.mClip.width == 1 << 30); // NS_MAXSIZE None } else { Some(Au(self.gecko.mClip.x + self.gecko.mClip.width).into()) }; Either::First(ClipRect { top: top, right: right, bottom: bottom, left: left, }) } } <% # This array is several filter function which has percentage or # number value for function of clone / set. # The setting / cloning process of other function(e.g. Blur / HueRotate) is # different from these function. So this array don't include such function. FILTER_FUNCTIONS = [ 'Brightness', 'Contrast', 'Grayscale', 'Invert', 'Opacity', 'Saturate', 'Sepia' ] %> pub fn set_filter(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator, { use values::generics::effects::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); } let v = v.into_iter(); unsafe { Gecko_ResetFilters(&mut self.gecko, v.len()); } debug_assert_eq!(v.len(), self.gecko.mFilters.len()); for (servo, gecko_filter) in v.zip(self.gecko.mFilters.iter_mut()) { match servo { % for func in FILTER_FUNCTIONS: ${func}(factor) => fill_filter(NS_STYLE_FILTER_${func.upper()}, CoordDataValue::Factor(factor.0), gecko_filter), % endfor Blur(length) => fill_filter(NS_STYLE_FILTER_BLUR, CoordDataValue::Coord(length.0.to_i32_au()), gecko_filter), HueRotate(angle) => fill_filter(NS_STYLE_FILTER_HUE_ROTATE, CoordDataValue::from(angle), 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 shadow_array: &mut *mut nsCSSShadowArray = union.mDropShadow.as_mut(); *shadow_array = Gecko_NewCSSShadowArray(1); &mut **shadow_array } } let gecko_shadow = init_shadow(gecko_filter); gecko_shadow.mArray[0].set_from_simple_shadow(shadow); }, Url(ref url) => { unsafe { bindings::Gecko_nsStyleFilter_SetURLValue(gecko_filter, url.for_ffi()); } }, } } } pub fn copy_filter_from(&mut self, other: &Self) { unsafe { Gecko_CopyFiltersFrom(&other.gecko as *const _ as *mut _, &mut self.gecko); } } pub fn reset_filter(&mut self, other: &Self) { self.copy_filter_from(other) } pub fn clone_filter(&self) -> longhands::filter::computed_value::T { use values::generics::effects::Filter; use values::specified::url::SpecifiedUrl; 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; use gecko_bindings::structs::NS_STYLE_FILTER_URL; let mut filters = Vec::new(); for filter in self.gecko.mFilters.iter(){ match filter.mType { % for func in FILTER_FUNCTIONS: NS_STYLE_FILTER_${func.upper()} => { filters.push(Filter::${func}( GeckoStyleCoordConvertible::from_gecko_style_coord( &filter.mFilterParameter).unwrap())); }, % endfor NS_STYLE_FILTER_BLUR => { filters.push(Filter::Blur(NonNegativeLength::from_gecko_style_coord( &filter.mFilterParameter).unwrap())); }, NS_STYLE_FILTER_HUE_ROTATE => { filters.push(Filter::HueRotate( GeckoStyleCoordConvertible::from_gecko_style_coord( &filter.mFilterParameter).unwrap())); }, NS_STYLE_FILTER_DROP_SHADOW => { filters.push(unsafe { Filter::DropShadow( (**filter.__bindgen_anon_1.mDropShadow.as_ref()).mArray[0].to_simple_shadow(), ) }); }, NS_STYLE_FILTER_URL => { filters.push(unsafe { Filter::Url( SpecifiedUrl::from_url_value_data(&(**filter.__bindgen_anon_1.mURL.as_ref())._base).unwrap() ) }); } _ => {}, } } longhands::filter::computed_value::T(filters) } <%self:impl_trait style_struct_name="InheritedBox" skip_longhands="image-orientation"> // FIXME: Gecko uses a tricky way to store computed value of image-orientation // within an u8. We could inline following glue codes by implementing all // those tricky parts for Servo as well. But, it's not done yet just for // convenience. pub fn set_image_orientation(&mut self, v: longhands::image_orientation::computed_value::T) { use properties::longhands::image_orientation::computed_value::T; match v { T::FromImage => { unsafe { bindings::Gecko_SetImageOrientationAsFromImage(&mut self.gecko); } }, T::AngleWithFlipped(ref orientation, flipped) => { unsafe { bindings::Gecko_SetImageOrientation(&mut self.gecko, *orientation as u8, flipped); } } } } pub fn copy_image_orientation_from(&mut self, other: &Self) { unsafe { bindings::Gecko_CopyImageOrientationFrom(&mut self.gecko, &other.gecko); } } pub fn reset_image_orientation(&mut self, other: &Self) { self.copy_image_orientation_from(other) } pub fn clone_image_orientation(&self) -> longhands::image_orientation::computed_value::T { use gecko_bindings::structs::{nsStyleImageOrientation_Bits, nsStyleImageOrientation_Angles}; use properties::longhands::image_orientation::computed_value::{Orientation, T}; let gecko_orientation = self.gecko.mImageOrientation.mOrientation; if gecko_orientation & nsStyleImageOrientation_Bits::FROM_IMAGE_MASK as u8 != 0 { T::FromImage } else { const ANGLE0: u8 = nsStyleImageOrientation_Angles::ANGLE_0 as u8; const ANGLE90: u8 = nsStyleImageOrientation_Angles::ANGLE_90 as u8; const ANGLE180: u8 = nsStyleImageOrientation_Angles::ANGLE_180 as u8; const ANGLE270: u8 = nsStyleImageOrientation_Angles::ANGLE_270 as u8; let flip = gecko_orientation & nsStyleImageOrientation_Bits::FLIP_MASK as u8 != 0; let orientation = match gecko_orientation & nsStyleImageOrientation_Bits::ORIENTATION_MASK as u8 { ANGLE0 => Orientation::Angle0, ANGLE90 => Orientation::Angle90, ANGLE180 => Orientation::Angle180, ANGLE270 => Orientation::Angle270, _ => unreachable!() }; T::AngleWithFlipped(orientation, flip) } } <%self:impl_trait style_struct_name="InheritedTable" skip_longhands="border-spacing"> pub fn set_border_spacing(&mut self, v: longhands::border_spacing::computed_value::T) { self.gecko.mBorderSpacingCol = v.horizontal().0; self.gecko.mBorderSpacingRow = v.vertical().0; } pub fn copy_border_spacing_from(&mut self, other: &Self) { self.gecko.mBorderSpacingCol = other.gecko.mBorderSpacingCol; self.gecko.mBorderSpacingRow = other.gecko.mBorderSpacingRow; } pub fn reset_border_spacing(&mut self, other: &Self) { self.copy_border_spacing_from(other) } pub fn clone_border_spacing(&self) -> longhands::border_spacing::computed_value::T { longhands::border_spacing::computed_value::T::new( Au(self.gecko.mBorderSpacingCol).into(), Au(self.gecko.mBorderSpacingRow).into() ) } <%self:impl_trait style_struct_name="InheritedText" skip_longhands="text-align text-emphasis-style text-shadow line-height letter-spacing word-spacing -webkit-text-stroke-width text-emphasis-position -moz-tab-size"> <% text_align_keyword = Keyword("text-align", "start end left right center justify -moz-center -moz-left -moz-right char", gecko_strip_moz_prefix=False) %> ${impl_keyword('text_align', 'mTextAlign', text_align_keyword)} pub fn set_text_shadow(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator { let v = v.into_iter(); self.gecko.mTextShadow.replace_with_new(v.len() as u32); for (servo, gecko_shadow) in v.zip(self.gecko.mTextShadow.iter_mut()) { gecko_shadow.set_from_simple_shadow(servo); } } pub fn copy_text_shadow_from(&mut self, other: &Self) { self.gecko.mTextShadow.copy_from(&other.gecko.mTextShadow); } pub fn reset_text_shadow(&mut self, other: &Self) { self.copy_text_shadow_from(other) } pub fn clone_text_shadow(&self) -> longhands::text_shadow::computed_value::T { let buf = self.gecko.mTextShadow.iter().map(|v| v.to_simple_shadow()).collect(); longhands::text_shadow::computed_value::T(buf) } pub fn set_line_height(&mut self, v: longhands::line_height::computed_value::T) { use values::generics::text::LineHeight; // FIXME: Align binary representations and ditch |match| for cast + static_asserts let en = match v { LineHeight::Normal => CoordDataValue::Normal, LineHeight::Length(val) => CoordDataValue::Coord(val.0.to_i32_au()), LineHeight::Number(val) => CoordDataValue::Factor(val.0), LineHeight::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 values::generics::text::LineHeight; return match self.gecko.mLineHeight.as_value() { CoordDataValue::Normal => LineHeight::Normal, CoordDataValue::Coord(coord) => LineHeight::Length(Au(coord).into()), CoordDataValue::Factor(n) => LineHeight::Number(n.into()), CoordDataValue::Enumerated(val) if val == structs::NS_STYLE_LINE_HEIGHT_BLOCK_HEIGHT => LineHeight::MozBlockHeight, _ => panic!("this should not happen"), } } <%call expr="impl_coord_copy('line_height', 'mLineHeight')"> pub fn set_letter_spacing(&mut self, v: longhands::letter_spacing::computed_value::T) { use values::generics::text::Spacing; match v { Spacing::Value(value) => self.gecko.mLetterSpacing.set(value), Spacing::Normal => self.gecko.mLetterSpacing.set_value(CoordDataValue::Normal) } } pub fn clone_letter_spacing(&self) -> longhands::letter_spacing::computed_value::T { use values::computed::Length; use values::generics::text::Spacing; debug_assert!( matches!(self.gecko.mLetterSpacing.as_value(), CoordDataValue::Normal | CoordDataValue::Coord(_)), "Unexpected computed value for letter-spacing"); Length::from_gecko_style_coord(&self.gecko.mLetterSpacing).map_or(Spacing::Normal, Spacing::Value) } <%call expr="impl_coord_copy('letter_spacing', 'mLetterSpacing')"> pub fn set_word_spacing(&mut self, v: longhands::word_spacing::computed_value::T) { use values::generics::text::Spacing; match v { Spacing::Value(lop) => self.gecko.mWordSpacing.set(lop), // https://drafts.csswg.org/css-text-3/#valdef-word-spacing-normal Spacing::Normal => self.gecko.mWordSpacing.set_value(CoordDataValue::Coord(0)), } } pub fn clone_word_spacing(&self) -> longhands::word_spacing::computed_value::T { use values::computed::LengthOrPercentage; use values::generics::text::Spacing; debug_assert!( matches!(self.gecko.mWordSpacing.as_value(), CoordDataValue::Normal | CoordDataValue::Coord(_) | CoordDataValue::Percent(_) | CoordDataValue::Calc(_)), "Unexpected computed value for word-spacing"); LengthOrPercentage::from_gecko_style_coord(&self.gecko.mWordSpacing).map_or(Spacing::Normal, Spacing::Value) } <%call expr="impl_coord_copy('word_spacing', 'mWordSpacing')"> fn clear_text_emphasis_style_if_string(&mut self) { if self.gecko.mTextEmphasisStyle == structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING as u8 { self.gecko.mTextEmphasisStyleString.truncate(); self.gecko.mTextEmphasisStyle = structs::NS_STYLE_TEXT_EMPHASIS_STYLE_NONE as u8; } } ${impl_simple_type_with_conversion("text_emphasis_position")} pub fn set_text_emphasis_style(&mut self, v: longhands::text_emphasis_style::computed_value::T) { use properties::longhands::text_emphasis_style::computed_value::T; use properties::longhands::text_emphasis_style::ShapeKeyword; self.clear_text_emphasis_style_if_string(); let (te, s) = match v { T::None => (structs::NS_STYLE_TEXT_EMPHASIS_STYLE_NONE, ""), T::Keyword(ref keyword) => { let fill = if keyword.fill { structs::NS_STYLE_TEXT_EMPHASIS_STYLE_FILLED } else { structs::NS_STYLE_TEXT_EMPHASIS_STYLE_OPEN }; let shape = match keyword.shape { ShapeKeyword::Dot => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_DOT, ShapeKeyword::Circle => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_CIRCLE, ShapeKeyword::DoubleCircle => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_DOUBLE_CIRCLE, ShapeKeyword::Triangle => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_TRIANGLE, ShapeKeyword::Sesame => structs::NS_STYLE_TEXT_EMPHASIS_STYLE_SESAME, }; (shape | fill, keyword.shape.char(keyword.fill)) }, T::String(ref s) => { (structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING, &**s) }, }; self.gecko.mTextEmphasisStyleString.assign_utf8(s); self.gecko.mTextEmphasisStyle = te as u8; } pub fn copy_text_emphasis_style_from(&mut self, other: &Self) { self.clear_text_emphasis_style_if_string(); if other.gecko.mTextEmphasisStyle == structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING as u8 { self.gecko.mTextEmphasisStyleString .assign(&*other.gecko.mTextEmphasisStyleString) } self.gecko.mTextEmphasisStyle = other.gecko.mTextEmphasisStyle; } pub fn reset_text_emphasis_style(&mut self, other: &Self) { self.copy_text_emphasis_style_from(other) } pub fn clone_text_emphasis_style(&self) -> longhands::text_emphasis_style::computed_value::T { use properties::longhands::text_emphasis_style::computed_value::{T, KeywordValue}; use properties::longhands::text_emphasis_style::ShapeKeyword; if self.gecko.mTextEmphasisStyle == structs::NS_STYLE_TEXT_EMPHASIS_STYLE_NONE as u8 { return T::None; } else if self.gecko.mTextEmphasisStyle == structs::NS_STYLE_TEXT_EMPHASIS_STYLE_STRING as u8 { return T::String(self.gecko.mTextEmphasisStyleString.to_string()); } let fill = self.gecko.mTextEmphasisStyle & structs::NS_STYLE_TEXT_EMPHASIS_STYLE_OPEN as u8 == 0; let shape = match self.gecko.mTextEmphasisStyle as u32 & !structs::NS_STYLE_TEXT_EMPHASIS_STYLE_OPEN { structs::NS_STYLE_TEXT_EMPHASIS_STYLE_DOT => ShapeKeyword::Dot, structs::NS_STYLE_TEXT_EMPHASIS_STYLE_CIRCLE => ShapeKeyword::Circle, structs::NS_STYLE_TEXT_EMPHASIS_STYLE_DOUBLE_CIRCLE => ShapeKeyword::DoubleCircle, structs::NS_STYLE_TEXT_EMPHASIS_STYLE_TRIANGLE => ShapeKeyword::Triangle, structs::NS_STYLE_TEXT_EMPHASIS_STYLE_SESAME => ShapeKeyword::Sesame, x => panic!("Unexpected value in style struct for text-emphasis-style property: {:?}", x) }; T::Keyword(KeywordValue { fill: fill, shape: shape }) } ${impl_non_negative_length('_webkit_text_stroke_width', 'mWebkitTextStrokeWidth')} #[allow(non_snake_case)] pub fn set__moz_tab_size(&mut self, v: longhands::_moz_tab_size::computed_value::T) { use values::Either; match v { Either::Second(non_negative_number) => { self.gecko.mTabSize.set_value(CoordDataValue::Factor(non_negative_number.0)); } Either::First(non_negative_length) => { self.gecko.mTabSize.set(non_negative_length); } } } #[allow(non_snake_case)] pub fn clone__moz_tab_size(&self) -> longhands::_moz_tab_size::computed_value::T { use values::Either; match self.gecko.mTabSize.as_value() { CoordDataValue::Coord(coord) => Either::First(Au(coord).into()), CoordDataValue::Factor(number) => Either::Second(From::from(number)), _ => unreachable!(), } } <%call expr="impl_coord_copy('_moz_tab_size', 'mTabSize')"> <%self:impl_trait style_struct_name="Text" skip_longhands="text-decoration-line text-overflow initial-letter"> ${impl_simple_type_with_conversion("text_decoration_line")} fn clear_overflow_sides_if_string(&mut self) { use gecko_bindings::structs::nsStyleTextOverflowSide; fn clear_if_string(side: &mut nsStyleTextOverflowSide) { if side.mType == structs::NS_STYLE_TEXT_OVERFLOW_STRING as u8 { side.mString.truncate(); 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 values::specified::text::TextOverflowSide; fn set(side: &mut nsStyleTextOverflowSide, value: &TextOverflowSide) { let ty = match *value { TextOverflowSide::Clip => structs::NS_STYLE_TEXT_OVERFLOW_CLIP, TextOverflowSide::Ellipsis => structs::NS_STYLE_TEXT_OVERFLOW_ELLIPSIS, TextOverflowSide::String(ref s) => { side.mString.assign_utf8(s); structs::NS_STYLE_TEXT_OVERFLOW_STRING } }; side.mType = ty as u8; } self.clear_overflow_sides_if_string(); self.gecko.mTextOverflow.mLogicalDirections = v.sides_are_logical; set(&mut self.gecko.mTextOverflow.mLeft, &v.first); set(&mut self.gecko.mTextOverflow.mRight, &v.second); } pub fn copy_text_overflow_from(&mut self, other: &Self) { use gecko_bindings::structs::nsStyleTextOverflowSide; fn set(side: &mut nsStyleTextOverflowSide, other: &nsStyleTextOverflowSide) { if other.mType == structs::NS_STYLE_TEXT_OVERFLOW_STRING as u8 { side.mString.assign(&*other.mString) } side.mType = other.mType } self.clear_overflow_sides_if_string(); set(&mut self.gecko.mTextOverflow.mLeft, &other.gecko.mTextOverflow.mLeft); set(&mut self.gecko.mTextOverflow.mRight, &other.gecko.mTextOverflow.mRight); self.gecko.mTextOverflow.mLogicalDirections = other.gecko.mTextOverflow.mLogicalDirections; } pub fn reset_text_overflow(&mut self, other: &Self) { self.copy_text_overflow_from(other) } pub fn clone_text_overflow(&self) -> longhands::text_overflow::computed_value::T { use gecko_bindings::structs::nsStyleTextOverflowSide; use values::specified::text::TextOverflowSide; fn to_servo(side: &nsStyleTextOverflowSide) -> TextOverflowSide { match side.mType as u32 { structs::NS_STYLE_TEXT_OVERFLOW_CLIP => TextOverflowSide::Clip, structs::NS_STYLE_TEXT_OVERFLOW_ELLIPSIS => TextOverflowSide::Ellipsis, structs::NS_STYLE_TEXT_OVERFLOW_STRING => TextOverflowSide::String(side.mString.to_string().into_boxed_str()), x => panic!("Found unexpected value in style struct for text_overflow property: {:?}", x), } } longhands::text_overflow::computed_value::T { first: to_servo(&self.gecko.mTextOverflow.mLeft), second: to_servo(&self.gecko.mTextOverflow.mRight), sides_are_logical: self.gecko.mTextOverflow.mLogicalDirections } } pub fn set_initial_letter(&mut self, v: longhands::initial_letter::computed_value::T) { use values::generics::text::InitialLetter; match v { InitialLetter::Normal => { self.gecko.mInitialLetterSize = 0.; self.gecko.mInitialLetterSink = 0; }, InitialLetter::Specified(size, sink) => { self.gecko.mInitialLetterSize = size; if let Some(sink) = sink { self.gecko.mInitialLetterSink = sink; } else { self.gecko.mInitialLetterSink = size.floor() as i32; } } } } pub fn copy_initial_letter_from(&mut self, other: &Self) { self.gecko.mInitialLetterSize = other.gecko.mInitialLetterSize; self.gecko.mInitialLetterSink = other.gecko.mInitialLetterSink; } pub fn reset_initial_letter(&mut self, other: &Self) { self.copy_initial_letter_from(other) } pub fn clone_initial_letter(&self) -> longhands::initial_letter::computed_value::T { use values::generics::text::InitialLetter; if self.gecko.mInitialLetterSize == 0. && self.gecko.mInitialLetterSink == 0 { InitialLetter::Normal } else if self.gecko.mInitialLetterSize.floor() as i32 == self.gecko.mInitialLetterSink { InitialLetter::Specified(self.gecko.mInitialLetterSize, None) } else { InitialLetter::Specified(self.gecko.mInitialLetterSize, Some(self.gecko.mInitialLetterSink)) } } #[inline] pub fn has_underline(&self) -> bool { (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE as u8)) != 0 } #[inline] pub fn has_overline(&self) -> bool { (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_OVERLINE as u8)) != 0 } #[inline] pub fn has_line_through(&self) -> bool { (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH as u8)) != 0 } <%def name="impl_shape_source(ident, gecko_ffi_name)"> pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { use gecko_bindings::bindings::{Gecko_NewBasicShape, Gecko_DestroyShapeSource}; use gecko_bindings::structs::{StyleBasicShape, StyleBasicShapeType, StyleShapeSourceType}; use gecko_bindings::structs::{StyleFillRule, StyleGeometryBox, StyleShapeSource}; use gecko::conversions::basic_shape::set_corners_from_radius; use gecko::values::GeckoStyleCoordConvertible; use values::generics::basic_shape::{BasicShape, FillRule, ShapeSource}; let ref mut ${ident} = self.gecko.${gecko_ffi_name}; // clean up existing struct unsafe { Gecko_DestroyShapeSource(${ident}) }; ${ident}.mType = StyleShapeSourceType::None; match v { ShapeSource::Url(ref url) => { unsafe { bindings::Gecko_StyleShapeSource_SetURLValue(${ident}, url.for_ffi()) } } ShapeSource::None => {} // don't change the type ShapeSource::Box(reference) => { ${ident}.mReferenceBox = reference.into(); ${ident}.mType = StyleShapeSourceType::Box; } ShapeSource::Shape(servo_shape, maybe_box) => { fn init_shape(${ident}: &mut StyleShapeSource, basic_shape_type: StyleBasicShapeType) -> &mut StyleBasicShape { unsafe { // Create StyleBasicShape in StyleShapeSource. mReferenceBox and mType // will be set manually later. Gecko_NewBasicShape(${ident}, basic_shape_type); &mut *${ident}.mBasicShape.mPtr } } match servo_shape { BasicShape::Inset(inset) => { let shape = init_shape(${ident}, StyleBasicShapeType::Inset); unsafe { shape.mCoordinates.set_len(4) }; // set_len() can't call constructors, so the coordinates // can contain any value. set_value() attempts to free // allocated coordinates, so we don't want to feed it // garbage values which it may misinterpret. // Instead, we use leaky_set_value to blindly overwrite // the garbage data without // attempting to clean up. shape.mCoordinates[0].leaky_set_null(); inset.rect.0.to_gecko_style_coord(&mut shape.mCoordinates[0]); shape.mCoordinates[1].leaky_set_null(); inset.rect.1.to_gecko_style_coord(&mut shape.mCoordinates[1]); shape.mCoordinates[2].leaky_set_null(); inset.rect.2.to_gecko_style_coord(&mut shape.mCoordinates[2]); shape.mCoordinates[3].leaky_set_null(); inset.rect.3.to_gecko_style_coord(&mut shape.mCoordinates[3]); set_corners_from_radius(inset.round, &mut shape.mRadius); } BasicShape::Circle(circ) => { let shape = init_shape(${ident}, StyleBasicShapeType::Circle); unsafe { shape.mCoordinates.set_len(1) }; shape.mCoordinates[0].leaky_set_null(); circ.radius.to_gecko_style_coord(&mut shape.mCoordinates[0]); shape.mPosition = circ.position.into(); } BasicShape::Ellipse(el) => { let shape = init_shape(${ident}, StyleBasicShapeType::Ellipse); unsafe { shape.mCoordinates.set_len(2) }; shape.mCoordinates[0].leaky_set_null(); el.semiaxis_x.to_gecko_style_coord(&mut shape.mCoordinates[0]); shape.mCoordinates[1].leaky_set_null(); el.semiaxis_y.to_gecko_style_coord(&mut shape.mCoordinates[1]); shape.mPosition = el.position.into(); } BasicShape::Polygon(poly) => { let shape = init_shape(${ident}, StyleBasicShapeType::Polygon); unsafe { shape.mCoordinates.set_len(poly.coordinates.len() as u32 * 2); } for (i, coord) in poly.coordinates.iter().enumerate() { shape.mCoordinates[2 * i].leaky_set_null(); shape.mCoordinates[2 * i + 1].leaky_set_null(); coord.0.to_gecko_style_coord(&mut shape.mCoordinates[2 * i]); coord.1.to_gecko_style_coord(&mut shape.mCoordinates[2 * i + 1]); } shape.mFillRule = if poly.fill == FillRule::EvenOdd { StyleFillRule::Evenodd } else { StyleFillRule::Nonzero }; } } ${ident}.mReferenceBox = maybe_box.map(Into::into) .unwrap_or(StyleGeometryBox::NoBox); ${ident}.mType = StyleShapeSourceType::Shape; } } } pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { (&self.gecko.${gecko_ffi_name}).into() } pub fn copy_${ident}_from(&mut self, other: &Self) { use gecko_bindings::bindings::Gecko_CopyShapeSourceFrom; unsafe { Gecko_CopyShapeSourceFrom(&mut self.gecko.${gecko_ffi_name}, &other.gecko.${gecko_ffi_name}); } } pub fn reset_${ident}(&mut self, other: &Self) { self.copy_${ident}_from(other) } <% 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}"> <% impl_common_image_layer_properties("mask") %> <% impl_simple_image_array_property("mode", "mask", "mMask", "mMaskMode", "SVG") %> <% impl_simple_image_array_property("composite", "mask", "mMask", "mComposite", "SVG") %> <% impl_shape_source("clip_path", "mClipPath") %> <%self:impl_trait style_struct_name="InheritedSVG" skip_longhands="paint-order stroke-dasharray -moz-context-properties"> pub fn set_paint_order(&mut self, v: longhands::paint_order::computed_value::T) { self.gecko.mPaintOrder = v.0; } ${impl_simple_copy('paint_order', 'mPaintOrder')} pub fn clone_paint_order(&self) -> longhands::paint_order::computed_value::T { use properties::longhands::paint_order::computed_value::T; T(self.gecko.mPaintOrder) } pub fn set_stroke_dasharray(&mut self, v: longhands::stroke_dasharray::computed_value::T) { use gecko_bindings::structs::nsStyleSVG_STROKE_DASHARRAY_CONTEXT as CONTEXT_VALUE; use values::generics::svg::{SVGStrokeDashArray, SvgLengthOrPercentageOrNumber}; match v { SVGStrokeDashArray::Values(v) => { let v = v.into_iter(); self.gecko.mContextFlags &= !CONTEXT_VALUE; unsafe { bindings::Gecko_nsStyleSVG_SetDashArrayLength(&mut self.gecko, v.len() as u32); } for (gecko, servo) in self.gecko.mStrokeDasharray.iter_mut().zip(v) { match servo { SvgLengthOrPercentageOrNumber::LengthOrPercentage(lop) => gecko.set(lop), SvgLengthOrPercentageOrNumber::Number(num) => gecko.set_value(CoordDataValue::Factor(num.into())), } } } SVGStrokeDashArray::ContextValue => { self.gecko.mContextFlags |= CONTEXT_VALUE; unsafe { bindings::Gecko_nsStyleSVG_SetDashArrayLength(&mut self.gecko, 0); } } } } pub fn copy_stroke_dasharray_from(&mut self, other: &Self) { use gecko_bindings::structs::nsStyleSVG_STROKE_DASHARRAY_CONTEXT as CONTEXT_VALUE; unsafe { bindings::Gecko_nsStyleSVG_CopyDashArray(&mut self.gecko, &other.gecko); } self.gecko.mContextFlags = (self.gecko.mContextFlags & !CONTEXT_VALUE) | (other.gecko.mContextFlags & CONTEXT_VALUE); } pub fn reset_stroke_dasharray(&mut self, other: &Self) { self.copy_stroke_dasharray_from(other) } pub fn clone_stroke_dasharray(&self) -> longhands::stroke_dasharray::computed_value::T { use gecko_bindings::structs::nsStyleSVG_STROKE_DASHARRAY_CONTEXT as CONTEXT_VALUE; use values::computed::LengthOrPercentage; use values::generics::svg::{SVGStrokeDashArray, SvgLengthOrPercentageOrNumber}; if self.gecko.mContextFlags & CONTEXT_VALUE != 0 { debug_assert_eq!(self.gecko.mStrokeDasharray.len(), 0); return SVGStrokeDashArray::ContextValue; } let mut vec = vec![]; for gecko in self.gecko.mStrokeDasharray.iter() { match gecko.as_value() { CoordDataValue::Factor(number) => vec.push(SvgLengthOrPercentageOrNumber::Number(number.into())), CoordDataValue::Coord(coord) => vec.push(SvgLengthOrPercentageOrNumber::LengthOrPercentage( LengthOrPercentage::Length(Au(coord).into()).into())), CoordDataValue::Percent(p) => vec.push(SvgLengthOrPercentageOrNumber::LengthOrPercentage( LengthOrPercentage::Percentage(Percentage(p)).into())), CoordDataValue::Calc(calc) => vec.push(SvgLengthOrPercentageOrNumber::LengthOrPercentage( LengthOrPercentage::Calc(calc.into()).into())), _ => unreachable!(), } } SVGStrokeDashArray::Values(vec) } #[allow(non_snake_case)] pub fn set__moz_context_properties(&mut self, v: I) where I: IntoIterator, I::IntoIter: ExactSizeIterator { let v = v.into_iter(); unsafe { bindings::Gecko_nsStyleSVG_SetContextPropertiesLength(&mut self.gecko, v.len() as u32); } self.gecko.mContextPropsBits = 0; for (gecko, servo) in self.gecko.mContextProps.iter_mut().zip(v) { if servo.0 == atom!("fill") { self.gecko.mContextPropsBits |= structs::NS_STYLE_CONTEXT_PROPERTY_FILL as u8; } else if servo.0 == atom!("stroke") { self.gecko.mContextPropsBits |= structs::NS_STYLE_CONTEXT_PROPERTY_STROKE as u8; } else if servo.0 == atom!("fill-opacity") { self.gecko.mContextPropsBits |= structs::NS_STYLE_CONTEXT_PROPERTY_FILL_OPACITY as u8; } else if servo.0 == atom!("stroke-opacity") { self.gecko.mContextPropsBits |= structs::NS_STYLE_CONTEXT_PROPERTY_STROKE_OPACITY as u8; } unsafe { gecko.mRawPtr = servo.0.into_addrefed() } } } #[allow(non_snake_case)] pub fn copy__moz_context_properties_from(&mut self, other: &Self) { unsafe { bindings::Gecko_nsStyleSVG_CopyContextProperties(&mut self.gecko, &other.gecko); } } #[allow(non_snake_case)] pub fn reset__moz_context_properties(&mut self, other: &Self) { self.copy__moz_context_properties_from(other) } <%self:impl_trait style_struct_name="Color" skip_longhands="*"> pub fn set_color(&mut self, v: longhands::color::computed_value::T) { let result = convert_rgba_to_nscolor(&v); ${set_gecko_property("mColor", "result")} } <%call expr="impl_simple_copy('color', 'mColor')"> pub fn clone_color(&self) -> longhands::color::computed_value::T { let color = ${get_gecko_property("mColor")} as u32; convert_nscolor_to_rgba(color) } <%self:impl_trait style_struct_name="Pointing" skip_longhands="cursor caret-color"> pub fn set_cursor(&mut self, v: longhands::cursor::computed_value::T) { use properties::longhands::cursor::computed_value::Keyword; use style_traits::cursor::Cursor; self.gecko.mCursor = match v.keyword { Keyword::Auto => structs::NS_STYLE_CURSOR_AUTO, Keyword::Cursor(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_SPINNING, 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, // note: the following properties are gecko-only. Cursor::MozGrab => structs::NS_STYLE_CURSOR_GRAB, Cursor::MozGrabbing => structs::NS_STYLE_CURSOR_GRABBING, Cursor::MozZoomIn => structs::NS_STYLE_CURSOR_ZOOM_IN, Cursor::MozZoomOut => structs::NS_STYLE_CURSOR_ZOOM_OUT, } } as u8; unsafe { Gecko_SetCursorArrayLength(&mut self.gecko, v.images.len()); } for i in 0..v.images.len() { unsafe { Gecko_SetCursorImageValue(&mut self.gecko.mCursorImages[i], v.images[i].url.clone().image_value.unwrap().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. match v.images[i].hotspot { Some((x, y)) => { self.gecko.mCursorImages[i].mHaveHotspot = true; self.gecko.mCursorImages[i].mHotspotX = x; self.gecko.mCursorImages[i].mHotspotY = y; }, _ => { self.gecko.mCursorImages[i].mHaveHotspot = false; } } } } pub fn copy_cursor_from(&mut self, other: &Self) { self.gecko.mCursor = other.gecko.mCursor; unsafe { Gecko_CopyCursorArrayFrom(&mut self.gecko, &other.gecko); } } pub fn reset_cursor(&mut self, other: &Self) { self.copy_cursor_from(other) } pub fn clone_cursor(&self) -> longhands::cursor::computed_value::T { use properties::longhands::cursor::computed_value::{Keyword, Image}; use style_traits::cursor::Cursor; use values::specified::url::SpecifiedUrl; let keyword = match self.gecko.mCursor as u32 { structs::NS_STYLE_CURSOR_AUTO => Keyword::Auto, structs::NS_STYLE_CURSOR_NONE => Keyword::Cursor(Cursor::None), structs::NS_STYLE_CURSOR_DEFAULT => Keyword::Cursor(Cursor::Default), structs::NS_STYLE_CURSOR_POINTER => Keyword::Cursor(Cursor::Pointer), structs::NS_STYLE_CURSOR_CONTEXT_MENU => Keyword::Cursor(Cursor::ContextMenu), structs::NS_STYLE_CURSOR_HELP => Keyword::Cursor(Cursor::Help), structs::NS_STYLE_CURSOR_SPINNING => Keyword::Cursor(Cursor::Progress), structs::NS_STYLE_CURSOR_WAIT => Keyword::Cursor(Cursor::Wait), structs::NS_STYLE_CURSOR_CELL => Keyword::Cursor(Cursor::Cell), structs::NS_STYLE_CURSOR_CROSSHAIR => Keyword::Cursor(Cursor::Crosshair), structs::NS_STYLE_CURSOR_TEXT => Keyword::Cursor(Cursor::Text), structs::NS_STYLE_CURSOR_VERTICAL_TEXT => Keyword::Cursor(Cursor::VerticalText), structs::NS_STYLE_CURSOR_ALIAS => Keyword::Cursor(Cursor::Alias), structs::NS_STYLE_CURSOR_COPY => Keyword::Cursor(Cursor::Copy), structs::NS_STYLE_CURSOR_MOVE => Keyword::Cursor(Cursor::Move), structs::NS_STYLE_CURSOR_NO_DROP => Keyword::Cursor(Cursor::NoDrop), structs::NS_STYLE_CURSOR_NOT_ALLOWED => Keyword::Cursor(Cursor::NotAllowed), structs::NS_STYLE_CURSOR_GRAB => Keyword::Cursor(Cursor::Grab), structs::NS_STYLE_CURSOR_GRABBING => Keyword::Cursor(Cursor::Grabbing), structs::NS_STYLE_CURSOR_E_RESIZE => Keyword::Cursor(Cursor::EResize), structs::NS_STYLE_CURSOR_N_RESIZE => Keyword::Cursor(Cursor::NResize), structs::NS_STYLE_CURSOR_NE_RESIZE => Keyword::Cursor(Cursor::NeResize), structs::NS_STYLE_CURSOR_NW_RESIZE => Keyword::Cursor(Cursor::NwResize), structs::NS_STYLE_CURSOR_S_RESIZE => Keyword::Cursor(Cursor::SResize), structs::NS_STYLE_CURSOR_SE_RESIZE => Keyword::Cursor(Cursor::SeResize), structs::NS_STYLE_CURSOR_SW_RESIZE => Keyword::Cursor(Cursor::SwResize), structs::NS_STYLE_CURSOR_W_RESIZE => Keyword::Cursor(Cursor::WResize), structs::NS_STYLE_CURSOR_EW_RESIZE => Keyword::Cursor(Cursor::EwResize), structs::NS_STYLE_CURSOR_NS_RESIZE => Keyword::Cursor(Cursor::NsResize), structs::NS_STYLE_CURSOR_NESW_RESIZE => Keyword::Cursor(Cursor::NeswResize), structs::NS_STYLE_CURSOR_NWSE_RESIZE => Keyword::Cursor(Cursor::NwseResize), structs::NS_STYLE_CURSOR_COL_RESIZE => Keyword::Cursor(Cursor::ColResize), structs::NS_STYLE_CURSOR_ROW_RESIZE => Keyword::Cursor(Cursor::RowResize), structs::NS_STYLE_CURSOR_ALL_SCROLL => Keyword::Cursor(Cursor::AllScroll), structs::NS_STYLE_CURSOR_ZOOM_IN => Keyword::Cursor(Cursor::ZoomIn), structs::NS_STYLE_CURSOR_ZOOM_OUT => Keyword::Cursor(Cursor::ZoomOut), x => panic!("Found unexpected value in style struct for cursor property: {:?}", x), }; let images = self.gecko.mCursorImages.iter().map(|gecko_cursor_image| { let url = unsafe { let gecko_image_request = gecko_cursor_image.mImage.mRawPtr.as_ref().unwrap(); SpecifiedUrl::from_image_request(&gecko_image_request) .expect("mCursorImages.mImage could not convert to SpecifiedUrl") }; let hotspot = if gecko_cursor_image.mHaveHotspot { Some((gecko_cursor_image.mHotspotX, gecko_cursor_image.mHotspotY)) } else { None }; Image { url, hotspot } }).collect(); longhands::cursor::computed_value::T { images, keyword } } <%call expr="impl_color('caret_color', 'mCaretColor')"> <%self:impl_trait style_struct_name="Column" skip_longhands="column-count column-rule-width"> #[allow(unused_unsafe)] pub fn set_column_count(&mut self, v: longhands::column_count::computed_value::T) { use gecko_bindings::structs::{NS_STYLE_COLUMN_COUNT_AUTO, nsStyleColumn_kMaxColumnCount}; self.gecko.mColumnCount = match v { Either::First(integer) => unsafe { cmp::min(integer.0 as u32, nsStyleColumn_kMaxColumnCount) }, Either::Second(Auto) => NS_STYLE_COLUMN_COUNT_AUTO }; } ${impl_simple_copy('column_count', 'mColumnCount')} pub fn clone_column_count(&self) -> longhands::column_count::computed_value::T { use gecko_bindings::structs::{NS_STYLE_COLUMN_COUNT_AUTO, nsStyleColumn_kMaxColumnCount}; if self.gecko.mColumnCount != NS_STYLE_COLUMN_COUNT_AUTO { debug_assert!(self.gecko.mColumnCount >= 1 && self.gecko.mColumnCount <= nsStyleColumn_kMaxColumnCount); Either::First((self.gecko.mColumnCount as i32).into()) } else { Either::Second(Auto) } } <% impl_non_negative_length("column_rule_width", "mColumnRuleWidth", round_to_pixels=True) %> <%self:impl_trait style_struct_name="Counters" skip_longhands="content counter-increment counter-reset"> pub fn ineffective_content_property(&self) -> bool { self.gecko.mContents.is_empty() } pub fn set_content(&mut self, v: longhands::content::computed_value::T, device: &Device) { use properties::longhands::content::computed_value::T; use properties::longhands::content::computed_value::ContentItem; use values::generics::CounterStyleOrNone; use gecko_bindings::structs::nsStyleContentData; use gecko_bindings::structs::nsStyleContentType; 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 } fn set_counter_function(data: &mut nsStyleContentData, content_type: nsStyleContentType, name: &str, sep: &str, style: CounterStyleOrNone, device: &Device) { debug_assert!(content_type == eStyleContentType_Counter || content_type == eStyleContentType_Counters); let counter_func = unsafe { bindings::Gecko_SetCounterFunction(data, content_type).as_mut().unwrap() }; counter_func.mIdent.assign_utf8(name); if content_type == eStyleContentType_Counters { counter_func.mSeparator.assign_utf8(sep); } style.to_gecko_value(&mut counter_func.mCounterStyle, device); } 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::MozAltContent => { unsafe { Gecko_ClearAndResizeStyleContents(&mut self.gecko, 1); *self.gecko.mContents[0].mContent.mString.as_mut() = ptr::null_mut(); } self.gecko.mContents[0].mType = eStyleContentType_AltContent; }, T::Items(items) => { unsafe { Gecko_ClearAndResizeStyleContents(&mut self.gecko, items.len() as u32); } for (i, item) in items.into_iter().enumerate() { // NB: Gecko compares the mString value if type is not image // or URI independently of whatever gets there. In the quote // cases, they set it to null, so do the same here. unsafe { *self.gecko.mContents[i].mContent.mString.as_mut() = ptr::null_mut(); } match item { ContentItem::String(value) => { self.gecko.mContents[i].mType = eStyleContentType_String; unsafe { // NB: we share allocators, so doing this is fine. *self.gecko.mContents[i].mContent.mString.as_mut() = as_utf16_and_forget(&value); } } ContentItem::Attr(attr) => { self.gecko.mContents[i].mType = eStyleContentType_Attr; let s = if let Some((_, ns)) = attr.namespace { format!("{}|{}", ns, attr.attribute) } else { attr.attribute.into() }; unsafe { // NB: we share allocators, so doing this is fine. *self.gecko.mContents[i].mContent.mString.as_mut() = as_utf16_and_forget(&s); } } ContentItem::OpenQuote => self.gecko.mContents[i].mType = eStyleContentType_OpenQuote, ContentItem::CloseQuote => self.gecko.mContents[i].mType = eStyleContentType_CloseQuote, ContentItem::NoOpenQuote => self.gecko.mContents[i].mType = eStyleContentType_NoOpenQuote, ContentItem::NoCloseQuote => self.gecko.mContents[i].mType = eStyleContentType_NoCloseQuote, ContentItem::Counter(name, style) => { set_counter_function(&mut self.gecko.mContents[i], eStyleContentType_Counter, &name, "", style, device); } ContentItem::Counters(name, sep, style) => { set_counter_function(&mut self.gecko.mContents[i], eStyleContentType_Counters, &name, &sep, style, device); } ContentItem::Url(ref url) => { unsafe { bindings::Gecko_SetContentDataImageValue(&mut self.gecko.mContents[i], url.image_value.clone().unwrap().get()) } } } } } } } pub fn copy_content_from(&mut self, other: &Self) { use gecko_bindings::bindings::Gecko_CopyStyleContentsFrom; unsafe { Gecko_CopyStyleContentsFrom(&mut self.gecko, &other.gecko) } } pub fn reset_content(&mut self, other: &Self) { self.copy_content_from(other) } pub fn clone_content(&self) -> longhands::content::computed_value::T { use gecko::conversions::string_from_chars_pointer; use gecko_bindings::structs::nsStyleContentType::*; use properties::longhands::content::computed_value::{T, ContentItem}; use values::Either; use values::generics::CounterStyleOrNone; use values::specified::url::SpecifiedUrl; use values::specified::Attr; if self.gecko.mContents.is_empty() { return T::Normal; } if self.gecko.mContents.len() == 1 && self.gecko.mContents[0].mType == eStyleContentType_AltContent { return T::MozAltContent; } T::Items( self.gecko.mContents.iter().map(|gecko_content| { match gecko_content.mType { eStyleContentType_OpenQuote => ContentItem::OpenQuote, eStyleContentType_CloseQuote => ContentItem::CloseQuote, eStyleContentType_NoOpenQuote => ContentItem::NoOpenQuote, eStyleContentType_NoCloseQuote => ContentItem::NoCloseQuote, eStyleContentType_String => { let gecko_chars = unsafe { gecko_content.mContent.mString.as_ref() }; let string = unsafe { string_from_chars_pointer(*gecko_chars) }; ContentItem::String(string) }, eStyleContentType_Attr => { let gecko_chars = unsafe { gecko_content.mContent.mString.as_ref() }; let string = unsafe { string_from_chars_pointer(*gecko_chars) }; let (namespace, attribute) = match string.find('|') { None => (None, string), Some(index) => { let (_, val) = string.split_at(index); // FIXME: We should give NamespaceId as well to make Attr // struct. However, there is no field for it in Gecko. debug_assert!(false, "Attr with namespace does not support yet"); (None, val.to_string()) } }; ContentItem::Attr(Attr { namespace, attribute }) }, eStyleContentType_Counter | eStyleContentType_Counters => { let gecko_function = unsafe { &**gecko_content.mContent.mCounters.as_ref() }; let ident = gecko_function.mIdent.to_string(); let style = CounterStyleOrNone::from_gecko_value(&gecko_function.mCounterStyle); let style = match style { Either::First(counter_style) => counter_style, Either::Second(_) => unreachable!("counter function shouldn't have single string type"), }; if gecko_content.mType == eStyleContentType_Counter { ContentItem::Counter(ident, style) } else { let separator = gecko_function.mSeparator.to_string(); ContentItem::Counters(ident, separator, style) } }, eStyleContentType_Image => { unsafe { let gecko_image_request = unsafe { &**gecko_content.mContent.mImage.as_ref() }; ContentItem::Url( SpecifiedUrl::from_image_request(gecko_image_request) .expect("mContent could not convert to SpecifiedUrl") ) } }, x => panic!("Found unexpected value in style struct for content property: {:?}", x), } }).collect() ) } % for counter_property in ["Increment", "Reset"]: pub fn set_counter_${counter_property.lower()}(&mut self, v: longhands::counter_increment::computed_value::T) { unsafe { bindings::Gecko_ClearAndResizeCounter${counter_property}s(&mut self.gecko, v.0.len() as u32); for (i, (name, value)) in v.0.into_iter().enumerate() { self.gecko.m${counter_property}s[i].mCounter.assign(name.0.as_slice()); self.gecko.m${counter_property}s[i].mValue = value; } } } pub fn copy_counter_${counter_property.lower()}_from(&mut self, other: &Self) { unsafe { bindings::Gecko_CopyCounter${counter_property}sFrom(&mut self.gecko, &other.gecko) } } pub fn reset_counter_${counter_property.lower()}(&mut self, other: &Self) { self.copy_counter_${counter_property.lower()}_from(other) } pub fn clone_counter_${counter_property.lower()}(&self) -> longhands::counter_increment::computed_value::T { use values::CustomIdent; use gecko_string_cache::Atom; longhands::counter_increment::computed_value::T( self.gecko.m${counter_property}s.iter().map(|ref gecko_counter| { (CustomIdent(Atom::from(gecko_counter.mCounter.to_string())), gecko_counter.mValue) }).collect() ) } % endfor <%self:impl_trait style_struct_name="UI" skip_longhands="-moz-force-broken-image-icon"> ${impl_simple_type_with_conversion("_moz_force_broken_image_icon", "mForceBrokenImageIcon")} <%self:impl_trait style_struct_name="XUL" skip_longhands="-moz-box-ordinal-group"> #[allow(non_snake_case)] pub fn set__moz_box_ordinal_group(&mut self, v: i32) { self.gecko.mBoxOrdinal = v as u32; } ${impl_simple_copy("_moz_box_ordinal_group", "mBoxOrdinal")} #[allow(non_snake_case)] pub fn clone__moz_box_ordinal_group(&self) -> i32 { self.gecko.mBoxOrdinal as i32 } % for style_struct in data.style_structs: ${declare_style_struct(style_struct)} ${impl_style_struct(style_struct)} % endfor