mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Auto merge of #20900 - emilio:gecko-sync, r=emilio
style: sync changes from mozilla-central. See each individual commit for details.
This commit is contained in:
commit
2434c2bef1
22 changed files with 456 additions and 351 deletions
|
@ -65,6 +65,7 @@ reset
|
|||
right
|
||||
sans-serif
|
||||
screen
|
||||
scroll-position
|
||||
search
|
||||
select
|
||||
serif
|
||||
|
|
|
@ -757,14 +757,16 @@ impl LayoutThread {
|
|||
Msg::RegisterPaint(name, mut properties, painter) => {
|
||||
debug!("Registering the painter");
|
||||
let properties = properties.drain(..)
|
||||
.filter_map(|name| PropertyId::parse(&*name)
|
||||
.ok().map(|id| (name.clone(), id)))
|
||||
.filter(|&(_, ref id)| id.as_shorthand().is_err())
|
||||
.filter_map(|name| {
|
||||
let id = PropertyId::parse_enabled_for_all_content(&*name).ok()?;
|
||||
Some((name.clone(), id))
|
||||
})
|
||||
.filter(|&(_, ref id)| !id.is_shorthand())
|
||||
.collect();
|
||||
let registered_painter = RegisteredPainterImpl {
|
||||
name: name.clone(),
|
||||
properties: properties,
|
||||
painter: painter,
|
||||
properties,
|
||||
painter,
|
||||
};
|
||||
self.registered_painters.0.insert(name, registered_painter);
|
||||
},
|
||||
|
|
|
@ -312,22 +312,18 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
|
|||
|
||||
// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue
|
||||
fn GetPropertyValue(&self, property: DOMString) -> DOMString {
|
||||
let id = if let Ok(id) = PropertyId::parse(&property) {
|
||||
id
|
||||
} else {
|
||||
// Unkwown property
|
||||
return DOMString::new()
|
||||
let id = match PropertyId::parse_enabled_for_all_content(&property) {
|
||||
Ok(id) => id,
|
||||
Err(..) => return DOMString::new(),
|
||||
};
|
||||
self.get_property_value(id)
|
||||
}
|
||||
|
||||
// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority
|
||||
fn GetPropertyPriority(&self, property: DOMString) -> DOMString {
|
||||
let id = if let Ok(id) = PropertyId::parse(&property) {
|
||||
id
|
||||
} else {
|
||||
// Unkwown property
|
||||
return DOMString::new()
|
||||
let id = match PropertyId::parse_enabled_for_all_content(&property) {
|
||||
Ok(id) => id,
|
||||
Err(..) => return DOMString::new(),
|
||||
};
|
||||
|
||||
self.owner.with_block(|pdb| {
|
||||
|
@ -347,11 +343,9 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
|
|||
priority: DOMString)
|
||||
-> ErrorResult {
|
||||
// Step 3
|
||||
let id = if let Ok(id) = PropertyId::parse(&property) {
|
||||
id
|
||||
} else {
|
||||
// Unknown property
|
||||
return Ok(())
|
||||
let id = match PropertyId::parse_enabled_for_all_content(&property) {
|
||||
Ok(id) => id,
|
||||
Err(..) => return Ok(()),
|
||||
};
|
||||
self.set_property(id, value, priority)
|
||||
}
|
||||
|
@ -364,7 +358,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
|
|||
}
|
||||
|
||||
// Step 2 & 3
|
||||
let id = match PropertyId::parse(&property) {
|
||||
let id = match PropertyId::parse_enabled_for_all_content(&property) {
|
||||
Ok(id) => id,
|
||||
Err(..) => return Ok(()), // Unkwown property
|
||||
};
|
||||
|
@ -396,11 +390,9 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
|
|||
return Err(Error::NoModificationAllowed);
|
||||
}
|
||||
|
||||
let id = if let Ok(id) = PropertyId::parse(&property) {
|
||||
id
|
||||
} else {
|
||||
// Unkwown property, cannot be there to remove.
|
||||
return Ok(DOMString::new())
|
||||
let id = match PropertyId::parse_enabled_for_all_content(&property) {
|
||||
Ok(id) => id,
|
||||
Err(..) => return Ok(DOMString::new()),
|
||||
};
|
||||
|
||||
let mut string = String::new();
|
||||
|
|
|
@ -466,12 +466,6 @@ pub trait TElement:
|
|||
&[]
|
||||
}
|
||||
|
||||
/// For a given NAC element, return the closest non-NAC ancestor, which is
|
||||
/// guaranteed to exist.
|
||||
fn closest_non_native_anonymous_ancestor(&self) -> Option<Self> {
|
||||
unreachable!("Servo doesn't know about NAC");
|
||||
}
|
||||
|
||||
/// Get this element's style attribute.
|
||||
fn style_attribute(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>;
|
||||
|
||||
|
@ -657,9 +651,8 @@ pub trait TElement:
|
|||
false
|
||||
}
|
||||
|
||||
/// Returns true if this element is native anonymous (only Gecko has native
|
||||
/// anonymous content).
|
||||
fn is_native_anonymous(&self) -> bool {
|
||||
/// Returns true if this element is in a native anonymous subtree.
|
||||
fn is_in_native_anonymous_subtree(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
|
@ -794,7 +787,7 @@ pub trait TElement:
|
|||
/// element.
|
||||
fn rule_hash_target(&self) -> Self {
|
||||
if self.implemented_pseudo_element().is_some() {
|
||||
self.closest_non_native_anonymous_ancestor()
|
||||
self.pseudo_element_originating_element()
|
||||
.expect("Trying to collect rules for a detached pseudo-element")
|
||||
} else {
|
||||
*self
|
||||
|
|
|
@ -588,6 +588,20 @@ impl<'le> fmt::Debug for GeckoElement<'le> {
|
|||
}
|
||||
|
||||
impl<'le> GeckoElement<'le> {
|
||||
#[inline]
|
||||
fn closest_anon_subtree_root_parent(&self) -> Option<Self> {
|
||||
debug_assert!(self.is_in_native_anonymous_subtree());
|
||||
let mut current = *self;
|
||||
|
||||
loop {
|
||||
if current.is_root_of_native_anonymous_subtree() {
|
||||
return current.traversal_parent();
|
||||
}
|
||||
|
||||
current = current.traversal_parent()?;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn may_have_anonymous_children(&self) -> bool {
|
||||
self.as_node()
|
||||
|
@ -813,13 +827,6 @@ impl<'le> GeckoElement<'le> {
|
|||
return self.flags() & (NODE_IS_NATIVE_ANONYMOUS_ROOT as u32) != 0;
|
||||
}
|
||||
|
||||
/// This logic is duplicated in Gecko's nsINode::IsInNativeAnonymousSubtree.
|
||||
#[inline]
|
||||
fn is_in_native_anonymous_subtree(&self) -> bool {
|
||||
use gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE;
|
||||
self.flags() & (NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE as u32) != 0
|
||||
}
|
||||
|
||||
/// This logic is duplicated in Gecko's nsIContent::IsInAnonymousSubtree.
|
||||
#[inline]
|
||||
fn is_in_anonymous_subtree(&self) -> bool {
|
||||
|
@ -1038,13 +1045,11 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
type TraversalChildrenIterator = GeckoChildrenIterator<'le>;
|
||||
|
||||
fn inheritance_parent(&self) -> Option<Self> {
|
||||
if self.is_native_anonymous() {
|
||||
self.closest_non_native_anonymous_ancestor()
|
||||
} else {
|
||||
self.as_node()
|
||||
.flattened_tree_parent()
|
||||
.and_then(|n| n.as_element())
|
||||
if self.implemented_pseudo_element().is_some() {
|
||||
return self.pseudo_element_originating_element()
|
||||
}
|
||||
|
||||
self.as_node().flattened_tree_parent().and_then(|n| n.as_element())
|
||||
}
|
||||
|
||||
fn traversal_children(&self) -> LayoutIterator<GeckoChildrenIterator<'le>> {
|
||||
|
@ -1174,19 +1179,6 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
unsafe { bindings::Gecko_DestroyAnonymousContentList(array) };
|
||||
}
|
||||
|
||||
fn closest_non_native_anonymous_ancestor(&self) -> Option<Self> {
|
||||
debug_assert!(self.is_native_anonymous());
|
||||
let mut parent = self.traversal_parent()?;
|
||||
|
||||
loop {
|
||||
if !parent.is_native_anonymous() {
|
||||
return Some(parent);
|
||||
}
|
||||
|
||||
parent = parent.traversal_parent()?;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_node(&self) -> Self::ConcreteNode {
|
||||
unsafe { GeckoNode(&*(self.0 as *const _ as *const RawGeckoNode)) }
|
||||
|
@ -1223,16 +1215,9 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
unsafe {
|
||||
let slots = self.extended_slots()?;
|
||||
|
||||
let base_declaration: &structs::DeclarationBlock =
|
||||
let declaration: &structs::DeclarationBlock =
|
||||
slots.mSMILOverrideStyleDeclaration.mRawPtr.as_ref()?;
|
||||
|
||||
let declaration: &structs::ServoDeclarationBlock = mem::transmute(base_declaration);
|
||||
|
||||
debug_assert_eq!(
|
||||
&declaration._base as *const structs::DeclarationBlock,
|
||||
base_declaration as *const structs::DeclarationBlock
|
||||
);
|
||||
|
||||
let raw: &structs::RawServoDeclarationBlock = declaration.mRaw.mRawPtr.as_ref()?;
|
||||
|
||||
Some(
|
||||
|
@ -1354,10 +1339,11 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
self.state().intersects(ElementState::IN_VISITED_STATE)
|
||||
}
|
||||
|
||||
/// This logic is duplicated in Gecko's nsINode::IsInNativeAnonymousSubtree.
|
||||
#[inline]
|
||||
fn is_native_anonymous(&self) -> bool {
|
||||
use gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS;
|
||||
self.flags() & (NODE_IS_NATIVE_ANONYMOUS as u32) != 0
|
||||
fn is_in_native_anonymous_subtree(&self) -> bool {
|
||||
use gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE;
|
||||
self.flags() & (NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE as u32) != 0
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -1366,7 +1352,7 @@ impl<'le> TElement for GeckoElement<'le> {
|
|||
}
|
||||
|
||||
fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
|
||||
if !self.is_native_anonymous() {
|
||||
if !self.is_in_native_anonymous_subtree() {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
@ -1915,7 +1901,22 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
|||
#[inline]
|
||||
fn pseudo_element_originating_element(&self) -> Option<Self> {
|
||||
debug_assert!(self.implemented_pseudo_element().is_some());
|
||||
self.closest_non_native_anonymous_ancestor()
|
||||
let parent = self.closest_anon_subtree_root_parent()?;
|
||||
|
||||
// FIXME(emilio): Special-case for <input type="number">s
|
||||
// pseudo-elements, which are nested NAC. Probably nsNumberControlFrame
|
||||
// should instead inherit from nsTextControlFrame, and then this could
|
||||
// go away.
|
||||
if let Some(PseudoElement::MozNumberText) = parent.implemented_pseudo_element() {
|
||||
debug_assert_eq!(
|
||||
self.implemented_pseudo_element().unwrap(),
|
||||
PseudoElement::Placeholder,
|
||||
"You added a new pseudo, do you really want this?"
|
||||
);
|
||||
return parent.closest_anon_subtree_root_parent();
|
||||
}
|
||||
|
||||
Some(parent)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -678,7 +678,7 @@ pub trait MatchMethods: TElement {
|
|||
let new_primary_style = data.styles.primary.as_ref().unwrap();
|
||||
|
||||
let mut cascade_requirement = ChildCascadeRequirement::CanSkipCascade;
|
||||
if self.is_root() && !self.is_native_anonymous() {
|
||||
if self.is_root() && !self.is_in_native_anonymous_subtree() {
|
||||
let device = context.shared.stylist.device();
|
||||
let new_font_size = new_primary_style.get_font().clone_font_size();
|
||||
|
||||
|
|
|
@ -1158,7 +1158,7 @@ impl<'a, 'b, 'i> DeclarationParser<'i> for PropertyDeclarationParser<'a, 'b> {
|
|||
name: CowRcStr<'i>,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<Importance, ParseError<'i>> {
|
||||
let id = match PropertyId::parse(&name) {
|
||||
let id = match PropertyId::parse(&name, self.context) {
|
||||
Ok(id) => id,
|
||||
Err(..) => {
|
||||
return Err(input.new_custom_error(if is_non_mozilla_vendor_identifier(&name) {
|
||||
|
|
|
@ -3515,77 +3515,27 @@ fn static_assert() {
|
|||
|
||||
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) => {
|
||||
T::AnimateableFeatures { features, bits } => {
|
||||
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()) {
|
||||
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);
|
||||
}
|
||||
},
|
||||
}
|
||||
Gecko_AppendWillChange(&mut self.gecko, feature.0.as_ptr())
|
||||
}
|
||||
}
|
||||
|
||||
self.gecko.mWillChangeBitField = bits.bits();
|
||||
},
|
||||
T::Auto => {
|
||||
unsafe {
|
||||
Gecko_ClearWillChange(&mut self.gecko, 0);
|
||||
}
|
||||
self.gecko.mWillChangeBitField = 0;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -3607,6 +3557,7 @@ fn static_assert() {
|
|||
use properties::longhands::will_change::computed_value::T;
|
||||
use gecko_bindings::structs::nsAtom;
|
||||
use values::CustomIdent;
|
||||
use values::specified::box_::WillChangeBits;
|
||||
|
||||
if self.gecko.mWillChange.len() == 0 {
|
||||
return T::Auto
|
||||
|
@ -3618,7 +3569,10 @@ fn static_assert() {
|
|||
}
|
||||
}).collect();
|
||||
|
||||
T::AnimateableFeatures(custom_idents.into_boxed_slice())
|
||||
T::AnimateableFeatures {
|
||||
features: custom_idents.into_boxed_slice(),
|
||||
bits: WillChangeBits::from_bits_truncate(self.gecko.mWillChangeBitField),
|
||||
}
|
||||
}
|
||||
|
||||
<% impl_shape_source("shape_outside", "mShapeOutside") %>
|
||||
|
@ -3626,10 +3580,13 @@ fn static_assert() {
|
|||
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_CONTENT;
|
||||
use gecko_bindings::structs::NS_STYLE_CONTAIN_SIZE;
|
||||
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 gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT_BITS;
|
||||
use properties::longhands::contain::SpecifiedValue;
|
||||
|
||||
if v.is_empty() {
|
||||
|
@ -3641,6 +3598,10 @@ fn static_assert() {
|
|||
self.gecko.mContain = (NS_STYLE_CONTAIN_STRICT | NS_STYLE_CONTAIN_ALL_BITS) as u8;
|
||||
return;
|
||||
}
|
||||
if v.contains(SpecifiedValue::CONTENT) {
|
||||
self.gecko.mContain = (NS_STYLE_CONTAIN_CONTENT | NS_STYLE_CONTAIN_CONTENT_BITS) as u8;
|
||||
return;
|
||||
}
|
||||
|
||||
let mut bitfield = 0;
|
||||
if v.contains(SpecifiedValue::LAYOUT) {
|
||||
|
@ -3652,36 +3613,57 @@ fn static_assert() {
|
|||
if v.contains(SpecifiedValue::PAINT) {
|
||||
bitfield |= NS_STYLE_CONTAIN_PAINT;
|
||||
}
|
||||
if v.contains(SpecifiedValue::SIZE) {
|
||||
bitfield |= NS_STYLE_CONTAIN_SIZE;
|
||||
}
|
||||
|
||||
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_CONTENT;
|
||||
use gecko_bindings::structs::NS_STYLE_CONTAIN_SIZE;
|
||||
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 gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT_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 {
|
||||
if gecko_flags & (NS_STYLE_CONTAIN_STRICT as u8) != 0 {
|
||||
debug_assert_eq!(
|
||||
gecko_flags & (NS_STYLE_CONTAIN_ALL_BITS as u8),
|
||||
NS_STYLE_CONTAIN_ALL_BITS as u8,
|
||||
"When strict is specified, ALL_BITS should be specified as well"
|
||||
);
|
||||
servo_flags.insert(SpecifiedValue::STRICT | SpecifiedValue::STRICT_BITS);
|
||||
return servo_flags;
|
||||
}
|
||||
|
||||
if gecko_flags & (NS_STYLE_CONTAIN_CONTENT as u8) != 0 {
|
||||
debug_assert_eq!(
|
||||
gecko_flags & (NS_STYLE_CONTAIN_CONTENT_BITS as u8),
|
||||
NS_STYLE_CONTAIN_CONTENT_BITS as u8,
|
||||
"When content is specified, CONTENT_BITS should be specified as well"
|
||||
);
|
||||
servo_flags.insert(SpecifiedValue::CONTENT | SpecifiedValue::CONTENT_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{
|
||||
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);
|
||||
}
|
||||
if gecko_flags & (NS_STYLE_CONTAIN_SIZE as u8) != 0 {
|
||||
servo_flags.insert(SpecifiedValue::SIZE);
|
||||
}
|
||||
|
||||
return servo_flags;
|
||||
}
|
||||
|
|
|
@ -2501,82 +2501,83 @@ impl Animate for ComputedScale {
|
|||
/// <https://drafts.csswg.org/css-transforms/#interpolation-of-transforms>
|
||||
impl Animate for ComputedTransform {
|
||||
#[inline]
|
||||
fn animate(
|
||||
&self,
|
||||
other_: &Self,
|
||||
procedure: Procedure,
|
||||
) -> Result<Self, ()> {
|
||||
|
||||
let animate_equal_lists = |this: &[ComputedTransformOperation],
|
||||
other: &[ComputedTransformOperation]|
|
||||
-> Result<ComputedTransform, ()> {
|
||||
Ok(Transform(this.iter().zip(other)
|
||||
.map(|(this, other)| this.animate(other, procedure))
|
||||
.collect::<Result<Vec<_>, _>>()?))
|
||||
// If we can't animate for a pair of matched transform lists
|
||||
// this means we have at least one undecomposable matrix,
|
||||
// so we should bubble out Err here, and let the caller do
|
||||
// the fallback procedure.
|
||||
};
|
||||
if self.0.is_empty() && other_.0.is_empty() {
|
||||
return Ok(Transform(vec![]));
|
||||
}
|
||||
|
||||
|
||||
let this = &self.0;
|
||||
let other = &other_.0;
|
||||
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||
use std::borrow::Cow;
|
||||
|
||||
if procedure == Procedure::Add {
|
||||
let result = this.iter().chain(other).cloned().collect::<Vec<_>>();
|
||||
let result = self.0.iter().chain(&other.0).cloned().collect::<Vec<_>>();
|
||||
return Ok(Transform(result));
|
||||
}
|
||||
|
||||
// https://drafts.csswg.org/css-transforms-1/#transform-transform-neutral-extend-animation
|
||||
fn match_operations_if_possible<'a>(
|
||||
this: &mut Cow<'a, Vec<ComputedTransformOperation>>,
|
||||
other: &mut Cow<'a, Vec<ComputedTransformOperation>>,
|
||||
) -> bool {
|
||||
if !this.iter().zip(other.iter()).all(|(this, other)| is_matched_operation(this, other)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// For matched transform lists.
|
||||
{
|
||||
if this.len() == other.len() {
|
||||
let is_matched_transforms = this.iter().zip(other).all(|(this, other)| {
|
||||
is_matched_operation(this, other)
|
||||
});
|
||||
|
||||
if is_matched_transforms {
|
||||
return animate_equal_lists(this, other);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
let (shorter, longer) =
|
||||
if this.len() < other.len() {
|
||||
(this.to_mut(), other)
|
||||
} else {
|
||||
(other.to_mut(), this)
|
||||
};
|
||||
|
||||
shorter.reserve(longer.len());
|
||||
for op in longer.iter().skip(shorter.len()) {
|
||||
shorter.push(op.to_animated_zero().unwrap());
|
||||
}
|
||||
|
||||
// The resulting operations won't be matched regardless if the
|
||||
// extended component is already InterpolateMatrix /
|
||||
// AccumulateMatrix.
|
||||
//
|
||||
// Otherwise they should be matching operations all the time.
|
||||
let already_mismatched = matches!(
|
||||
longer[0],
|
||||
TransformOperation::InterpolateMatrix { .. } |
|
||||
TransformOperation::AccumulateMatrix { .. }
|
||||
);
|
||||
|
||||
debug_assert_eq!(
|
||||
!already_mismatched,
|
||||
longer.iter().zip(shorter.iter()).all(|(this, other)| is_matched_operation(this, other)),
|
||||
"ToAnimatedZero should generate matched operations"
|
||||
);
|
||||
|
||||
!already_mismatched
|
||||
}
|
||||
|
||||
// For mismatched transform lists.
|
||||
let mut owned_this = this.clone();
|
||||
let mut owned_other = other.clone();
|
||||
let mut this = Cow::Borrowed(&self.0);
|
||||
let mut other = Cow::Borrowed(&other.0);
|
||||
|
||||
if this.is_empty() {
|
||||
let this = other_.to_animated_zero()?.0;
|
||||
if this.iter().zip(other).all(|(this, other)| is_matched_operation(this, other)) {
|
||||
return animate_equal_lists(&this, other)
|
||||
}
|
||||
owned_this = this;
|
||||
}
|
||||
if other.is_empty() {
|
||||
let other = self.to_animated_zero()?.0;
|
||||
if this.iter().zip(&other).all(|(this, other)| is_matched_operation(this, other)) {
|
||||
return animate_equal_lists(this, &other)
|
||||
}
|
||||
owned_other = other;
|
||||
if match_operations_if_possible(&mut this, &mut other) {
|
||||
return Ok(Transform(
|
||||
this.iter().zip(other.iter())
|
||||
.map(|(this, other)| this.animate(other, procedure))
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
));
|
||||
}
|
||||
|
||||
match procedure {
|
||||
Procedure::Add => Err(()),
|
||||
Procedure::Interpolate { progress } => {
|
||||
Ok(Transform(vec![TransformOperation::InterpolateMatrix {
|
||||
from_list: Transform(owned_this),
|
||||
to_list: Transform(owned_other),
|
||||
from_list: Transform(this.into_owned()),
|
||||
to_list: Transform(other.into_owned()),
|
||||
progress: Percentage(progress as f32),
|
||||
}]))
|
||||
},
|
||||
Procedure::Accumulate { count } => {
|
||||
Ok(Transform(vec![TransformOperation::AccumulateMatrix {
|
||||
from_list: Transform(owned_this),
|
||||
to_list: Transform(owned_other),
|
||||
from_list: Transform(this.into_owned()),
|
||||
to_list: Transform(other.into_owned()),
|
||||
count: cmp::min(count, i32::max_value() as u64) as i32,
|
||||
}]))
|
||||
},
|
||||
|
|
|
@ -593,7 +593,7 @@ ${helpers.predefined_type("contain",
|
|||
"specified::Contain::empty()",
|
||||
animation_value_type="discrete",
|
||||
products="gecko",
|
||||
flags="FIXPOS_CB",
|
||||
flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
|
||||
gecko_pref="layout.css.contain.enabled",
|
||||
spec="https://drafts.csswg.org/css-contain/#contain-property")}
|
||||
|
||||
|
|
|
@ -21,8 +21,8 @@ ${helpers.single_keyword("vector-effect", "none non-scaling-stroke",
|
|||
|
||||
${helpers.predefined_type(
|
||||
"stop-color",
|
||||
"RGBAColor",
|
||||
"RGBA::new(0, 0, 0, 255)",
|
||||
"Color",
|
||||
"RGBA::new(0, 0, 0, 255).into()",
|
||||
products="gecko",
|
||||
animation_value_type="AnimatedRGBA",
|
||||
spec="https://www.w3.org/TR/SVGTiny12/painting.html#StopColorProperty",
|
||||
|
@ -37,10 +37,10 @@ ${helpers.predefined_type("stop-opacity", "Opacity", "1.0",
|
|||
|
||||
${helpers.predefined_type(
|
||||
"flood-color",
|
||||
"RGBAColor",
|
||||
"RGBA::new(0, 0, 0, 255)",
|
||||
"Color",
|
||||
"RGBA::new(0, 0, 0, 255).into()",
|
||||
products="gecko",
|
||||
animation_value_type="AnimatedRGBA",
|
||||
animation_value_type="AnimatedColor",
|
||||
spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty",
|
||||
)}
|
||||
|
||||
|
@ -50,10 +50,10 @@ ${helpers.predefined_type("flood-opacity", "Opacity",
|
|||
|
||||
${helpers.predefined_type(
|
||||
"lighting-color",
|
||||
"RGBAColor",
|
||||
"RGBA::new(255, 255, 255, 255)",
|
||||
"Color",
|
||||
"RGBA::new(255, 255, 255, 255).into()",
|
||||
products="gecko",
|
||||
animation_value_type="AnimatedRGBA",
|
||||
animation_value_type="AnimatedColor",
|
||||
spec="https://www.w3.org/TR/SVG/filters.html#LightingColorProperty",
|
||||
)}
|
||||
|
||||
|
|
|
@ -1422,10 +1422,12 @@ impl UnparsedValue {
|
|||
.and_then(|css| {
|
||||
// As of this writing, only the base URL is used for property
|
||||
// values.
|
||||
//
|
||||
// FIXME(emilio): These bits are slightly fishy.
|
||||
let context = ParserContext::new(
|
||||
Origin::Author,
|
||||
&self.url_data,
|
||||
None,
|
||||
Some(CssRuleType::Style),
|
||||
ParsingMode::DEFAULT,
|
||||
quirks_mode,
|
||||
);
|
||||
|
@ -1595,7 +1597,7 @@ impl PropertyId {
|
|||
/// Returns a given property from the string `s`.
|
||||
///
|
||||
/// Returns Err(()) for unknown non-custom properties.
|
||||
pub fn parse(property_name: &str) -> Result<Self, ()> {
|
||||
fn parse_unchecked(property_name: &str) -> Result<Self, ()> {
|
||||
// FIXME(https://github.com/rust-lang/rust/issues/33156): remove this
|
||||
// enum and use PropertyId when stable Rust allows destructors in
|
||||
// statics.
|
||||
|
@ -1639,13 +1641,39 @@ impl PropertyId {
|
|||
PropertyId::ShorthandAlias(id, alias)
|
||||
},
|
||||
None => {
|
||||
return ::custom_properties::parse_name(property_name).map(|name| {
|
||||
PropertyId::Custom(::custom_properties::Name::from(name))
|
||||
})
|
||||
let name = ::custom_properties::parse_name(property_name)?;
|
||||
PropertyId::Custom(::custom_properties::Name::from(name))
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses a property name, and returns an error if it's unknown or isn't
|
||||
/// enabled for all content.
|
||||
#[inline]
|
||||
pub fn parse_enabled_for_all_content(name: &str) -> Result<Self, ()> {
|
||||
let id = Self::parse_unchecked(name)?;
|
||||
|
||||
if !id.enabled_for_all_content() {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
|
||||
/// Parses a property name, and returns an error if it's unknown or isn't
|
||||
/// allowed in this context.
|
||||
#[inline]
|
||||
pub fn parse(name: &str, context: &ParserContext) -> Result<Self, ()> {
|
||||
let id = Self::parse_unchecked(name)?;
|
||||
|
||||
if !id.allowed_in(context) {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
|
||||
/// Returns a property id from Gecko's nsCSSPropertyID.
|
||||
#[cfg(feature = "gecko")]
|
||||
#[allow(non_upper_case_globals)]
|
||||
|
@ -1972,12 +2000,7 @@ impl PropertyDeclaration {
|
|||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<(), ParseError<'i>> {
|
||||
assert!(declarations.is_empty());
|
||||
|
||||
if !id.allowed_in(context) {
|
||||
return Err(input.new_custom_error(
|
||||
StyleParseErrorKind::UnknownProperty(name)
|
||||
));
|
||||
}
|
||||
debug_assert!(id.allowed_in(context), "{:?}", id);
|
||||
|
||||
let start = input.state();
|
||||
match id {
|
||||
|
|
|
@ -565,7 +565,7 @@ impl<E: TElement> StyleSharingCache<E> {
|
|||
},
|
||||
};
|
||||
|
||||
if element.is_native_anonymous() {
|
||||
if element.is_in_native_anonymous_subtree() {
|
||||
debug!("Failing to insert into the cache: NAC");
|
||||
return;
|
||||
}
|
||||
|
@ -656,7 +656,7 @@ impl<E: TElement> StyleSharingCache<E> {
|
|||
return None;
|
||||
}
|
||||
|
||||
if target.is_native_anonymous() {
|
||||
if target.is_in_native_anonymous_subtree() {
|
||||
debug!("{:?} Cannot share style: NAC", target.element);
|
||||
return None;
|
||||
}
|
||||
|
@ -681,7 +681,7 @@ impl<E: TElement> StyleSharingCache<E> {
|
|||
nth_index_cache: &mut NthIndexCache,
|
||||
selector_flags_map: &mut SelectorFlagsMap<E>,
|
||||
) -> Option<ResolvedElementStyles> {
|
||||
debug_assert!(!target.is_native_anonymous());
|
||||
debug_assert!(!target.is_in_native_anonymous_subtree());
|
||||
|
||||
// Check that we have the same parent, or at least that the parents
|
||||
// share styles and permit sharing across their children. The latter
|
||||
|
|
|
@ -191,7 +191,9 @@ where
|
|||
// Before doing the cascade, check the sharing cache and see if we can
|
||||
// reuse the style via rule node identity.
|
||||
let may_reuse =
|
||||
!self.element.is_native_anonymous() && parent_style.is_some() && inputs.rules.is_some();
|
||||
!self.element.is_in_native_anonymous_subtree() &&
|
||||
parent_style.is_some() &&
|
||||
inputs.rules.is_some();
|
||||
|
||||
if may_reuse {
|
||||
let cached = self.context.thread_local.sharing_cache.lookup_by_rules(
|
||||
|
|
|
@ -620,9 +620,12 @@ impl<'a, 'b, 'i> DeclarationParser<'i> for KeyframeDeclarationParser<'a, 'b> {
|
|||
name: CowRcStr<'i>,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<(), ParseError<'i>> {
|
||||
let id = PropertyId::parse(&name).map_err(|()| {
|
||||
input.new_custom_error(StyleParseErrorKind::UnknownProperty(name.clone()))
|
||||
})?;
|
||||
let id = match PropertyId::parse(&name, self.context) {
|
||||
Ok(id) => id,
|
||||
Err(()) => return Err(input.new_custom_error(
|
||||
StyleParseErrorKind::UnknownProperty(name.clone())
|
||||
)),
|
||||
};
|
||||
|
||||
// TODO(emilio): Shouldn't this use parse_entirely?
|
||||
PropertyDeclaration::parse_into(self.declarations, id, name, self.context, input)?;
|
||||
|
|
|
@ -46,7 +46,7 @@ pub use self::media_rule::MediaRule;
|
|||
pub use self::namespace_rule::NamespaceRule;
|
||||
pub use self::origin::{Origin, OriginSet, OriginSetIterator, PerOrigin, PerOriginIter};
|
||||
pub use self::page_rule::PageRule;
|
||||
pub use self::rule_parser::{State, TopLevelRuleParser};
|
||||
pub use self::rule_parser::{State, TopLevelRuleParser, InsertRuleContext};
|
||||
pub use self::rule_list::{CssRules, CssRulesHelpers};
|
||||
pub use self::rules_iterator::{AllRules, EffectiveRules};
|
||||
pub use self::rules_iterator::{NestedRuleIterationCondition, RulesIterator};
|
||||
|
@ -178,12 +178,6 @@ pub enum CssRuleType {
|
|||
Viewport = 15,
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub enum SingleRuleParseError {
|
||||
Syntax,
|
||||
Hierarchy,
|
||||
}
|
||||
|
||||
#[allow(missing_docs)]
|
||||
pub enum RulesMutateError {
|
||||
Syntax,
|
||||
|
@ -192,15 +186,6 @@ pub enum RulesMutateError {
|
|||
InvalidState,
|
||||
}
|
||||
|
||||
impl From<SingleRuleParseError> for RulesMutateError {
|
||||
fn from(other: SingleRuleParseError) -> Self {
|
||||
match other {
|
||||
SingleRuleParseError::Syntax => RulesMutateError::Syntax,
|
||||
SingleRuleParseError::Hierarchy => RulesMutateError::HierarchyRequest,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CssRule {
|
||||
/// Returns the CSSOM rule type of this rule.
|
||||
pub fn rule_type(&self) -> CssRuleType {
|
||||
|
@ -236,11 +221,12 @@ impl CssRule {
|
|||
/// Input state is None for a nested rule
|
||||
pub fn parse(
|
||||
css: &str,
|
||||
insert_rule_context: InsertRuleContext,
|
||||
parent_stylesheet_contents: &StylesheetContents,
|
||||
shared_lock: &SharedRwLock,
|
||||
state: Option<State>,
|
||||
state: State,
|
||||
loader: Option<&StylesheetLoader>,
|
||||
) -> Result<(Self, State), SingleRuleParseError> {
|
||||
) -> Result<Self, RulesMutateError> {
|
||||
let url_data = parent_stylesheet_contents.url_data.read();
|
||||
let error_reporter = NullReporter;
|
||||
let context = ParserContext::new(
|
||||
|
@ -257,28 +243,23 @@ impl CssRule {
|
|||
let mut guard = parent_stylesheet_contents.namespaces.write();
|
||||
|
||||
// nested rules are in the body state
|
||||
let state = state.unwrap_or(State::Body);
|
||||
let mut rule_parser = TopLevelRuleParser {
|
||||
stylesheet_origin: parent_stylesheet_contents.origin,
|
||||
context: context,
|
||||
context,
|
||||
error_context: ParserErrorContext {
|
||||
error_reporter: &error_reporter,
|
||||
},
|
||||
shared_lock: &shared_lock,
|
||||
loader: loader,
|
||||
state: state,
|
||||
had_hierarchy_error: false,
|
||||
loader,
|
||||
state,
|
||||
dom_error: None,
|
||||
namespaces: &mut *guard,
|
||||
insert_rule_context: Some(insert_rule_context),
|
||||
};
|
||||
|
||||
parse_one_rule(&mut input, &mut rule_parser)
|
||||
.map(|result| (result, rule_parser.state))
|
||||
.map_err(|_| {
|
||||
if rule_parser.take_had_hierarchy_error() {
|
||||
SingleRuleParseError::Hierarchy
|
||||
} else {
|
||||
SingleRuleParseError::Syntax
|
||||
}
|
||||
rule_parser.dom_error.unwrap_or(RulesMutateError::Syntax)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ use std::fmt::{self, Write};
|
|||
use str::CssStringWriter;
|
||||
use stylesheets::{CssRule, RulesMutateError};
|
||||
use stylesheets::loader::StylesheetLoader;
|
||||
use stylesheets::rule_parser::State;
|
||||
use stylesheets::rule_parser::{InsertRuleContext, State};
|
||||
use stylesheets::stylesheet::StylesheetContents;
|
||||
|
||||
/// A list of CSS rules.
|
||||
|
@ -141,7 +141,7 @@ impl CssRulesHelpers for RawOffsetArc<Locked<CssRules>> {
|
|||
nested: bool,
|
||||
loader: Option<&StylesheetLoader>,
|
||||
) -> Result<CssRule, RulesMutateError> {
|
||||
let state = {
|
||||
let new_rule = {
|
||||
let read_guard = lock.read();
|
||||
let rules = self.read_with(&read_guard);
|
||||
|
||||
|
@ -151,39 +151,33 @@ impl CssRulesHelpers for RawOffsetArc<Locked<CssRules>> {
|
|||
}
|
||||
|
||||
// Computes the parser state at the given index
|
||||
if nested {
|
||||
None
|
||||
let state = if nested {
|
||||
State::Body
|
||||
} else if index == 0 {
|
||||
Some(State::Start)
|
||||
State::Start
|
||||
} else {
|
||||
rules.0.get(index - 1).map(CssRule::rule_state)
|
||||
}
|
||||
};
|
||||
rules.0.get(index - 1).map(CssRule::rule_state).unwrap_or(State::Body)
|
||||
};
|
||||
|
||||
// Step 3, 4
|
||||
// XXXManishearth should we also store the namespace map?
|
||||
let (new_rule, new_state) =
|
||||
CssRule::parse(&rule, parent_stylesheet_contents, lock, state, loader)?;
|
||||
let insert_rule_context = InsertRuleContext {
|
||||
rule_list: &rules.0,
|
||||
index,
|
||||
};
|
||||
|
||||
// Steps 3, 4, 5, 6
|
||||
CssRule::parse(
|
||||
&rule,
|
||||
insert_rule_context,
|
||||
parent_stylesheet_contents,
|
||||
lock,
|
||||
state,
|
||||
loader,
|
||||
)?
|
||||
};
|
||||
|
||||
{
|
||||
let mut write_guard = lock.write();
|
||||
let rules = self.write_with(&mut write_guard);
|
||||
// Step 5
|
||||
// Computes the maximum allowed parser state at a given index.
|
||||
let rev_state = rules.0.get(index).map_or(State::Body, CssRule::rule_state);
|
||||
if new_state > rev_state {
|
||||
// We inserted a rule too early, e.g. inserting
|
||||
// a regular style rule before @namespace rules
|
||||
return Err(RulesMutateError::HierarchyRequest);
|
||||
}
|
||||
|
||||
// Step 6
|
||||
if let CssRule::Namespace(..) = new_rule {
|
||||
if !rules.only_ns_or_import() {
|
||||
return Err(RulesMutateError::InvalidState);
|
||||
}
|
||||
}
|
||||
|
||||
rules.0.insert(index, new_rule.clone());
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ use servo_arc::Arc;
|
|||
use shared_lock::{Locked, SharedRwLock};
|
||||
use str::starts_with_ignore_ascii_case;
|
||||
use style_traits::{ParseError, StyleParseErrorKind};
|
||||
use stylesheets::{CssRule, CssRuleType, CssRules, Origin, StylesheetLoader};
|
||||
use stylesheets::{CssRule, CssRuleType, CssRules, Origin, RulesMutateError, StylesheetLoader};
|
||||
use stylesheets::{DocumentRule, FontFeatureValuesRule, KeyframesRule, MediaRule};
|
||||
use stylesheets::{NamespaceRule, PageRule, StyleRule, SupportsRule, ViewportRule};
|
||||
use stylesheets::document_rule::DocumentCondition;
|
||||
|
@ -31,6 +31,14 @@ use stylesheets::viewport_rule;
|
|||
use values::{CssUrl, CustomIdent, KeyframesName};
|
||||
use values::computed::font::FamilyName;
|
||||
|
||||
/// The information we need particularly to do CSSOM insertRule stuff.
|
||||
pub struct InsertRuleContext<'a> {
|
||||
/// The rule list we're about to insert into.
|
||||
pub rule_list: &'a [CssRule],
|
||||
/// The index we're about to get inserted at.
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
/// The parser for the top-level rules in a stylesheet.
|
||||
pub struct TopLevelRuleParser<'a, R: 'a> {
|
||||
/// The origin of the stylesheet we're parsing.
|
||||
|
@ -51,11 +59,13 @@ pub struct TopLevelRuleParser<'a, R: 'a> {
|
|||
/// Whether we have tried to parse was invalid due to being in the wrong
|
||||
/// place (e.g. an @import rule was found while in the `Body` state). Reset
|
||||
/// to `false` when `take_had_hierarchy_error` is called.
|
||||
pub had_hierarchy_error: bool,
|
||||
pub dom_error: Option<RulesMutateError>,
|
||||
/// The namespace map we use for parsing. Needs to start as `Some()`, and
|
||||
/// will be taken out after parsing namespace rules, and that reference will
|
||||
/// be moved to `ParserContext`.
|
||||
pub namespaces: &'a mut Namespaces,
|
||||
/// The info we need insert a rule in a list.
|
||||
pub insert_rule_context: Option<InsertRuleContext<'a>>,
|
||||
}
|
||||
|
||||
impl<'b, R> TopLevelRuleParser<'b, R> {
|
||||
|
@ -74,14 +84,43 @@ impl<'b, R> TopLevelRuleParser<'b, R> {
|
|||
self.state
|
||||
}
|
||||
|
||||
/// Returns whether we previously tried to parse a rule that was invalid
|
||||
/// due to being in the wrong place (e.g. an @import rule was found after
|
||||
/// a regular style rule). The state of this flag is reset when this
|
||||
/// function is called.
|
||||
pub fn take_had_hierarchy_error(&mut self) -> bool {
|
||||
let had_hierarchy_error = self.had_hierarchy_error;
|
||||
self.had_hierarchy_error = false;
|
||||
had_hierarchy_error
|
||||
/// Checks whether we can parse a rule that would transition us to
|
||||
/// `new_state`.
|
||||
///
|
||||
/// This is usually a simple branch, but we may need more bookkeeping if
|
||||
/// doing `insertRule` from CSSOM.
|
||||
fn check_state(&mut self, new_state: State) -> bool {
|
||||
if self.state > new_state {
|
||||
self.dom_error = Some(RulesMutateError::HierarchyRequest);
|
||||
return false;
|
||||
}
|
||||
|
||||
let ctx = match self.insert_rule_context {
|
||||
Some(ref ctx) => ctx,
|
||||
None => return true,
|
||||
};
|
||||
|
||||
let next_rule_state = match ctx.rule_list.get(ctx.index) {
|
||||
None => return true,
|
||||
Some(rule) => rule.rule_state(),
|
||||
};
|
||||
|
||||
if new_state > next_rule_state {
|
||||
self.dom_error = Some(RulesMutateError::HierarchyRequest);
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there's anything that isn't a namespace rule (or import rule, but
|
||||
// we checked that already at the beginning), reject with a
|
||||
// StateError.
|
||||
if new_state == State::Namespaces &&
|
||||
ctx.rule_list[ctx.index..].iter().any(|r| !matches!(*r, CssRule::Namespace(..)))
|
||||
{
|
||||
self.dom_error = Some(RulesMutateError::InvalidState);
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,9 +190,7 @@ impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a,
|
|||
let location = input.current_source_location();
|
||||
match_ignore_ascii_case! { &*name,
|
||||
"import" => {
|
||||
if self.state > State::Imports {
|
||||
// "@import must be before any rule but @charset"
|
||||
self.had_hierarchy_error = true;
|
||||
if !self.check_state(State::Imports) {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule))
|
||||
}
|
||||
|
||||
|
@ -168,9 +205,7 @@ impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a,
|
|||
return Ok(AtRuleType::WithoutBlock(prelude));
|
||||
},
|
||||
"namespace" => {
|
||||
if self.state > State::Namespaces {
|
||||
// "@namespace must be before any rule but @charset and @import"
|
||||
self.had_hierarchy_error = true;
|
||||
if !self.check_state(State::Namespaces) {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedNamespaceRule))
|
||||
}
|
||||
|
||||
|
@ -190,12 +225,16 @@ impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a,
|
|||
// @charset is removed by rust-cssparser if it’s the first rule in the stylesheet
|
||||
// anything left is invalid.
|
||||
"charset" => {
|
||||
self.had_hierarchy_error = true;
|
||||
self.dom_error = Some(RulesMutateError::HierarchyRequest);
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedCharsetRule))
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if !self.check_state(State::Body) {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
|
||||
AtRuleParser::parse_prelude(&mut self.nested(), name, input)
|
||||
}
|
||||
|
||||
|
@ -229,8 +268,8 @@ impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a,
|
|||
self.state = State::Imports;
|
||||
CssRule::Import(import_rule)
|
||||
},
|
||||
AtRuleNonBlockPrelude::Namespace(prefix, url, location) => {
|
||||
let opt_prefix = if let Some(prefix) = prefix {
|
||||
AtRuleNonBlockPrelude::Namespace(prefix, url, source_location) => {
|
||||
let prefix = if let Some(prefix) = prefix {
|
||||
self.namespaces.prefixes.insert(prefix.clone(), url.clone());
|
||||
Some(prefix)
|
||||
} else {
|
||||
|
@ -240,9 +279,9 @@ impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a,
|
|||
|
||||
self.state = State::Namespaces;
|
||||
CssRule::Namespace(Arc::new(self.shared_lock.wrap(NamespaceRule {
|
||||
prefix: opt_prefix,
|
||||
url: url,
|
||||
source_location: location,
|
||||
prefix,
|
||||
url,
|
||||
source_location,
|
||||
})))
|
||||
},
|
||||
}
|
||||
|
@ -264,6 +303,10 @@ impl<'a, 'i, R: ParseErrorReporter> QualifiedRuleParser<'i> for TopLevelRulePars
|
|||
&mut self,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<QualifiedRuleParserPrelude, ParseError<'i>> {
|
||||
if !self.check_state(State::Body) {
|
||||
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
|
||||
}
|
||||
|
||||
QualifiedRuleParser::parse_prelude(&mut self.nested(), input)
|
||||
}
|
||||
|
||||
|
@ -463,27 +506,27 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a
|
|||
),
|
||||
)))
|
||||
},
|
||||
AtRuleBlockPrelude::Media(media_queries, location) => {
|
||||
AtRuleBlockPrelude::Media(media_queries, source_location) => {
|
||||
Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule {
|
||||
media_queries: media_queries,
|
||||
media_queries,
|
||||
rules: self.parse_nested_rules(input, CssRuleType::Media),
|
||||
source_location: location,
|
||||
source_location,
|
||||
}))))
|
||||
},
|
||||
AtRuleBlockPrelude::Supports(cond, location) => {
|
||||
AtRuleBlockPrelude::Supports(condition, source_location) => {
|
||||
let eval_context = ParserContext::new_with_rule_type(
|
||||
self.context,
|
||||
CssRuleType::Style,
|
||||
self.namespaces,
|
||||
);
|
||||
|
||||
let enabled = cond.eval(&eval_context);
|
||||
let enabled = condition.eval(&eval_context);
|
||||
Ok(CssRule::Supports(Arc::new(self.shared_lock.wrap(
|
||||
SupportsRule {
|
||||
condition: cond,
|
||||
condition,
|
||||
rules: self.parse_nested_rules(input, CssRuleType::Supports),
|
||||
enabled: enabled,
|
||||
source_location: location,
|
||||
enabled,
|
||||
source_location,
|
||||
},
|
||||
))))
|
||||
},
|
||||
|
@ -498,7 +541,7 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a
|
|||
ViewportRule::parse(&context, self.error_context, input)?,
|
||||
))))
|
||||
},
|
||||
AtRuleBlockPrelude::Keyframes(name, prefix, location) => {
|
||||
AtRuleBlockPrelude::Keyframes(name, vendor_prefix, source_location) => {
|
||||
let context = ParserContext::new_with_rule_type(
|
||||
self.context,
|
||||
CssRuleType::Keyframes,
|
||||
|
@ -507,19 +550,19 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a
|
|||
|
||||
Ok(CssRule::Keyframes(Arc::new(self.shared_lock.wrap(
|
||||
KeyframesRule {
|
||||
name: name,
|
||||
name,
|
||||
keyframes: parse_keyframe_list(
|
||||
&context,
|
||||
self.error_context,
|
||||
input,
|
||||
self.shared_lock,
|
||||
),
|
||||
vendor_prefix: prefix,
|
||||
source_location: location,
|
||||
vendor_prefix,
|
||||
source_location,
|
||||
},
|
||||
))))
|
||||
},
|
||||
AtRuleBlockPrelude::Page(location) => {
|
||||
AtRuleBlockPrelude::Page(source_location) => {
|
||||
let context = ParserContext::new_with_rule_type(
|
||||
self.context,
|
||||
CssRuleType::Page,
|
||||
|
@ -530,21 +573,20 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a
|
|||
parse_property_declaration_list(&context, self.error_context, input);
|
||||
Ok(CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule {
|
||||
block: Arc::new(self.shared_lock.wrap(declarations)),
|
||||
source_location: location,
|
||||
source_location,
|
||||
}))))
|
||||
},
|
||||
AtRuleBlockPrelude::Document(cond, location) => {
|
||||
if cfg!(feature = "gecko") {
|
||||
Ok(CssRule::Document(Arc::new(self.shared_lock.wrap(
|
||||
DocumentRule {
|
||||
condition: cond,
|
||||
rules: self.parse_nested_rules(input, CssRuleType::Document),
|
||||
source_location: location,
|
||||
},
|
||||
))))
|
||||
} else {
|
||||
AtRuleBlockPrelude::Document(condition, source_location) => {
|
||||
if !cfg!(feature = "gecko") {
|
||||
unreachable!()
|
||||
}
|
||||
Ok(CssRule::Document(Arc::new(self.shared_lock.wrap(
|
||||
DocumentRule {
|
||||
condition,
|
||||
rules: self.parse_nested_rules(input, CssRuleType::Document),
|
||||
source_location,
|
||||
},
|
||||
))))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -565,13 +607,10 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> QualifiedRuleParser<'i> for NestedRulePa
|
|||
url_data: Some(self.context.url_data),
|
||||
};
|
||||
|
||||
let location = input.current_source_location();
|
||||
let source_location = input.current_source_location();
|
||||
let selectors = SelectorList::parse(&selector_parser, input)?;
|
||||
|
||||
Ok(QualifiedRuleParserPrelude {
|
||||
selectors: selectors,
|
||||
source_location: location,
|
||||
})
|
||||
Ok(QualifiedRuleParserPrelude { selectors, source_location, })
|
||||
}
|
||||
|
||||
fn parse_block<'t>(
|
||||
|
|
|
@ -363,13 +363,14 @@ impl Stylesheet {
|
|||
|
||||
let rule_parser = TopLevelRuleParser {
|
||||
stylesheet_origin: origin,
|
||||
shared_lock: shared_lock,
|
||||
shared_lock,
|
||||
loader: stylesheet_loader,
|
||||
context: context,
|
||||
error_context: error_context,
|
||||
context,
|
||||
error_context,
|
||||
state: State::Start,
|
||||
had_hierarchy_error: false,
|
||||
namespaces: namespaces,
|
||||
dom_error: None,
|
||||
insert_rule_context: None,
|
||||
namespaces,
|
||||
};
|
||||
|
||||
{
|
||||
|
|
|
@ -315,12 +315,12 @@ impl Declaration {
|
|||
|
||||
let mut input = ParserInput::new(&self.0);
|
||||
let mut input = Parser::new(&mut input);
|
||||
input
|
||||
.parse_entirely(|input| -> Result<(), CssParseError<()>> {
|
||||
input.parse_entirely(|input| -> Result<(), CssParseError<()>> {
|
||||
let prop = input.expect_ident_cloned().unwrap();
|
||||
input.expect_colon().unwrap();
|
||||
|
||||
let id = PropertyId::parse(&prop).map_err(|_| input.new_custom_error(()))?;
|
||||
let id = PropertyId::parse(&prop, context)
|
||||
.map_err(|_| input.new_custom_error(()))?;
|
||||
|
||||
let mut declarations = SourcePropertyDeclaration::new();
|
||||
input.parse_until_before(Delimiter::Bang, |input| {
|
||||
|
|
|
@ -433,9 +433,10 @@ pub fn recalc_style_at<E, D, F>(
|
|||
if compute_self {
|
||||
child_cascade_requirement = compute_style(traversal_data, context, element, data);
|
||||
|
||||
if element.is_native_anonymous() {
|
||||
// We must always cascade native anonymous subtrees, since they inherit
|
||||
// styles from their first non-NAC ancestor.
|
||||
if element.is_in_native_anonymous_subtree() {
|
||||
// We must always cascade native anonymous subtrees, since they
|
||||
// may have pseudo-elements underneath that would inherit from the
|
||||
// closest non-NAC ancestor instead of us.
|
||||
child_cascade_requirement = cmp::max(
|
||||
child_cascade_requirement,
|
||||
ChildCascadeRequirement::MustCascadeChildren,
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
use Atom;
|
||||
use cssparser::Parser;
|
||||
use parser::{Parse, ParserContext};
|
||||
use properties::{LonghandId, PropertyId, PropertyFlags, PropertyDeclarationId};
|
||||
use selectors::parser::SelectorParseErrorKind;
|
||||
use std::fmt::{self, Write};
|
||||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
||||
|
@ -382,7 +383,15 @@ pub enum WillChange {
|
|||
Auto,
|
||||
/// <custom-ident>
|
||||
#[css(comma)]
|
||||
AnimateableFeatures(#[css(iterable)] Box<[CustomIdent]>),
|
||||
AnimateableFeatures {
|
||||
/// The features that are supposed to change.
|
||||
#[css(iterable)]
|
||||
features: Box<[CustomIdent]>,
|
||||
/// A bitfield with the kind of change that the value will create, based
|
||||
/// on the above field.
|
||||
#[css(skip)]
|
||||
bits: WillChangeBits,
|
||||
},
|
||||
}
|
||||
|
||||
impl WillChange {
|
||||
|
@ -393,10 +402,68 @@ impl WillChange {
|
|||
}
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
/// The change bits that we care about.
|
||||
///
|
||||
/// These need to be in sync with NS_STYLE_WILL_CHANGE_*.
|
||||
#[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue)]
|
||||
pub struct WillChangeBits: u8 {
|
||||
/// Whether the stacking context will change.
|
||||
const STACKING_CONTEXT = 1 << 0;
|
||||
/// Whether `transform` will change.
|
||||
const TRANSFORM = 1 << 1;
|
||||
/// Whether `scroll-position` will change.
|
||||
const SCROLL = 1 << 2;
|
||||
/// Whether `opacity` will change.
|
||||
const OPACITY = 1 << 3;
|
||||
/// Fixed pos containing block.
|
||||
const FIXPOS_CB = 1 << 4;
|
||||
/// Abs pos containing block.
|
||||
const ABSPOS_CB = 1 << 5;
|
||||
}
|
||||
}
|
||||
|
||||
fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits {
|
||||
let mut flags = match longhand {
|
||||
LonghandId::Opacity => WillChangeBits::OPACITY,
|
||||
LonghandId::Transform => WillChangeBits::TRANSFORM,
|
||||
_ => WillChangeBits::empty(),
|
||||
};
|
||||
|
||||
let property_flags = longhand.flags();
|
||||
if property_flags.contains(PropertyFlags::CREATES_STACKING_CONTEXT) {
|
||||
flags |= WillChangeBits::STACKING_CONTEXT;
|
||||
}
|
||||
if property_flags.contains(PropertyFlags::FIXPOS_CB) {
|
||||
flags |= WillChangeBits::FIXPOS_CB;
|
||||
}
|
||||
if property_flags.contains(PropertyFlags::ABSPOS_CB) {
|
||||
flags |= WillChangeBits::ABSPOS_CB;
|
||||
}
|
||||
flags
|
||||
}
|
||||
|
||||
fn change_bits_for_maybe_property(ident: &str, context: &ParserContext) -> WillChangeBits {
|
||||
let id = match PropertyId::parse(ident, context) {
|
||||
Ok(id) => id,
|
||||
Err(..) => return WillChangeBits::empty(),
|
||||
};
|
||||
|
||||
match id.as_shorthand() {
|
||||
Ok(shorthand) => {
|
||||
shorthand.longhands().fold(WillChangeBits::empty(), |flags, p| {
|
||||
flags | change_bits_for_longhand(p)
|
||||
})
|
||||
}
|
||||
Err(PropertyDeclarationId::Longhand(longhand)) => change_bits_for_longhand(longhand),
|
||||
Err(PropertyDeclarationId::Custom(..)) => WillChangeBits::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for WillChange {
|
||||
/// auto | <animateable-feature>#
|
||||
fn parse<'i, 't>(
|
||||
_context: &ParserContext,
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
) -> Result<WillChange, ParseError<'i>> {
|
||||
if input
|
||||
|
@ -406,18 +473,28 @@ impl Parse for WillChange {
|
|||
return Ok(WillChange::Auto);
|
||||
}
|
||||
|
||||
let mut bits = WillChangeBits::empty();
|
||||
let custom_idents = input.parse_comma_separated(|i| {
|
||||
let location = i.current_source_location();
|
||||
CustomIdent::from_ident(
|
||||
let parser_ident = i.expect_ident()?;
|
||||
let ident = CustomIdent::from_ident(
|
||||
location,
|
||||
i.expect_ident()?,
|
||||
parser_ident,
|
||||
&["will-change", "none", "all", "auto"],
|
||||
)
|
||||
)?;
|
||||
|
||||
if ident.0 == atom!("scroll-position") {
|
||||
bits |= WillChangeBits::SCROLL;
|
||||
} else {
|
||||
bits |= change_bits_for_maybe_property(&parser_ident, context);
|
||||
}
|
||||
Ok(ident)
|
||||
})?;
|
||||
|
||||
Ok(WillChange::AnimateableFeatures(
|
||||
custom_idents.into_boxed_slice(),
|
||||
))
|
||||
Ok(WillChange::AnimateableFeatures {
|
||||
features: custom_idents.into_boxed_slice(),
|
||||
bits,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -525,19 +602,25 @@ pub fn assert_touch_action_matches() {
|
|||
|
||||
bitflags! {
|
||||
#[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue)]
|
||||
#[value_info(other_values = "none,strict,layout,style,paint")]
|
||||
#[value_info(other_values = "none,strict,content,size,layout,style,paint")]
|
||||
/// Constants for contain: https://drafts.csswg.org/css-contain/#contain-property
|
||||
pub struct Contain: u8 {
|
||||
/// 'size' variant, turns on size containment
|
||||
const SIZE = 0x01;
|
||||
/// `layout` variant, turns on layout containment
|
||||
const LAYOUT = 0x01;
|
||||
const LAYOUT = 0x02;
|
||||
/// `style` variant, turns on style containment
|
||||
const STYLE = 0x02;
|
||||
const STYLE = 0x04;
|
||||
/// `paint` variant, turns on paint containment
|
||||
const PAINT = 0x04;
|
||||
const PAINT = 0x08;
|
||||
/// `strict` variant, turns on all types of containment
|
||||
const STRICT = 0x8;
|
||||
const STRICT = 0x10;
|
||||
/// 'content' variant, turns on style, layout, and paint containment
|
||||
const CONTENT = 0x20;
|
||||
/// variant with all the bits that contain: strict turns on
|
||||
const STRICT_BITS = Contain::LAYOUT.bits | Contain::STYLE.bits | Contain::PAINT.bits;
|
||||
const STRICT_BITS = Contain::LAYOUT.bits | Contain::STYLE.bits | Contain::PAINT.bits | Contain::SIZE.bits;
|
||||
/// variant with all the bits that contain: content turns on
|
||||
const CONTENT_BITS = Contain::STYLE.bits | Contain::LAYOUT.bits | Contain::PAINT.bits;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -552,6 +635,9 @@ impl ToCss for Contain {
|
|||
if self.contains(Contain::STRICT) {
|
||||
return dest.write_str("strict");
|
||||
}
|
||||
if self.contains(Contain::CONTENT) {
|
||||
return dest.write_str("content");
|
||||
}
|
||||
|
||||
let mut has_any = false;
|
||||
macro_rules! maybe_write_value {
|
||||
|
@ -565,6 +651,7 @@ impl ToCss for Contain {
|
|||
}
|
||||
};
|
||||
}
|
||||
maybe_write_value!(Contain::SIZE => "size");
|
||||
maybe_write_value!(Contain::LAYOUT => "layout");
|
||||
maybe_write_value!(Contain::STYLE => "style");
|
||||
maybe_write_value!(Contain::PAINT => "paint");
|
||||
|
@ -583,10 +670,12 @@ impl Parse for Contain {
|
|||
let mut result = Contain::empty();
|
||||
while let Ok(name) = input.try(|i| i.expect_ident_cloned()) {
|
||||
let flag = match_ignore_ascii_case! { &name,
|
||||
"size" => Some(Contain::SIZE),
|
||||
"layout" => Some(Contain::LAYOUT),
|
||||
"style" => Some(Contain::STYLE),
|
||||
"paint" => Some(Contain::PAINT),
|
||||
"strict" if result.is_empty() => return Ok(Contain::STRICT | Contain::STRICT_BITS),
|
||||
"content" if result.is_empty() => return Ok(Contain::CONTENT | Contain::CONTENT_BITS),
|
||||
"none" if result.is_empty() => return Ok(result),
|
||||
_ => None
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue