mirror of
https://github.com/servo/servo.git
synced 2025-06-29 03:23:41 +01:00
Co-authored-by: Emilio Cobos Álvarez <emilio@crisal.io> Bug: 1436048 Reviewed-by: jfkthame,jwatt MozReview-Commit-ID: 7ONYtICeAqb
445 lines
14 KiB
Rust
445 lines
14 KiB
Rust
/* 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/. */
|
|
|
|
//! Little helpers for `nsCSSValue`.
|
|
|
|
use gecko_bindings::bindings;
|
|
use gecko_bindings::structs;
|
|
use gecko_bindings::structs::{nsCSSUnit, nsCSSValue};
|
|
use gecko_bindings::structs::{nsCSSValueList, nsCSSValue_Array};
|
|
use gecko_string_cache::Atom;
|
|
use std::marker::PhantomData;
|
|
use std::mem;
|
|
use std::ops::{Index, IndexMut};
|
|
use std::slice;
|
|
use values::computed::{Angle, Length, LengthOrPercentage, Percentage};
|
|
use values::specified::url::SpecifiedUrl;
|
|
|
|
impl nsCSSValue {
|
|
/// Create a CSSValue with null unit, useful to be used as a return value.
|
|
#[inline]
|
|
pub fn null() -> Self {
|
|
unsafe { mem::zeroed() }
|
|
}
|
|
|
|
/// Returns true if this nsCSSValue is none.
|
|
#[inline]
|
|
pub fn is_none(&self) -> bool {
|
|
self.mUnit == nsCSSUnit::eCSSUnit_None
|
|
}
|
|
|
|
/// Returns this nsCSSValue value as an integer, unchecked in release
|
|
/// builds.
|
|
pub fn integer_unchecked(&self) -> i32 {
|
|
debug_assert!(
|
|
self.mUnit == nsCSSUnit::eCSSUnit_Integer ||
|
|
self.mUnit == nsCSSUnit::eCSSUnit_Enumerated
|
|
);
|
|
unsafe { *self.mValue.mInt.as_ref() }
|
|
}
|
|
|
|
/// Checks if it is an integer and returns it if so
|
|
pub fn integer(&self) -> Option<i32> {
|
|
if self.mUnit == nsCSSUnit::eCSSUnit_Integer || self.mUnit == nsCSSUnit::eCSSUnit_Enumerated
|
|
{
|
|
Some(unsafe { *self.mValue.mInt.as_ref() })
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
/// Returns this nsCSSValue value as a floating point value, unchecked in
|
|
/// release builds.
|
|
pub fn float_unchecked(&self) -> f32 {
|
|
debug_assert!(nsCSSUnit::eCSSUnit_Number as u32 <= self.mUnit as u32);
|
|
unsafe { *self.mValue.mFloat.as_ref() }
|
|
}
|
|
|
|
/// Returns this nsCSSValue as a nsCSSValue::Array, unchecked in release
|
|
/// builds.
|
|
pub unsafe fn array_unchecked(&self) -> &nsCSSValue_Array {
|
|
debug_assert!(
|
|
nsCSSUnit::eCSSUnit_Array as u32 <= self.mUnit as u32 &&
|
|
self.mUnit as u32 <= nsCSSUnit::eCSSUnit_Calc_Divided as u32
|
|
);
|
|
let array = *self.mValue.mArray.as_ref();
|
|
debug_assert!(!array.is_null());
|
|
&*array
|
|
}
|
|
|
|
/// Sets LengthOrPercentage value to this nsCSSValue.
|
|
pub unsafe fn set_lop(&mut self, lop: LengthOrPercentage) {
|
|
match lop {
|
|
LengthOrPercentage::Length(px) => self.set_px(px.px()),
|
|
LengthOrPercentage::Percentage(pc) => self.set_percentage(pc.0),
|
|
LengthOrPercentage::Calc(calc) => bindings::Gecko_CSSValue_SetCalc(self, calc.into()),
|
|
}
|
|
}
|
|
|
|
/// Sets a px value to this nsCSSValue.
|
|
pub unsafe fn set_px(&mut self, px: f32) {
|
|
bindings::Gecko_CSSValue_SetPixelLength(self, px)
|
|
}
|
|
|
|
/// Sets a percentage value to this nsCSSValue.
|
|
pub unsafe fn set_percentage(&mut self, unit_value: f32) {
|
|
bindings::Gecko_CSSValue_SetPercentage(self, unit_value)
|
|
}
|
|
|
|
/// Returns LengthOrPercentage value.
|
|
pub unsafe fn get_lop(&self) -> LengthOrPercentage {
|
|
match self.mUnit {
|
|
nsCSSUnit::eCSSUnit_Pixel => {
|
|
LengthOrPercentage::Length(Length::new(bindings::Gecko_CSSValue_GetNumber(self)))
|
|
},
|
|
nsCSSUnit::eCSSUnit_Percent => LengthOrPercentage::Percentage(Percentage(
|
|
bindings::Gecko_CSSValue_GetPercentage(self),
|
|
)),
|
|
nsCSSUnit::eCSSUnit_Calc => {
|
|
LengthOrPercentage::Calc(bindings::Gecko_CSSValue_GetCalc(self).into())
|
|
},
|
|
_ => panic!("Unexpected unit"),
|
|
}
|
|
}
|
|
|
|
/// Returns Length value.
|
|
pub unsafe fn get_length(&self) -> Length {
|
|
match self.mUnit {
|
|
nsCSSUnit::eCSSUnit_Pixel => Length::new(bindings::Gecko_CSSValue_GetNumber(self)),
|
|
_ => panic!("Unexpected unit"),
|
|
}
|
|
}
|
|
|
|
fn set_valueless_unit(&mut self, unit: nsCSSUnit) {
|
|
debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_Null);
|
|
debug_assert!(
|
|
unit as u32 <= nsCSSUnit::eCSSUnit_DummyInherit as u32,
|
|
"Not a valueless unit"
|
|
);
|
|
self.mUnit = unit;
|
|
}
|
|
|
|
/// Set to an auto value
|
|
///
|
|
/// This method requires the current value to be null.
|
|
pub fn set_auto(&mut self) {
|
|
self.set_valueless_unit(nsCSSUnit::eCSSUnit_Auto);
|
|
}
|
|
|
|
/// Set to a normal value
|
|
///
|
|
/// This method requires the current value to be null.
|
|
pub fn set_normal(&mut self) {
|
|
self.set_valueless_unit(nsCSSUnit::eCSSUnit_Normal);
|
|
}
|
|
|
|
fn set_string_internal(&mut self, s: &str, unit: nsCSSUnit) {
|
|
unsafe { bindings::Gecko_CSSValue_SetString(self, s.as_ptr(), s.len() as u32, unit) }
|
|
}
|
|
|
|
fn set_string_from_atom_internal(&mut self, s: &Atom, unit: nsCSSUnit) {
|
|
unsafe { bindings::Gecko_CSSValue_SetStringFromAtom(self, s.as_ptr(), unit) }
|
|
}
|
|
|
|
/// Set to a string value
|
|
pub fn set_string(&mut self, s: &str) {
|
|
self.set_string_internal(s, nsCSSUnit::eCSSUnit_String)
|
|
}
|
|
|
|
/// Set to a string value from the given atom
|
|
pub fn set_string_from_atom(&mut self, s: &Atom) {
|
|
self.set_string_from_atom_internal(s, nsCSSUnit::eCSSUnit_String)
|
|
}
|
|
|
|
/// Set to a ident value from the given atom
|
|
pub fn set_ident_from_atom(&mut self, s: &Atom) {
|
|
self.set_string_from_atom_internal(s, nsCSSUnit::eCSSUnit_Ident)
|
|
}
|
|
|
|
/// Set to an identifier value
|
|
pub fn set_ident(&mut self, s: &str) {
|
|
self.set_string_internal(s, nsCSSUnit::eCSSUnit_Ident)
|
|
}
|
|
|
|
/// Set to an atom identifier value
|
|
pub fn set_atom_ident(&mut self, s: Atom) {
|
|
unsafe { bindings::Gecko_CSSValue_SetAtomIdent(self, s.into_addrefed()) }
|
|
}
|
|
|
|
/// Set to a font format.
|
|
pub fn set_font_format(&mut self, s: &str) {
|
|
self.set_string_internal(s, nsCSSUnit::eCSSUnit_Font_Format);
|
|
}
|
|
|
|
/// Set to a local font value.
|
|
pub fn set_local_font(&mut self, s: &Atom) {
|
|
self.set_string_from_atom_internal(s, nsCSSUnit::eCSSUnit_Local_Font);
|
|
}
|
|
|
|
/// Set to a font stretch.
|
|
pub fn set_font_stretch(&mut self, s: f32) {
|
|
unsafe { bindings::Gecko_CSSValue_SetFontStretch(self, s) }
|
|
}
|
|
|
|
/// Set to a font style
|
|
pub fn set_font_style(&mut self, s: f32) {
|
|
unsafe { bindings::Gecko_CSSValue_SetFontSlantStyle(self, s) }
|
|
}
|
|
|
|
/// Set to a font weight
|
|
pub fn set_font_weight(&mut self, w: f32) {
|
|
unsafe { bindings::Gecko_CSSValue_SetFontWeight(self, w) }
|
|
}
|
|
|
|
fn set_int_internal(&mut self, value: i32, unit: nsCSSUnit) {
|
|
unsafe { bindings::Gecko_CSSValue_SetInt(self, value, unit) }
|
|
}
|
|
|
|
/// Set to an integer value
|
|
pub fn set_integer(&mut self, value: i32) {
|
|
self.set_int_internal(value, nsCSSUnit::eCSSUnit_Integer)
|
|
}
|
|
|
|
/// Set to an enumerated value
|
|
pub fn set_enum<T: Into<i32>>(&mut self, value: T) {
|
|
self.set_int_internal(value.into(), nsCSSUnit::eCSSUnit_Enumerated);
|
|
}
|
|
|
|
/// Set to a number value
|
|
pub fn set_number(&mut self, number: f32) {
|
|
unsafe { bindings::Gecko_CSSValue_SetFloat(self, number, nsCSSUnit::eCSSUnit_Number) }
|
|
}
|
|
|
|
/// Set to a url value
|
|
pub fn set_url(&mut self, url: &SpecifiedUrl) {
|
|
unsafe { bindings::Gecko_CSSValue_SetURL(self, url.url_value.get()) }
|
|
}
|
|
|
|
/// Set to an array of given length
|
|
pub fn set_array(&mut self, len: i32) -> &mut nsCSSValue_Array {
|
|
unsafe { bindings::Gecko_CSSValue_SetArray(self, len) }
|
|
unsafe { self.mValue.mArray.as_mut().as_mut() }.unwrap()
|
|
}
|
|
|
|
/// Generic set from any value that implements the ToNsCssValue trait.
|
|
pub fn set_from<T: ToNsCssValue>(&mut self, value: T) {
|
|
value.convert(self)
|
|
}
|
|
|
|
/// Returns an `Angle` value from this `nsCSSValue`.
|
|
///
|
|
/// Panics if the unit is not `eCSSUnit_Degree` `eCSSUnit_Grad`, `eCSSUnit_Turn`
|
|
/// or `eCSSUnit_Radian`.
|
|
pub fn get_angle(&self) -> Angle {
|
|
Angle::from_gecko_values(self.float_unchecked(), self.mUnit)
|
|
}
|
|
|
|
/// Sets Angle value to this nsCSSValue.
|
|
pub fn set_angle(&mut self, angle: Angle) {
|
|
debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_Null);
|
|
let (value, unit) = angle.to_gecko_values();
|
|
self.mUnit = unit;
|
|
unsafe {
|
|
*self.mValue.mFloat.as_mut() = value;
|
|
}
|
|
}
|
|
|
|
/// Set to a pair value
|
|
///
|
|
/// This is only supported on the main thread.
|
|
pub fn set_pair(&mut self, x: &nsCSSValue, y: &nsCSSValue) {
|
|
unsafe { bindings::Gecko_CSSValue_SetPair(self, x, y) }
|
|
}
|
|
|
|
/// Set to a list value
|
|
///
|
|
/// This is only supported on the main thread.
|
|
pub fn set_list<I>(&mut self, values: I)
|
|
where
|
|
I: ExactSizeIterator<Item = nsCSSValue>,
|
|
{
|
|
debug_assert!(values.len() > 0, "Empty list is not supported");
|
|
unsafe {
|
|
bindings::Gecko_CSSValue_SetList(self, values.len() as u32);
|
|
}
|
|
debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_List);
|
|
let list: &mut structs::nsCSSValueList = &mut unsafe {
|
|
self.mValue.mList.as_ref() // &*nsCSSValueList_heap
|
|
.as_mut().expect("List pointer should be non-null")
|
|
}._base;
|
|
for (item, new_value) in list.into_iter().zip(values) {
|
|
*item = new_value;
|
|
}
|
|
}
|
|
|
|
/// Set to a pair list value
|
|
///
|
|
/// This is only supported on the main thread.
|
|
pub fn set_pair_list<I>(&mut self, mut values: I)
|
|
where
|
|
I: ExactSizeIterator<Item = (nsCSSValue, nsCSSValue)>,
|
|
{
|
|
debug_assert!(values.len() > 0, "Empty list is not supported");
|
|
unsafe {
|
|
bindings::Gecko_CSSValue_SetPairList(self, values.len() as u32);
|
|
}
|
|
debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_PairList);
|
|
let mut item_ptr = &mut unsafe {
|
|
self.mValue.mPairList.as_ref() // &*nsCSSValuePairList_heap
|
|
.as_mut().expect("List pointer should be non-null")
|
|
}._base as *mut structs::nsCSSValuePairList;
|
|
while let Some(item) = unsafe { item_ptr.as_mut() } {
|
|
let value = values.next().expect("Values shouldn't have been exhausted");
|
|
item.mXValue = value.0;
|
|
item.mYValue = value.1;
|
|
item_ptr = item.mNext;
|
|
}
|
|
debug_assert!(values.next().is_none(), "Values should have been exhausted");
|
|
}
|
|
|
|
/// Set a shared list
|
|
pub fn set_shared_list<I>(&mut self, values: I)
|
|
where
|
|
I: ExactSizeIterator<Item = nsCSSValue>,
|
|
{
|
|
debug_assert!(values.len() > 0, "Empty list is not supported");
|
|
unsafe { bindings::Gecko_CSSValue_InitSharedList(self, values.len() as u32) };
|
|
debug_assert_eq!(self.mUnit, nsCSSUnit::eCSSUnit_SharedList);
|
|
let list = unsafe {
|
|
self.mValue
|
|
.mSharedList
|
|
.as_ref()
|
|
.as_mut()
|
|
.expect("List pointer should be non-null")
|
|
.mHead
|
|
.as_mut()
|
|
};
|
|
debug_assert!(list.is_some(), "New created shared list shouldn't be null");
|
|
for (item, new_value) in list.unwrap().into_iter().zip(values) {
|
|
*item = new_value;
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Drop for nsCSSValue {
|
|
fn drop(&mut self) {
|
|
unsafe { bindings::Gecko_CSSValue_Drop(self) };
|
|
}
|
|
}
|
|
|
|
/// Iterator of nsCSSValueList.
|
|
#[allow(non_camel_case_types)]
|
|
pub struct nsCSSValueListIterator<'a> {
|
|
current: Option<&'a nsCSSValueList>,
|
|
}
|
|
|
|
impl<'a> Iterator for nsCSSValueListIterator<'a> {
|
|
type Item = &'a nsCSSValue;
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
match self.current {
|
|
Some(item) => {
|
|
self.current = unsafe { item.mNext.as_ref() };
|
|
Some(&item.mValue)
|
|
},
|
|
None => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> IntoIterator for &'a nsCSSValueList {
|
|
type Item = &'a nsCSSValue;
|
|
type IntoIter = nsCSSValueListIterator<'a>;
|
|
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
nsCSSValueListIterator {
|
|
current: Some(self),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Mutable Iterator of nsCSSValueList.
|
|
#[allow(non_camel_case_types)]
|
|
pub struct nsCSSValueListMutIterator<'a> {
|
|
current: *mut nsCSSValueList,
|
|
phantom: PhantomData<&'a mut nsCSSValue>,
|
|
}
|
|
|
|
impl<'a> Iterator for nsCSSValueListMutIterator<'a> {
|
|
type Item = &'a mut nsCSSValue;
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
match unsafe { self.current.as_mut() } {
|
|
Some(item) => {
|
|
self.current = item.mNext;
|
|
Some(&mut item.mValue)
|
|
},
|
|
None => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> IntoIterator for &'a mut nsCSSValueList {
|
|
type Item = &'a mut nsCSSValue;
|
|
type IntoIter = nsCSSValueListMutIterator<'a>;
|
|
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
nsCSSValueListMutIterator {
|
|
current: self as *mut nsCSSValueList,
|
|
phantom: PhantomData,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl nsCSSValue_Array {
|
|
/// Return the length of this `nsCSSValue::Array`
|
|
#[inline]
|
|
pub fn len(&self) -> usize {
|
|
self.mCount
|
|
}
|
|
|
|
#[inline]
|
|
fn buffer(&self) -> *const nsCSSValue {
|
|
self.mArray.as_ptr()
|
|
}
|
|
|
|
/// Get the array as a slice of nsCSSValues.
|
|
#[inline]
|
|
pub fn as_slice(&self) -> &[nsCSSValue] {
|
|
unsafe { slice::from_raw_parts(self.buffer(), self.len()) }
|
|
}
|
|
|
|
/// Get the array as a mutable slice of nsCSSValues.
|
|
#[inline]
|
|
pub fn as_mut_slice(&mut self) -> &mut [nsCSSValue] {
|
|
unsafe { slice::from_raw_parts_mut(self.buffer() as *mut _, self.len()) }
|
|
}
|
|
}
|
|
|
|
impl Index<usize> for nsCSSValue_Array {
|
|
type Output = nsCSSValue;
|
|
#[inline]
|
|
fn index(&self, i: usize) -> &nsCSSValue {
|
|
&self.as_slice()[i]
|
|
}
|
|
}
|
|
|
|
impl IndexMut<usize> for nsCSSValue_Array {
|
|
#[inline]
|
|
fn index_mut(&mut self, i: usize) -> &mut nsCSSValue {
|
|
&mut self.as_mut_slice()[i]
|
|
}
|
|
}
|
|
|
|
/// Generic conversion to nsCSSValue
|
|
pub trait ToNsCssValue {
|
|
/// Convert
|
|
fn convert(self, nscssvalue: &mut nsCSSValue);
|
|
}
|
|
|
|
impl<T: ToNsCssValue> From<T> for nsCSSValue {
|
|
fn from(value: T) -> nsCSSValue {
|
|
let mut result = nsCSSValue::null();
|
|
value.convert(&mut result);
|
|
result
|
|
}
|
|
}
|