Auto merge of #21188 - emilio:gecko-sync, r=emilio

style: Sync changes from mozilla-central.

https://bugzilla.mozilla.org/show_bug.cgi?id=1476054

See each individual commit for details. I skipped the import of bug 1467277, which can't be done until euclid is updated everywhere.
This commit is contained in:
bors-servo 2018-07-16 14:01:41 -04:00 committed by GitHub
commit bd6f3cb5ee
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 376 additions and 751 deletions

View file

@ -693,6 +693,90 @@ impl MallocSizeOf for selectors::parser::AncestorHashes {
}
}
impl<Impl: selectors::parser::SelectorImpl> MallocSizeOf
for selectors::parser::Selector<Impl>
where
Impl::NonTSPseudoClass: MallocSizeOf,
Impl::PseudoElement: MallocSizeOf,
{
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
let mut n = 0;
// It's OK to measure this ThinArc directly because it's the
// "primary" reference. (The secondary references are on the
// Stylist.)
n += unsafe { ops.malloc_size_of(self.thin_arc_heap_ptr()) };
for component in self.iter_raw_match_order() {
n += component.size_of(ops);
}
n
}
}
impl<Impl: selectors::parser::SelectorImpl> MallocSizeOf
for selectors::parser::Component<Impl>
where
Impl::NonTSPseudoClass: MallocSizeOf,
Impl::PseudoElement: MallocSizeOf,
{
fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
use selectors::parser::Component;
match self {
Component::AttributeOther(ref attr_selector) => {
attr_selector.size_of(ops)
}
Component::Negation(ref components) => {
components.size_of(ops)
}
Component::NonTSPseudoClass(ref pseudo) => {
(*pseudo).size_of(ops)
}
Component::Slotted(ref selector) |
Component::Host(Some(ref selector)) => {
selector.size_of(ops)
}
Component::PseudoElement(ref pseudo) => {
(*pseudo).size_of(ops)
}
Component::Combinator(..) |
Component::ExplicitAnyNamespace |
Component::ExplicitNoNamespace |
Component::DefaultNamespace(..) |
Component::Namespace(..) |
Component::ExplicitUniversalType |
Component::LocalName(..) |
Component::ID(..) |
Component::Class(..) |
Component::AttributeInNoNamespaceExists { .. } |
Component::AttributeInNoNamespace { .. } |
Component::FirstChild |
Component::LastChild |
Component::OnlyChild |
Component::Root |
Component::Empty |
Component::Scope |
Component::NthChild(..) |
Component::NthLastChild(..) |
Component::NthOfType(..) |
Component::NthLastOfType(..) |
Component::FirstOfType |
Component::LastOfType |
Component::OnlyOfType |
Component::Host(None) => 0,
}
}
}
impl<Impl: selectors::parser::SelectorImpl> MallocSizeOf
for selectors::attr::AttrSelectorWithNamespace<Impl>
{
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {
0
}
}
impl MallocSizeOf for Void {
#[inline]
fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize {

View file

@ -17,7 +17,7 @@ use dom_struct::dom_struct;
use servo_arc::Arc;
use servo_url::ServoUrl;
use style::attr::AttrValue;
use style::properties::{DeclarationSource, Importance, PropertyDeclarationBlock, PropertyId, LonghandId, ShorthandId};
use style::properties::{DeclarationPushMode, Importance, PropertyDeclarationBlock, PropertyId, LonghandId, ShorthandId};
use style::properties::{parse_one_declaration_into, parse_style_attribute, SourcePropertyDeclaration};
use style::selector_parser::PseudoElement;
use style::shared_lock::Locked;
@ -302,7 +302,7 @@ impl CSSStyleDeclaration {
*changed = pdb.extend(
declarations.drain(),
importance,
DeclarationSource::CssOm,
DeclarationPushMode::Update,
);
Ok(())

View file

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/// Gecko's pseudo-element definition.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
pub enum PseudoElement {
% for pseudo in PSEUDOS:
/// ${pseudo.value}

View file

@ -40,7 +40,7 @@ macro_rules! pseudo_class_name {
(bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
/// Our representation of a non tree-structural pseudo-class.
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
pub enum NonTSPseudoClass {
$(
#[doc = $css]

View file

@ -285,7 +285,6 @@ class Longhand(object):
"FontSynthesis",
"FontWeight",
"GridAutoFlow",
"ImageOrientation",
"InitialLetter",
"Integer",
"JustifyContent",

View file

@ -39,13 +39,21 @@ impl AnimationRules {
}
}
/// Whether a given declaration comes from CSS parsing, or from CSSOM.
/// Enum for how a given declaration should be pushed into a declaration block.
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
pub enum DeclarationSource {
/// The declaration was obtained from CSS parsing of sheets and such.
pub enum DeclarationPushMode {
/// Mode used when declarations were obtained from CSS parsing.
/// If there is an existing declaration of the same property with a higher
/// importance, the new declaration will be discarded. Otherwise, it will
/// be appended to the end of the declaration block.
Parsing,
/// The declaration was obtained from CSSOM.
CssOm,
/// In this mode, if there is an existing declaration of the same property,
/// the value is updated in-place. Otherwise it's appended. This is one
/// possible behavior of CSSOM.
Update,
/// In this mode, the new declaration is always pushed to the end of the
/// declaration block. This is another possible behavior of CSSOM.
Append,
}
/// A declaration [importance][importance].
@ -410,18 +418,57 @@ impl PropertyDeclarationBlock {
}
}
/// Returns whether the property is definitely new for this declaration
/// block. It returns true when the declaration is a non-custom longhand
/// and it doesn't exist in the block, and returns false otherwise.
#[inline]
fn is_definitely_new(&self, decl: &PropertyDeclaration) -> bool {
match decl.id() {
PropertyDeclarationId::Longhand(id) => !self.longhands.contains(id),
PropertyDeclarationId::Custom(..) => false,
}
}
/// Returns whether calling extend with `DeclarationPushMode::Update`
/// will cause this declaration block to change.
pub fn will_change_in_update_mode(
&self,
source_declarations: &SourcePropertyDeclaration,
importance: Importance,
) -> bool {
// XXX The type of parameter seems to be necessary because otherwise
// the compiler complains about `decl` not living long enough in the
// all_shorthand expression. Why?
let needs_update = |decl: &_| {
if self.is_definitely_new(decl) {
return true;
}
self.declarations.iter().enumerate()
.find(|&(_, ref slot)| slot.id() == decl.id())
.map_or(true, |(i, slot)| {
let important = self.declarations_importance[i];
*slot != *decl || important != importance.important()
})
};
source_declarations.declarations.iter().any(&needs_update) ||
source_declarations.all_shorthand.declarations().any(|decl| needs_update(&decl))
}
/// Adds or overrides the declaration for a given property in this block.
///
/// See the documentation of `push` to see what impact `source` has when the
/// property is already there.
///
/// When calling with `DeclarationPushMode::Update`, this should not change
/// anything if `will_change_in_update_mode` returns false.
pub fn extend(
&mut self,
mut drain: SourcePropertyDeclarationDrain,
importance: Importance,
source: DeclarationSource,
mode: DeclarationPushMode,
) -> bool {
match source {
DeclarationSource::Parsing => {
match mode {
DeclarationPushMode::Parsing => {
let all_shorthand_len = match drain.all_shorthand {
AllShorthand::NotSet => 0,
AllShorthand::CSSWideKeyword(_) |
@ -433,7 +480,7 @@ impl PropertyDeclarationBlock {
// With deduplication the actual length increase may be less than this.
self.declarations.reserve(push_calls_count);
}
DeclarationSource::CssOm => {
_ => {
// Don't bother reserving space, since it's usually the case
// that CSSOM just overrides properties and we don't need to use
// more memory. See bug 1424346 for an example where this
@ -448,75 +495,35 @@ impl PropertyDeclarationBlock {
changed |= self.push(
decl,
importance,
source,
mode,
);
}
match drain.all_shorthand {
AllShorthand::NotSet => {}
AllShorthand::CSSWideKeyword(keyword) => {
for id in ShorthandId::All.longhands() {
let decl = PropertyDeclaration::CSSWideKeyword(
WideKeywordDeclaration { id, keyword },
);
changed |= self.push(
decl,
importance,
source,
);
}
}
AllShorthand::WithVariables(unparsed) => {
for id in ShorthandId::All.longhands() {
let decl = PropertyDeclaration::WithVariables(
VariableDeclaration { id, value: unparsed.clone() },
);
changed |= self.push(
decl,
importance,
source,
);
}
}
}
changed
drain.all_shorthand.declarations().fold(changed, |changed, decl| {
changed | self.push(decl, importance, mode)
})
}
/// Adds or overrides the declaration for a given property in this block.
///
/// Depending on the value of `source`, this has a different behavior in the
/// Depending on the value of `mode`, this has a different behavior in the
/// presence of another declaration with the same ID in the declaration
/// block.
///
/// * For `DeclarationSource::Parsing`, this will not override a
/// declaration with more importance, and will ensure that, if inserted,
/// it's inserted at the end of the declaration block.
///
/// * For `DeclarationSource::CssOm`, this will override importance.
///
/// Returns whether the declaration has changed.
pub fn push(
&mut self,
declaration: PropertyDeclaration,
importance: Importance,
source: DeclarationSource,
mode: DeclarationPushMode,
) -> bool {
let longhand_id = match declaration.id() {
PropertyDeclarationId::Longhand(id) => Some(id),
PropertyDeclarationId::Custom(..) => None,
};
let definitely_new = longhand_id.map_or(false, |id| {
!self.longhands.contains(id)
});
if !definitely_new {
if !self.is_definitely_new(&declaration) {
let mut index_to_remove = None;
for (i, slot) in self.declarations.iter_mut().enumerate() {
if slot.id() != declaration.id() {
continue;
}
if matches!(source, DeclarationSource::Parsing) {
if matches!(mode, DeclarationPushMode::Parsing) {
let important = self.declarations_importance[i];
// For declarations from parsing, non-important declarations
@ -541,6 +548,15 @@ impl PropertyDeclarationBlock {
}
}
}
if matches!(mode, DeclarationPushMode::Update) {
let important = self.declarations_importance[i];
if *slot == declaration && important == importance.important() {
return false;
}
*slot = declaration;
self.declarations_importance.set(i, importance.important());
return true;
}
index_to_remove = Some(i);
break;
@ -555,7 +571,7 @@ impl PropertyDeclarationBlock {
}
}
if let Some(id) = longhand_id {
if let PropertyDeclarationId::Longhand(id) = declaration.id() {
self.longhands.insert(id);
}
self.declarations.push(declaration);
@ -1209,7 +1225,7 @@ pub fn parse_property_declaration_list(
block.extend(
iter.parser.declarations.drain(),
importance,
DeclarationSource::Parsing,
DeclarationPushMode::Parsing,
);
}
Err((error, slice)) => {

View file

@ -83,7 +83,6 @@ pub struct ComputedValues(::gecko_bindings::structs::mozilla::ComputedStyle);
impl ComputedValues {
pub fn new(
device: &Device,
parent: Option<<&ComputedValues>,
pseudo: Option<<&PseudoElement>,
custom_properties: Option<Arc<CustomPropertiesMap>>,
writing_mode: WritingMode,
@ -105,7 +104,6 @@ impl ComputedValues {
% endfor
).to_outer(
device.pres_context(),
parent,
pseudo.map(|p| p.pseudo_info())
)
}
@ -120,7 +118,7 @@ impl ComputedValues {
% for style_struct in data.style_structs:
style_structs::${style_struct.name}::default(pres_context),
% endfor
).to_outer(pres_context, None, None)
).to_outer(pres_context, None)
}
pub fn pseudo(&self) -> Option<PseudoElement> {
@ -195,7 +193,6 @@ impl Clone for ComputedValuesInner {
}
type PseudoInfo = (*mut structs::nsAtom, structs::CSSPseudoElementType);
type ParentComputedStyleInfo<'a> = Option< &'a ComputedValues>;
impl ComputedValuesInner {
pub fn new(custom_properties: Option<Arc<CustomPropertiesMap>>,
@ -222,7 +219,6 @@ impl ComputedValuesInner {
fn to_outer(
self,
pres_context: RawGeckoPresContextBorrowed,
parent: ParentComputedStyleInfo,
info: Option<PseudoInfo>
) -> Arc<ComputedValues> {
let (tag, ty) = if let Some(info) = info {
@ -231,21 +227,24 @@ impl ComputedValuesInner {
(ptr::null_mut(), structs::CSSPseudoElementType::NotPseudo)
};
unsafe { self.to_outer_helper(pres_context, parent, ty, tag) }
unsafe { self.to_outer_helper(pres_context, ty, tag) }
}
unsafe fn to_outer_helper(
self,
pres_context: bindings::RawGeckoPresContextBorrowed,
parent: ParentComputedStyleInfo,
pseudo_ty: structs::CSSPseudoElementType,
pseudo_tag: *mut structs::nsAtom
) -> Arc<ComputedValues> {
let arc = {
let arc: Arc<ComputedValues> = Arc::new(uninitialized());
bindings::Gecko_ComputedStyle_Init(&arc.0 as *const _ as *mut _,
parent, pres_context,
&self, pseudo_ty, pseudo_tag);
bindings::Gecko_ComputedStyle_Init(
&arc.0 as *const _ as *mut _,
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);
@ -668,14 +667,14 @@ def set_gecko_property(ffi_name, expr):
SVGPaintKind::Color(color) => {
paint.mType = nsStyleSVGPaintType::eStyleSVGPaintType_Color;
unsafe {
*paint.mPaint.mColor.as_mut() = convert_rgba_to_nscolor(&color);
*paint.mPaint.mColor.as_mut() = color.into();
}
}
}
paint.mFallbackType = match fallback {
Some(Either::First(color)) => {
paint.mFallbackColor = convert_rgba_to_nscolor(&color);
paint.mFallbackColor = color.into();
nsStyleSVGFallbackType::eStyleSVGFallbackType_Color
},
Some(Either::Second(_)) => {
@ -710,7 +709,7 @@ def set_gecko_property(ffi_name, expr):
let fallback = match paint.mFallbackType {
nsStyleSVGFallbackType::eStyleSVGFallbackType_Color => {
Some(Either::First(convert_nscolor_to_rgba(paint.mFallbackColor)))
Some(Either::First(paint.mFallbackColor.into()))
},
nsStyleSVGFallbackType::eStyleSVGFallbackType_None => {
Some(Either::Second(None_))
@ -729,7 +728,8 @@ def set_gecko_property(ffi_name, expr):
})
}
nsStyleSVGPaintType::eStyleSVGPaintType_Color => {
unsafe { SVGPaintKind::Color(convert_nscolor_to_rgba(*paint.mPaint.mColor.as_ref())) }
let col = unsafe { *paint.mPaint.mColor.as_ref() };
SVGPaintKind::Color(col.into())
}
};
SVGPaint {
@ -3083,6 +3083,17 @@ fn static_assert() {
<%call expr="impl_keyword_clone('display', 'mDisplay', display_keyword)"></%call>
<% float_keyword = Keyword("float", "Left Right None", gecko_enum_prefix="StyleFloat") %>
${impl_keyword('float', 'mFloat', float_keyword)}
<% clear_keyword = Keyword(
"clear",
"Left Right None Both",
gecko_enum_prefix="StyleClear",
gecko_inexhaustive=True,
) %>
${impl_keyword('clear', 'mBreakType', clear_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;
@ -4533,64 +4544,7 @@ fn static_assert() {
</%self:impl_trait>
<%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_Angles;
use properties::longhands::image_orientation::computed_value::T;
use values::computed::Orientation;
let gecko_orientation = self.gecko.mImageOrientation.mOrientation;
if gecko_orientation & structs::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 & structs::nsStyleImageOrientation_Bits_FLIP_MASK as u8 != 0;
let orientation =
match gecko_orientation & structs::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="InheritedBox">
</%self:impl_trait>
<%self:impl_trait style_struct_name="InheritedTable"

View file

@ -29,7 +29,7 @@ use hash::FnvHashMap;
use super::ComputedValues;
use values::CSSFloat;
use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero};
use values::animated::color::RGBA as AnimatedRGBA;
use values::animated::color::Color as AnimatedColor;
use values::animated::effects::Filter as AnimatedFilter;
#[cfg(feature = "gecko")] use values::computed::TransitionProperty;
use values::computed::{Angle, CalcLengthOrPercentage};
@ -2674,10 +2674,10 @@ impl ComputeSquaredDistance for ComputedTransform {
}
/// Animated SVGPaint
pub type IntermediateSVGPaint = SVGPaint<AnimatedRGBA, ComputedUrl>;
pub type IntermediateSVGPaint = SVGPaint<AnimatedColor, ComputedUrl>;
/// Animated SVGPaintKind
pub type IntermediateSVGPaintKind = SVGPaintKind<AnimatedRGBA, ComputedUrl>;
pub type IntermediateSVGPaintKind = SVGPaintKind<AnimatedColor, ComputedUrl>;
impl ToAnimatedZero for IntermediateSVGPaint {
#[inline]

View file

@ -76,11 +76,12 @@ ${helpers.single_keyword(
spec="https://drafts.csswg.org/css-images/#propdef-image-rendering",
)}
${helpers.predefined_type("image-orientation",
"ImageOrientation",
"computed::ImageOrientation::zero()",
products="gecko",
animation_value_type="discrete",
gecko_pref="layout.css.image-orientation.enabled",
spec="https://drafts.csswg.org/css-images/#propdef-image-orientation, \
/// additional values in https://developer.mozilla.org/en-US/docs/Web/CSS/image-orientation")}
${helpers.single_keyword(
"image-orientation",
"none from-image",
products="gecko",
gecko_enum_prefix="StyleImageOrientation",
animation_value_type="discrete",
gecko_pref="layout.css.image-orientation.enabled",
spec="https://drafts.csswg.org/css-images/#propdef-image-orientation",
)}

View file

@ -2222,6 +2222,49 @@ enum AllShorthand {
WithVariables(Arc<UnparsedValue>)
}
impl AllShorthand {
/// Iterates property declarations from the given all shorthand value.
#[inline]
fn declarations(&self) -> AllShorthandDeclarationIterator {
AllShorthandDeclarationIterator {
all_shorthand: self,
longhands: ShorthandId::All.longhands(),
}
}
}
struct AllShorthandDeclarationIterator<'a> {
all_shorthand: &'a AllShorthand,
longhands: NonCustomPropertyIterator<LonghandId>,
}
impl<'a> Iterator for AllShorthandDeclarationIterator<'a> {
type Item = PropertyDeclaration;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
match *self.all_shorthand {
AllShorthand::NotSet => None,
AllShorthand::CSSWideKeyword(ref keyword) => {
Some(PropertyDeclaration::CSSWideKeyword(
WideKeywordDeclaration {
id: self.longhands.next()?,
keyword: *keyword
}
))
}
AllShorthand::WithVariables(ref unparsed) => {
Some(PropertyDeclaration::WithVariables(
VariableDeclaration {
id: self.longhands.next()?,
value: unparsed.clone()
}
))
}
}
}
}
#[cfg(feature = "gecko")]
pub use gecko_properties::style_structs;
@ -2677,7 +2720,6 @@ impl ComputedValues {
/// Create a new refcounted `ComputedValues`
pub fn new(
_: &Device,
_: Option<<&ComputedValues>,
_: Option<<&PseudoElement>,
custom_properties: Option<Arc<::custom_properties::CustomPropertiesMap>>,
writing_mode: WritingMode,
@ -3085,10 +3127,6 @@ pub struct StyleBuilder<'a> {
/// The style we're getting reset structs from.
reset_style: &'a ComputedValues,
/// The style we're inheriting from explicitly, or none if we're the root of
/// a subtree.
parent_style: Option<<&'a ComputedValues>,
/// The rule node representing the ordered list of rules matched for this
/// node.
pub rules: Option<StrongRuleNode>,
@ -3156,7 +3194,6 @@ impl<'a> StyleBuilder<'a> {
StyleBuilder {
device,
parent_style,
inherited_style,
inherited_style_ignoring_first_line,
reset_style,
@ -3200,7 +3237,6 @@ impl<'a> StyleBuilder<'a> {
parent_style.unwrap().pseudo() != Some(PseudoElement::FirstLine));
StyleBuilder {
device,
parent_style,
inherited_style,
// None of our callers pass in ::first-line parent styles.
inherited_style_ignoring_first_line: inherited_style,
@ -3442,7 +3478,6 @@ impl<'a> StyleBuilder<'a> {
pub fn build(self) -> Arc<ComputedValues> {
ComputedValues::new(
self.device,
self.parent_style,
self.pseudo,
self.custom_properties,
self.writing_mode,
@ -3768,14 +3803,6 @@ where
PropertyDeclarationId::Custom(..) => continue,
};
// Only a few properties are allowed to depend on the visited state
// of links. When cascading visited styles, we can save time by
// only processing these properties.
if flags.contains(CascadeFlags::VISITED_DEPENDENT_ONLY) &&
!longhand_id.is_visited_dependent() {
continue
}
if !apply_reset && !longhand_id.inherited() {
continue;
}
@ -3795,6 +3822,14 @@ where
continue
}
// Only a few properties are allowed to depend on the visited state
// of links. When cascading visited styles, we can save time by
// only processing these properties.
if flags.contains(CascadeFlags::VISITED_DEPENDENT_ONLY) &&
!physical_longhand_id.is_visited_dependent() {
continue
}
let mut declaration = match *declaration {
PropertyDeclaration::WithVariables(ref declaration) => {
if !declaration.id.inherited() {

View file

@ -172,7 +172,7 @@ impl<T> PerPseudoElementMap<T> {
}
/// Values for the :dir() pseudo class
#[derive(Clone, Debug, Eq, PartialEq)]
#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)]
pub enum Direction {
/// left-to-right semantic directionality
Ltr,

View file

@ -8,7 +8,7 @@ use cssparser::{AtRuleParser, CowRcStr, Parser, ParserInput, QualifiedRuleParser
use cssparser::{parse_one_rule, DeclarationListParser, DeclarationParser, SourceLocation, Token};
use error_reporting::ContextualParseError;
use parser::ParserContext;
use properties::{DeclarationSource, Importance, PropertyDeclaration};
use properties::{DeclarationPushMode, Importance, PropertyDeclaration};
use properties::{LonghandId, PropertyDeclarationBlock, PropertyId};
use properties::{PropertyDeclarationId, SourcePropertyDeclaration};
use properties::LonghandIdSet;
@ -554,7 +554,7 @@ impl<'a, 'i> QualifiedRuleParser<'i> for KeyframeListParser<'a> {
block.extend(
iter.parser.declarations.drain(),
Importance::Normal,
DeclarationSource::Parsing,
DeclarationPushMode::Parsing,
);
},
Err((error, slice)) => {

View file

@ -6,7 +6,7 @@
use cssparser::SourceLocation;
#[cfg(feature = "gecko")]
use malloc_size_of::{MallocShallowSizeOf, MallocSizeOf, MallocSizeOfOps};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
#[cfg(feature = "gecko")]
use malloc_size_of::MallocUnconditionalShallowSizeOf;
use properties::PropertyDeclarationBlock;
@ -50,20 +50,9 @@ impl StyleRule {
#[cfg(feature = "gecko")]
pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
let mut n = 0;
// We may add measurement of things hanging off the embedded Components
// later.
n += self.selectors.0.shallow_size_of(ops);
for selector in self.selectors.0.iter() {
// It's safe to measure this ThinArc directly because it's the
// "primary" reference. (The secondary references are on the
// Stylist.)
n += unsafe { ops.malloc_size_of(selector.thin_arc_heap_ptr()) };
}
n += self.selectors.0.size_of(ops);
n += self.block.unconditional_shallow_size_of(ops) +
self.block.read_with(guard).size_of(ops);
n
}
}

View file

@ -1175,7 +1175,6 @@ impl Stylist {
pseudo_element.is_some()
);
let only_default_rules = rule_inclusion == RuleInclusion::DefaultOnly;
let matches_user_rules = rule_hash_target.matches_user_and_author_rules();
let matches_author_rules =
matches_user_rules && self.author_styles_enabled == AuthorStylesEnabled::Yes;
@ -1220,7 +1219,11 @@ impl Stylist {
}
}
if pseudo_element.is_none() && !only_default_rules {
if rule_inclusion == RuleInclusion::DefaultOnly {
return;
}
if pseudo_element.is_none() {
// Presentational hints.
//
// These go before author rules, but after user rules, see:
@ -1230,8 +1233,8 @@ impl Stylist {
context.visited_handling(),
applicable_declarations,
);
if applicable_declarations.len() != length_before_preshints {
if cfg!(debug_assertions) {
if cfg!(debug_assertions) {
if applicable_declarations.len() != length_before_preshints {
for declaration in &applicable_declarations[length_before_preshints..] {
assert_eq!(declaration.level(), CascadeLevel::PresHints);
}
@ -1248,7 +1251,7 @@ impl Stylist {
// particular, normally document rules override ::slotted() rules, but
// for !important it should be the other way around. So probably we need
// to add some sort of AuthorScoped cascade level or something.
if matches_author_rules && !only_default_rules {
if matches_author_rules {
if let Some(shadow) = rule_hash_target.shadow_root() {
if let Some(map) = shadow.style_data().host_rules(pseudo_element) {
context.with_shadow_host(Some(rule_hash_target), |context| {
@ -1315,10 +1318,8 @@ impl Stylist {
}
}
// FIXME(emilio): It looks very wrong to match XBL rules even for
// getDefaultComputedStyle!
//
// Also, this doesn't account for the author_styles_enabled stuff.
// FIXME(emilio): This doesn't account for the author_styles_enabled
// stuff...
let cut_xbl_binding_inheritance =
element.each_xbl_cascade_data(|cascade_data, quirks_mode| {
if let Some(map) = cascade_data.normal_rules(pseudo_element) {
@ -1349,7 +1350,7 @@ impl Stylist {
match_document_author_rules &= !cut_xbl_binding_inheritance;
if match_document_author_rules && !only_default_rules {
if match_document_author_rules {
// Author normal rules.
if let Some(map) = self.cascade_data.author.normal_rules(pseudo_element) {
map.get_all_matching_rules(
@ -1364,47 +1365,43 @@ impl Stylist {
}
}
if !only_default_rules {
// Style attribute ("Normal override declarations").
if let Some(sa) = style_attribute {
applicable_declarations.push(ApplicableDeclarationBlock::from_declarations(
sa.clone_arc(),
CascadeLevel::StyleAttributeNormal,
));
}
// Style attribute ("Normal override declarations").
if let Some(sa) = style_attribute {
applicable_declarations.push(ApplicableDeclarationBlock::from_declarations(
sa.clone_arc(),
CascadeLevel::StyleAttributeNormal,
));
}
// Declarations from SVG SMIL animation elements.
if let Some(so) = smil_override {
applicable_declarations.push(ApplicableDeclarationBlock::from_declarations(
so.clone_arc(),
CascadeLevel::SMILOverride,
));
}
// Declarations from SVG SMIL animation elements.
if let Some(so) = smil_override {
applicable_declarations.push(ApplicableDeclarationBlock::from_declarations(
so.clone_arc(),
CascadeLevel::SMILOverride,
));
}
// The animations sheet (CSS animations, script-generated
// animations, and CSS transitions that are no longer tied to CSS
// markup).
if let Some(anim) = animation_rules.0 {
applicable_declarations.push(ApplicableDeclarationBlock::from_declarations(
anim.clone(),
CascadeLevel::Animations,
));
}
// The animations sheet (CSS animations, script-generated
// animations, and CSS transitions that are no longer tied to CSS
// markup).
if let Some(anim) = animation_rules.0 {
applicable_declarations.push(ApplicableDeclarationBlock::from_declarations(
anim.clone(),
CascadeLevel::Animations,
));
}
//
// !important rules are handled during rule tree insertion.
//
if !only_default_rules {
// The transitions sheet (CSS transitions that are tied to CSS
// markup).
if let Some(anim) = animation_rules.1 {
applicable_declarations.push(ApplicableDeclarationBlock::from_declarations(
anim.clone(),
CascadeLevel::Transitions,
));
}
// The transitions sheet (CSS transitions that are tied to CSS
// markup).
if let Some(anim) = animation_rules.1 {
applicable_declarations.push(ApplicableDeclarationBlock::from_declarations(
anim.clone(),
CascadeLevel::Transitions,
));
}
}

View file

@ -126,11 +126,11 @@ impl Animate for Color {
let (this_weight, other_weight) = procedure.weights();
Ok(match (*self, *other, procedure) {
// Any interpolation of currentColor with currentColor returns currentColor.
(Foreground, Foreground, Procedure::Interpolate { .. }) => Color::currentcolor(),
// Any interpolation of currentcolor with currentcolor returns currentcolor.
(Foreground, Foreground, Procedure::Interpolate { .. }) => Foreground,
// Animating two numeric colors.
(Numeric(c1), Numeric(c2), _) => Numeric(c1.animate(&c2, procedure)?),
// Combinations of numeric color and currentColor
// Combinations of numeric color and currentcolor
(Foreground, Numeric(color), _) => Self::with_ratios(
color,
ComplexColorRatios {
@ -146,7 +146,7 @@ impl Animate for Color {
},
),
// Any other animation of currentColor with currentColor.
// Any other animation of currentcolor with currentcolor.
(Foreground, Foreground, _) => Self::with_ratios(
RGBA::transparent(),
ComplexColorRatios {
@ -157,20 +157,72 @@ impl Animate for Color {
// Defer to complex calculations
_ => {
// For interpolating between two complex colors, we need to
// generate colors with effective alpha value.
let self_color = self.effective_intermediate_rgba();
let other_color = other.effective_intermediate_rgba();
let color = self_color.animate(&other_color, procedure)?;
// Then we compute the final background ratio, and derive
// the final alpha value from the effective alpha value.
let self_ratios = self.effective_ratios();
let other_ratios = other.effective_ratios();
let ratios = self_ratios.animate(&other_ratios, procedure)?;
let alpha = color.alpha / ratios.bg;
let color = RGBA { alpha, ..color };
// Compute the "scaled" contribution for `color`.
fn scaled_rgba(color: &Color) -> RGBA {
match *color {
GenericColor::Numeric(color) => color,
GenericColor::Foreground => RGBA::transparent(),
GenericColor::Complex(color, ratios) => RGBA {
red: color.red * ratios.bg,
green: color.green * ratios.bg,
blue: color.blue * ratios.bg,
alpha: color.alpha * ratios.bg,
},
}
}
Self::with_ratios(color, ratios)
// Each `Color`, represents a complex combination of foreground color and
// background color where fg and bg represent the overall
// contributions. ie:
//
// color = { bg * mColor, fg * foreground }
// = { bg_color , fg_color }
// = bg_color + fg_color
//
// where `foreground` is `currentcolor`, and `bg_color`,
// `fg_color` are the scaled background and foreground
// contributions.
//
// Each operation, lerp, addition, or accumulate, can be
// represented as a scaled-addition each complex color. ie:
//
// p * col1 + q * col2
//
// where p = (1 - a), q = a for lerp(a), p = 1, q = 1 for
// addition, etc.
//
// Therefore:
//
// col1 op col2
// = p * col1 + q * col2
// = p * { bg_color1, fg_color1 } + q * { bg_color2, fg_color2 }
// = p * (bg_color1 + fg_color1) + q * (bg_color2 + fg_color2)
// = p * bg_color1 + p * fg_color1 + q * bg_color2 + p * fg_color2
// = (p * bg_color1 + q * bg_color2) + (p * fg_color1 + q * fg_color2)
// = (bg_color1 op bg_color2) + (fg_color1 op fg_color2)
//
// fg_color1 op fg_color2 is equivalent to (fg1 op fg2) * foreground,
// so the final color is:
//
// = { bg_color, fg_color }
// = { 1 * (bg_color1 op bg_color2), (fg1 op fg2) * foreground }
// To perform the operation on two complex colors, we need to
// generate the scaled contributions of each background color
// component.
let bg_color1 = scaled_rgba(self);
let bg_color2 = scaled_rgba(other);
// Perform bg_color1 op bg_color2
let bg_color = bg_color1.animate(&bg_color2, procedure)?;
// Calculate the final foreground color ratios; perform
// animation on effective fg ratios.
let ComplexColorRatios { fg: fg1, .. } = self.effective_ratios();
let ComplexColorRatios { fg: fg2, .. } = other.effective_ratios();
// Perform fg1 op fg2
let fg = fg1.animate(&fg2, procedure)?;
Self::with_ratios(bg_color, ComplexColorRatios { bg: 1., fg })
}
})
}

View file

@ -1,81 +0,0 @@
/* 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/. */
//! Computed values for inherited box
use std::fmt::{self, Write};
use style_traits::{CssWriter, ToCss};
use values::specified::Angle;
/// An angle rounded and normalized per https://drafts.csswg.org/css-images/#propdef-image-orientation
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, PartialEq)]
pub enum Orientation {
Angle0 = 0,
Angle90,
Angle180,
Angle270,
}
impl Orientation {
/// Get the actual angle that this orientation value represents.
pub fn angle(&self) -> Angle {
match *self {
Orientation::Angle0 => Angle::from_degrees(0.0, false),
Orientation::Angle90 => Angle::from_degrees(90.0, false),
Orientation::Angle180 => Angle::from_degrees(180.0, false),
Orientation::Angle270 => Angle::from_degrees(270.0, false),
}
}
}
impl ToCss for Orientation {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
// Should agree with Angle::to_css.
match *self {
Orientation::Angle0 => dest.write_str("0deg"),
Orientation::Angle90 => dest.write_str("90deg"),
Orientation::Angle180 => dest.write_str("180deg"),
Orientation::Angle270 => dest.write_str("270deg"),
}
}
}
/// https://drafts.csswg.org/css-images/#propdef-image-orientation
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq)]
pub enum ImageOrientation {
/// 'from-image'
FromImage,
/// '<angle>' | '<angle>? flip'
AngleWithFlipped(Orientation, bool),
}
impl ImageOrientation {
#[allow(missing_docs)]
pub fn zero() -> Self {
ImageOrientation::AngleWithFlipped(Orientation::Angle0, false)
}
}
impl ToCss for ImageOrientation {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
match *self {
ImageOrientation::FromImage => dest.write_str("from-image"),
ImageOrientation::AngleWithFlipped(angle, flipped) => {
angle.to_css(dest)?;
if flipped {
dest.write_str(" flip")?;
}
Ok(())
},
}
}
}

View file

@ -52,7 +52,6 @@ pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset};
pub use self::effects::{BoxShadow, Filter, SimpleShadow};
pub use self::flex::FlexBasis;
pub use self::image::{Gradient, GradientItem, Image, ImageLayer, LineDirection, MozImageRect};
pub use self::inherited_box::{ImageOrientation, Orientation};
#[cfg(feature = "gecko")]
pub use self::gecko::ScrollSnapPoint;
pub use self::rect::LengthOrNumberRect;
@ -99,7 +98,6 @@ pub mod font;
#[cfg(feature = "gecko")]
pub mod gecko;
pub mod image;
pub mod inherited_box;
pub mod length;
pub mod list;
pub mod outline;

View file

@ -9,6 +9,7 @@ use values::RGBA;
use values::computed::{LengthOrPercentage, NonNegativeLength};
use values::computed::{NonNegativeLengthOrPercentage, NonNegativeNumber, Number};
use values::computed::Opacity;
use values::computed::color::Color;
use values::computed::url::ComputedUrl;
use values::generics::svg as generic;
@ -17,9 +18,9 @@ pub use values::specified::SVGPaintOrder;
pub use values::specified::MozContextProperties;
/// Computed SVG Paint value
pub type SVGPaint = generic::SVGPaint<RGBA, ComputedUrl>;
pub type SVGPaint = generic::SVGPaint<Color, ComputedUrl>;
/// Computed SVG Paint Kind value
pub type SVGPaintKind = generic::SVGPaintKind<RGBA, ComputedUrl>;
pub type SVGPaintKind = generic::SVGPaintKind<Color, ComputedUrl>;
impl Default for SVGPaint {
fn default() -> Self {
@ -33,7 +34,7 @@ impl Default for SVGPaint {
impl SVGPaint {
/// Opaque black color
pub fn black() -> Self {
let rgba = RGBA::from_floats(0., 0., 0., 1.);
let rgba = RGBA::from_floats(0., 0., 0., 1.).into();
SVGPaint {
kind: generic::SVGPaintKind::Color(rgba),
fallback: None,

View file

@ -6,7 +6,7 @@
/// Ratios representing the contribution of color and currentcolor to
/// the final color value.
#[derive(Animate, Clone, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedValue)]
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToAnimatedValue)]
pub struct ComplexColorRatios {
/// Numeric color contribution.
pub bg: f32,

View file

@ -1,158 +0,0 @@
/* 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/. */
//! Specified values for inherited box
use cssparser::Parser;
use parser::{Parse, ParserContext};
use std::f64::consts::PI;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
use values::computed;
use values::computed::{Context, Orientation, ToComputedValue};
use values::specified::Angle;
/// The specified value of the `image-orientation` property.
/// https://drafts.csswg.org/css-images/#propdef-image-orientation
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo)]
pub struct ImageOrientation {
/// The angle specified, if any
pub angle: Option<Angle>,
/// Whether or not "flip" was specified
#[value_info(other_values = "flip,from-image")]
pub flipped: bool,
}
impl ToCss for ImageOrientation {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
if let Some(angle) = self.angle {
angle.to_css(dest)?;
if self.flipped {
dest.write_str(" flip")
} else {
Ok(())
}
} else {
if self.flipped {
dest.write_str("flip")
} else {
dest.write_str("from-image")
}
}
}
}
const TWO_PI: f64 = 2.0 * PI;
// According to CSS Content Module Level 3:
// The computed value of the property is calculated by rounding the specified angle
// to the nearest quarter-turn, rounding away from 0, then moduloing the value by 1 turn.
// This mirrors the Gecko implementation in
// nsStyleImageOrientation::CreateAsAngleAndFlip.
#[inline]
fn orientation_of_angle(angle: &computed::Angle) -> Orientation {
// Note that `angle` can be negative.
let mut rounded_angle = angle.radians64() % TWO_PI;
if rounded_angle < 0.0 {
// This computation introduces rounding error. Gecko previously
// didn't handle the negative case correctly; by branching we can
// match Gecko's behavior when it was correct.
rounded_angle += TWO_PI;
}
if rounded_angle < 0.25 * PI {
return Orientation::Angle0;
}
if rounded_angle < 0.75 * PI {
return Orientation::Angle90;
}
if rounded_angle < 1.25 * PI {
return Orientation::Angle180;
}
if rounded_angle < 1.75 * PI {
return Orientation::Angle270;
}
Orientation::Angle0
}
impl ToComputedValue for ImageOrientation {
type ComputedValue = computed::ImageOrientation;
#[inline]
fn to_computed_value(&self, context: &Context) -> computed::ImageOrientation {
if let Some(ref angle) = self.angle {
let angle = angle.to_computed_value(context);
let orientation = orientation_of_angle(&angle);
computed::ImageOrientation::AngleWithFlipped(orientation, self.flipped)
} else {
if self.flipped {
computed::ImageOrientation::zero()
} else {
computed::ImageOrientation::FromImage
}
}
}
#[inline]
fn from_computed_value(computed: &computed::ImageOrientation) -> Self {
match *computed {
computed::ImageOrientation::FromImage => ImageOrientation {
angle: None,
flipped: false,
},
computed::ImageOrientation::AngleWithFlipped(ref orientation, flipped) => {
ImageOrientation {
angle: Some(orientation.angle()),
flipped: flipped,
}
},
}
}
}
impl Parse for ImageOrientation {
// from-image | <angle> | [<angle>? flip]
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
if input
.try(|input| input.expect_ident_matching("from-image"))
.is_ok()
{
// Handle from-image
Ok(ImageOrientation {
angle: None,
flipped: false,
})
} else if input
.try(|input| input.expect_ident_matching("flip"))
.is_ok()
{
// Handle flip
Ok(ImageOrientation {
angle: Some(Angle::zero()),
flipped: true,
})
} else {
// Handle <angle> | <angle> flip
let angle = input.try(|input| Angle::parse(context, input)).ok();
if angle.is_none() {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
let flipped = input
.try(|input| input.expect_ident_matching("flip"))
.is_ok();
Ok(ImageOrientation {
angle: angle,
flipped: flipped,
})
}
}
}

View file

@ -49,7 +49,6 @@ pub use self::flex::FlexBasis;
pub use self::gecko::ScrollSnapPoint;
pub use self::image::{ColorStop, EndingShape as GradientEndingShape, Gradient};
pub use self::image::{GradientItem, GradientKind, Image, ImageLayer, MozImageRect};
pub use self::inherited_box::ImageOrientation;
pub use self::length::{AbsoluteLength, CalcLengthOrPercentage, CharacterWidth};
pub use self::length::{FontRelativeLength, Length, LengthOrNumber};
pub use self::length::{LengthOrPercentage, LengthOrPercentageOrAuto};
@ -99,7 +98,6 @@ pub mod font;
pub mod gecko;
pub mod grid;
pub mod image;
pub mod inherited_box;
pub mod length;
pub mod list;
pub mod outline;

View file

@ -13,14 +13,14 @@ use values::CustomIdent;
use values::generics::svg as generic;
use values::specified::{LengthOrPercentage, NonNegativeLengthOrPercentage, NonNegativeNumber};
use values::specified::{Number, Opacity};
use values::specified::color::RGBAColor;
use values::specified::color::Color;
use values::specified::url::SpecifiedUrl;
/// Specified SVG Paint value
pub type SVGPaint = generic::SVGPaint<RGBAColor, SpecifiedUrl>;
pub type SVGPaint = generic::SVGPaint<Color, SpecifiedUrl>;
/// Specified SVG Paint Kind value
pub type SVGPaintKind = generic::SVGPaintKind<RGBAColor, SpecifiedUrl>;
pub type SVGPaintKind = generic::SVGPaintKind<Color, SpecifiedUrl>;
#[cfg(feature = "gecko")]
fn is_context_value_enabled() -> bool {

View file

@ -1,259 +0,0 @@
/* 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/. */
use cssparser::SourceLocation;
use servo_arc::Arc;
use style::properties::{LonghandId, LonghandIdSet, PropertyDeclaration, PropertyDeclarationBlock, Importance};
use style::properties::DeclarationSource;
use style::shared_lock::SharedRwLock;
use style::stylesheets::keyframes_rule::{Keyframe, KeyframesAnimation, KeyframePercentage, KeyframeSelector};
use style::stylesheets::keyframes_rule::{KeyframesStep, KeyframesStepValue};
use style::values::specified::{LengthOrPercentageOrAuto, NoCalcLength};
macro_rules! longhand_set {
($($word:ident),+) => {{
let mut set = LonghandIdSet::new();
$(
set.insert(LonghandId::$word);
)+
set
}}
}
#[test]
fn test_empty_keyframe() {
let shared_lock = SharedRwLock::new();
let keyframes = vec![];
let animation = KeyframesAnimation::from_keyframes(&keyframes,
/* vendor_prefix = */ None,
&shared_lock.read());
let expected = KeyframesAnimation {
steps: vec![],
properties_changed: LonghandIdSet::new(),
vendor_prefix: None,
};
assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected));
}
#[test]
fn test_no_property_in_keyframe() {
let shared_lock = SharedRwLock::new();
let dummy_location = SourceLocation { line: 0, column: 0 };
let keyframes = vec![
Arc::new(shared_lock.wrap(Keyframe {
selector: KeyframeSelector::new_for_unit_testing(vec![KeyframePercentage::new(1.)]),
block: Arc::new(shared_lock.wrap(PropertyDeclarationBlock::new())),
source_location: dummy_location,
})),
];
let animation = KeyframesAnimation::from_keyframes(&keyframes,
/* vendor_prefix = */ None,
&shared_lock.read());
let expected = KeyframesAnimation {
steps: vec![],
properties_changed: LonghandIdSet::new(),
vendor_prefix: None,
};
assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected));
}
#[test]
fn test_missing_property_in_initial_keyframe() {
let shared_lock = SharedRwLock::new();
let declarations_on_initial_keyframe =
Arc::new(shared_lock.wrap(PropertyDeclarationBlock::with_one(
PropertyDeclaration::Width(
LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(20f32))),
Importance::Normal
)));
let declarations_on_final_keyframe =
Arc::new(shared_lock.wrap({
let mut block = PropertyDeclarationBlock::new();
block.push(
PropertyDeclaration::Width(
LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(20f32))),
Importance::Normal,
DeclarationSource::Parsing,
);
block.push(
PropertyDeclaration::Height(
LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(20f32))),
Importance::Normal,
DeclarationSource::Parsing,
);
block
}));
let dummy_location = SourceLocation { line: 0, column: 0 };
let keyframes = vec![
Arc::new(shared_lock.wrap(Keyframe {
selector: KeyframeSelector::new_for_unit_testing(vec![KeyframePercentage::new(0.)]),
block: declarations_on_initial_keyframe.clone(),
source_location: dummy_location,
})),
Arc::new(shared_lock.wrap(Keyframe {
selector: KeyframeSelector::new_for_unit_testing(vec![KeyframePercentage::new(1.)]),
block: declarations_on_final_keyframe.clone(),
source_location: dummy_location,
})),
];
let animation = KeyframesAnimation::from_keyframes(&keyframes,
/* vendor_prefix = */ None,
&shared_lock.read());
let expected = KeyframesAnimation {
steps: vec![
KeyframesStep {
start_percentage: KeyframePercentage(0.),
value: KeyframesStepValue::Declarations { block: declarations_on_initial_keyframe },
declared_timing_function: false,
},
KeyframesStep {
start_percentage: KeyframePercentage(1.),
value: KeyframesStepValue::Declarations { block: declarations_on_final_keyframe },
declared_timing_function: false,
},
],
properties_changed: longhand_set!(Width, Height),
vendor_prefix: None,
};
assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected));
}
#[test]
fn test_missing_property_in_final_keyframe() {
let shared_lock = SharedRwLock::new();
let declarations_on_initial_keyframe =
Arc::new(shared_lock.wrap({
let mut block = PropertyDeclarationBlock::new();
block.push(
PropertyDeclaration::Width(
LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(20f32))),
Importance::Normal,
DeclarationSource::Parsing,
);
block.push(
PropertyDeclaration::Height(
LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(20f32))),
Importance::Normal,
DeclarationSource::Parsing,
);
block
}));
let declarations_on_final_keyframe =
Arc::new(shared_lock.wrap(PropertyDeclarationBlock::with_one(
PropertyDeclaration::Height(
LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(20f32))),
Importance::Normal,
)));
let dummy_location = SourceLocation { line: 0, column: 0 };
let keyframes = vec![
Arc::new(shared_lock.wrap(Keyframe {
selector: KeyframeSelector::new_for_unit_testing(vec![KeyframePercentage::new(0.)]),
block: declarations_on_initial_keyframe.clone(),
source_location: dummy_location,
})),
Arc::new(shared_lock.wrap(Keyframe {
selector: KeyframeSelector::new_for_unit_testing(vec![KeyframePercentage::new(1.)]),
block: declarations_on_final_keyframe.clone(),
source_location: dummy_location,
})),
];
let animation = KeyframesAnimation::from_keyframes(&keyframes,
/* vendor_prefix = */ None,
&shared_lock.read());
let expected = KeyframesAnimation {
steps: vec![
KeyframesStep {
start_percentage: KeyframePercentage(0.),
value: KeyframesStepValue::Declarations { block: declarations_on_initial_keyframe },
declared_timing_function: false,
},
KeyframesStep {
start_percentage: KeyframePercentage(1.),
value: KeyframesStepValue::Declarations { block: declarations_on_final_keyframe },
declared_timing_function: false,
},
],
properties_changed: longhand_set!(Width, Height),
vendor_prefix: None,
};
assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected));
}
#[test]
fn test_missing_keyframe_in_both_of_initial_and_final_keyframe() {
let shared_lock = SharedRwLock::new();
let declarations =
Arc::new(shared_lock.wrap({
let mut block = PropertyDeclarationBlock::new();
block.push(
PropertyDeclaration::Width(
LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(20f32))),
Importance::Normal,
DeclarationSource::Parsing,
);
block.push(
PropertyDeclaration::Height(
LengthOrPercentageOrAuto::Length(NoCalcLength::from_px(20f32))),
Importance::Normal,
DeclarationSource::Parsing,
);
block
}));
let dummy_location = SourceLocation { line: 0, column: 0 };
let keyframes = vec![
Arc::new(shared_lock.wrap(Keyframe {
selector: KeyframeSelector::new_for_unit_testing(vec![KeyframePercentage::new(0.)]),
block: Arc::new(shared_lock.wrap(PropertyDeclarationBlock::new())),
source_location: dummy_location,
})),
Arc::new(shared_lock.wrap(Keyframe {
selector: KeyframeSelector::new_for_unit_testing(vec![KeyframePercentage::new(0.5)]),
block: declarations.clone(),
source_location: dummy_location,
})),
];
let animation = KeyframesAnimation::from_keyframes(&keyframes,
/* vendor_prefix = */ None,
&shared_lock.read());
let expected = KeyframesAnimation {
steps: vec![
KeyframesStep {
start_percentage: KeyframePercentage(0.),
value: KeyframesStepValue::Declarations {
block: Arc::new(shared_lock.wrap(
// XXX: Should we use ComputedValues in this case?
PropertyDeclarationBlock::new()
))
},
declared_timing_function: false,
},
KeyframesStep {
start_percentage: KeyframePercentage(0.5),
value: KeyframesStepValue::Declarations { block: declarations },
declared_timing_function: false,
},
KeyframesStep {
start_percentage: KeyframePercentage(1.),
value: KeyframesStepValue::ComputedValues,
declared_timing_function: false,
}
],
properties_changed: longhand_set!(Width, Height),
vendor_prefix: None,
};
assert_eq!(format!("{:#?}", animation), format!("{:#?}", expected));
}

View file

@ -25,7 +25,6 @@ extern crate test;
mod animated_properties;
mod attr;
mod custom_properties;
mod keyframes;
mod logical_geometry;
mod parsing;
mod properties;

View file

@ -17,7 +17,7 @@ use std::sync::atomic::AtomicBool;
use style::context::QuirksMode;
use style::error_reporting::{ParseErrorReporter, ContextualParseError};
use style::media_queries::MediaList;
use style::properties::{CSSWideKeyword, CustomDeclaration, DeclarationSource};
use style::properties::{CSSWideKeyword, CustomDeclaration, DeclarationPushMode};
use style::properties::{DeclaredValueOwned, Importance};
use style::properties::{PropertyDeclaration, PropertyDeclarationBlock};
use style::properties::longhands::{self, animation_timing_function};
@ -34,7 +34,7 @@ pub fn block_from<I>(iterable: I) -> PropertyDeclarationBlock
where I: IntoIterator<Item=(PropertyDeclaration, Importance)> {
let mut block = PropertyDeclarationBlock::new();
for (d, i) in iterable {
block.push(d, i, DeclarationSource::CssOm);
block.push(d, i, DeclarationPushMode::Append);
}
block
}