mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +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
|
right
|
||||||
sans-serif
|
sans-serif
|
||||||
screen
|
screen
|
||||||
|
scroll-position
|
||||||
search
|
search
|
||||||
select
|
select
|
||||||
serif
|
serif
|
||||||
|
|
|
@ -757,14 +757,16 @@ impl LayoutThread {
|
||||||
Msg::RegisterPaint(name, mut properties, painter) => {
|
Msg::RegisterPaint(name, mut properties, painter) => {
|
||||||
debug!("Registering the painter");
|
debug!("Registering the painter");
|
||||||
let properties = properties.drain(..)
|
let properties = properties.drain(..)
|
||||||
.filter_map(|name| PropertyId::parse(&*name)
|
.filter_map(|name| {
|
||||||
.ok().map(|id| (name.clone(), id)))
|
let id = PropertyId::parse_enabled_for_all_content(&*name).ok()?;
|
||||||
.filter(|&(_, ref id)| id.as_shorthand().is_err())
|
Some((name.clone(), id))
|
||||||
|
})
|
||||||
|
.filter(|&(_, ref id)| !id.is_shorthand())
|
||||||
.collect();
|
.collect();
|
||||||
let registered_painter = RegisteredPainterImpl {
|
let registered_painter = RegisteredPainterImpl {
|
||||||
name: name.clone(),
|
name: name.clone(),
|
||||||
properties: properties,
|
properties,
|
||||||
painter: painter,
|
painter,
|
||||||
};
|
};
|
||||||
self.registered_painters.0.insert(name, registered_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
|
// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertyvalue
|
||||||
fn GetPropertyValue(&self, property: DOMString) -> DOMString {
|
fn GetPropertyValue(&self, property: DOMString) -> DOMString {
|
||||||
let id = if let Ok(id) = PropertyId::parse(&property) {
|
let id = match PropertyId::parse_enabled_for_all_content(&property) {
|
||||||
id
|
Ok(id) => id,
|
||||||
} else {
|
Err(..) => return DOMString::new(),
|
||||||
// Unkwown property
|
|
||||||
return DOMString::new()
|
|
||||||
};
|
};
|
||||||
self.get_property_value(id)
|
self.get_property_value(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority
|
// https://dev.w3.org/csswg/cssom/#dom-cssstyledeclaration-getpropertypriority
|
||||||
fn GetPropertyPriority(&self, property: DOMString) -> DOMString {
|
fn GetPropertyPriority(&self, property: DOMString) -> DOMString {
|
||||||
let id = if let Ok(id) = PropertyId::parse(&property) {
|
let id = match PropertyId::parse_enabled_for_all_content(&property) {
|
||||||
id
|
Ok(id) => id,
|
||||||
} else {
|
Err(..) => return DOMString::new(),
|
||||||
// Unkwown property
|
|
||||||
return DOMString::new()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.owner.with_block(|pdb| {
|
self.owner.with_block(|pdb| {
|
||||||
|
@ -347,11 +343,9 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
|
||||||
priority: DOMString)
|
priority: DOMString)
|
||||||
-> ErrorResult {
|
-> ErrorResult {
|
||||||
// Step 3
|
// Step 3
|
||||||
let id = if let Ok(id) = PropertyId::parse(&property) {
|
let id = match PropertyId::parse_enabled_for_all_content(&property) {
|
||||||
id
|
Ok(id) => id,
|
||||||
} else {
|
Err(..) => return Ok(()),
|
||||||
// Unknown property
|
|
||||||
return Ok(())
|
|
||||||
};
|
};
|
||||||
self.set_property(id, value, priority)
|
self.set_property(id, value, priority)
|
||||||
}
|
}
|
||||||
|
@ -364,7 +358,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2 & 3
|
// Step 2 & 3
|
||||||
let id = match PropertyId::parse(&property) {
|
let id = match PropertyId::parse_enabled_for_all_content(&property) {
|
||||||
Ok(id) => id,
|
Ok(id) => id,
|
||||||
Err(..) => return Ok(()), // Unkwown property
|
Err(..) => return Ok(()), // Unkwown property
|
||||||
};
|
};
|
||||||
|
@ -396,11 +390,9 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
|
||||||
return Err(Error::NoModificationAllowed);
|
return Err(Error::NoModificationAllowed);
|
||||||
}
|
}
|
||||||
|
|
||||||
let id = if let Ok(id) = PropertyId::parse(&property) {
|
let id = match PropertyId::parse_enabled_for_all_content(&property) {
|
||||||
id
|
Ok(id) => id,
|
||||||
} else {
|
Err(..) => return Ok(DOMString::new()),
|
||||||
// Unkwown property, cannot be there to remove.
|
|
||||||
return Ok(DOMString::new())
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut string = String::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.
|
/// Get this element's style attribute.
|
||||||
fn style_attribute(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>;
|
fn style_attribute(&self) -> Option<ArcBorrow<Locked<PropertyDeclarationBlock>>>;
|
||||||
|
|
||||||
|
@ -657,9 +651,8 @@ pub trait TElement:
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this element is native anonymous (only Gecko has native
|
/// Returns true if this element is in a native anonymous subtree.
|
||||||
/// anonymous content).
|
fn is_in_native_anonymous_subtree(&self) -> bool {
|
||||||
fn is_native_anonymous(&self) -> bool {
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -794,7 +787,7 @@ pub trait TElement:
|
||||||
/// element.
|
/// element.
|
||||||
fn rule_hash_target(&self) -> Self {
|
fn rule_hash_target(&self) -> Self {
|
||||||
if self.implemented_pseudo_element().is_some() {
|
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")
|
.expect("Trying to collect rules for a detached pseudo-element")
|
||||||
} else {
|
} else {
|
||||||
*self
|
*self
|
||||||
|
|
|
@ -588,6 +588,20 @@ impl<'le> fmt::Debug for GeckoElement<'le> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'le> 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]
|
#[inline]
|
||||||
fn may_have_anonymous_children(&self) -> bool {
|
fn may_have_anonymous_children(&self) -> bool {
|
||||||
self.as_node()
|
self.as_node()
|
||||||
|
@ -813,13 +827,6 @@ impl<'le> GeckoElement<'le> {
|
||||||
return self.flags() & (NODE_IS_NATIVE_ANONYMOUS_ROOT as u32) != 0;
|
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.
|
/// This logic is duplicated in Gecko's nsIContent::IsInAnonymousSubtree.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_in_anonymous_subtree(&self) -> bool {
|
fn is_in_anonymous_subtree(&self) -> bool {
|
||||||
|
@ -1038,13 +1045,11 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
type TraversalChildrenIterator = GeckoChildrenIterator<'le>;
|
type TraversalChildrenIterator = GeckoChildrenIterator<'le>;
|
||||||
|
|
||||||
fn inheritance_parent(&self) -> Option<Self> {
|
fn inheritance_parent(&self) -> Option<Self> {
|
||||||
if self.is_native_anonymous() {
|
if self.implemented_pseudo_element().is_some() {
|
||||||
self.closest_non_native_anonymous_ancestor()
|
return self.pseudo_element_originating_element()
|
||||||
} else {
|
|
||||||
self.as_node()
|
|
||||||
.flattened_tree_parent()
|
|
||||||
.and_then(|n| n.as_element())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.as_node().flattened_tree_parent().and_then(|n| n.as_element())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn traversal_children(&self) -> LayoutIterator<GeckoChildrenIterator<'le>> {
|
fn traversal_children(&self) -> LayoutIterator<GeckoChildrenIterator<'le>> {
|
||||||
|
@ -1174,19 +1179,6 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
unsafe { bindings::Gecko_DestroyAnonymousContentList(array) };
|
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]
|
#[inline]
|
||||||
fn as_node(&self) -> Self::ConcreteNode {
|
fn as_node(&self) -> Self::ConcreteNode {
|
||||||
unsafe { GeckoNode(&*(self.0 as *const _ as *const RawGeckoNode)) }
|
unsafe { GeckoNode(&*(self.0 as *const _ as *const RawGeckoNode)) }
|
||||||
|
@ -1223,16 +1215,9 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let slots = self.extended_slots()?;
|
let slots = self.extended_slots()?;
|
||||||
|
|
||||||
let base_declaration: &structs::DeclarationBlock =
|
let declaration: &structs::DeclarationBlock =
|
||||||
slots.mSMILOverrideStyleDeclaration.mRawPtr.as_ref()?;
|
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()?;
|
let raw: &structs::RawServoDeclarationBlock = declaration.mRaw.mRawPtr.as_ref()?;
|
||||||
|
|
||||||
Some(
|
Some(
|
||||||
|
@ -1354,10 +1339,11 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
self.state().intersects(ElementState::IN_VISITED_STATE)
|
self.state().intersects(ElementState::IN_VISITED_STATE)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This logic is duplicated in Gecko's nsINode::IsInNativeAnonymousSubtree.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn is_native_anonymous(&self) -> bool {
|
fn is_in_native_anonymous_subtree(&self) -> bool {
|
||||||
use gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS;
|
use gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE;
|
||||||
self.flags() & (NODE_IS_NATIVE_ANONYMOUS as u32) != 0
|
self.flags() & (NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE as u32) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
|
@ -1366,7 +1352,7 @@ impl<'le> TElement for GeckoElement<'le> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
|
fn implemented_pseudo_element(&self) -> Option<PseudoElement> {
|
||||||
if !self.is_native_anonymous() {
|
if !self.is_in_native_anonymous_subtree() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1915,7 +1901,22 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn pseudo_element_originating_element(&self) -> Option<Self> {
|
fn pseudo_element_originating_element(&self) -> Option<Self> {
|
||||||
debug_assert!(self.implemented_pseudo_element().is_some());
|
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]
|
#[inline]
|
||||||
|
|
|
@ -678,7 +678,7 @@ pub trait MatchMethods: TElement {
|
||||||
let new_primary_style = data.styles.primary.as_ref().unwrap();
|
let new_primary_style = data.styles.primary.as_ref().unwrap();
|
||||||
|
|
||||||
let mut cascade_requirement = ChildCascadeRequirement::CanSkipCascade;
|
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 device = context.shared.stylist.device();
|
||||||
let new_font_size = new_primary_style.get_font().clone_font_size();
|
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>,
|
name: CowRcStr<'i>,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<Importance, ParseError<'i>> {
|
) -> Result<Importance, ParseError<'i>> {
|
||||||
let id = match PropertyId::parse(&name) {
|
let id = match PropertyId::parse(&name, self.context) {
|
||||||
Ok(id) => id,
|
Ok(id) => id,
|
||||||
Err(..) => {
|
Err(..) => {
|
||||||
return Err(input.new_custom_error(if is_non_mozilla_vendor_identifier(&name) {
|
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) {
|
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::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;
|
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 {
|
match v {
|
||||||
T::AnimateableFeatures(features) => {
|
T::AnimateableFeatures { features, bits } => {
|
||||||
unsafe {
|
unsafe {
|
||||||
Gecko_ClearWillChange(&mut self.gecko, features.len());
|
Gecko_ClearWillChange(&mut self.gecko, features.len());
|
||||||
}
|
}
|
||||||
|
|
||||||
for feature in features.iter() {
|
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 {
|
unsafe {
|
||||||
Gecko_AppendWillChange(&mut self.gecko, feature.0.as_ptr());
|
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);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.gecko.mWillChangeBitField = bits.bits();
|
||||||
},
|
},
|
||||||
T::Auto => {
|
T::Auto => {
|
||||||
unsafe {
|
unsafe {
|
||||||
Gecko_ClearWillChange(&mut self.gecko, 0);
|
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 properties::longhands::will_change::computed_value::T;
|
||||||
use gecko_bindings::structs::nsAtom;
|
use gecko_bindings::structs::nsAtom;
|
||||||
use values::CustomIdent;
|
use values::CustomIdent;
|
||||||
|
use values::specified::box_::WillChangeBits;
|
||||||
|
|
||||||
if self.gecko.mWillChange.len() == 0 {
|
if self.gecko.mWillChange.len() == 0 {
|
||||||
return T::Auto
|
return T::Auto
|
||||||
|
@ -3618,7 +3569,10 @@ fn static_assert() {
|
||||||
}
|
}
|
||||||
}).collect();
|
}).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") %>
|
<% 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) {
|
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_NONE;
|
||||||
use gecko_bindings::structs::NS_STYLE_CONTAIN_STRICT;
|
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_LAYOUT;
|
||||||
use gecko_bindings::structs::NS_STYLE_CONTAIN_STYLE;
|
use gecko_bindings::structs::NS_STYLE_CONTAIN_STYLE;
|
||||||
use gecko_bindings::structs::NS_STYLE_CONTAIN_PAINT;
|
use gecko_bindings::structs::NS_STYLE_CONTAIN_PAINT;
|
||||||
use gecko_bindings::structs::NS_STYLE_CONTAIN_ALL_BITS;
|
use gecko_bindings::structs::NS_STYLE_CONTAIN_ALL_BITS;
|
||||||
|
use gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT_BITS;
|
||||||
use properties::longhands::contain::SpecifiedValue;
|
use properties::longhands::contain::SpecifiedValue;
|
||||||
|
|
||||||
if v.is_empty() {
|
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;
|
self.gecko.mContain = (NS_STYLE_CONTAIN_STRICT | NS_STYLE_CONTAIN_ALL_BITS) as u8;
|
||||||
return;
|
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;
|
let mut bitfield = 0;
|
||||||
if v.contains(SpecifiedValue::LAYOUT) {
|
if v.contains(SpecifiedValue::LAYOUT) {
|
||||||
|
@ -3652,36 +3613,57 @@ fn static_assert() {
|
||||||
if v.contains(SpecifiedValue::PAINT) {
|
if v.contains(SpecifiedValue::PAINT) {
|
||||||
bitfield |= NS_STYLE_CONTAIN_PAINT;
|
bitfield |= NS_STYLE_CONTAIN_PAINT;
|
||||||
}
|
}
|
||||||
|
if v.contains(SpecifiedValue::SIZE) {
|
||||||
|
bitfield |= NS_STYLE_CONTAIN_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
self.gecko.mContain = bitfield as u8;
|
self.gecko.mContain = bitfield as u8;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clone_contain(&self) -> longhands::contain::computed_value::T {
|
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_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_LAYOUT;
|
||||||
use gecko_bindings::structs::NS_STYLE_CONTAIN_STYLE;
|
use gecko_bindings::structs::NS_STYLE_CONTAIN_STYLE;
|
||||||
use gecko_bindings::structs::NS_STYLE_CONTAIN_PAINT;
|
use gecko_bindings::structs::NS_STYLE_CONTAIN_PAINT;
|
||||||
use gecko_bindings::structs::NS_STYLE_CONTAIN_ALL_BITS;
|
use gecko_bindings::structs::NS_STYLE_CONTAIN_ALL_BITS;
|
||||||
|
use gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT_BITS;
|
||||||
use properties::longhands::contain::{self, SpecifiedValue};
|
use properties::longhands::contain::{self, SpecifiedValue};
|
||||||
|
|
||||||
let mut servo_flags = contain::computed_value::T::empty();
|
let mut servo_flags = contain::computed_value::T::empty();
|
||||||
let gecko_flags = self.gecko.mContain;
|
let gecko_flags = self.gecko.mContain;
|
||||||
|
|
||||||
if gecko_flags & (NS_STYLE_CONTAIN_STRICT as u8) != 0 &&
|
if gecko_flags & (NS_STYLE_CONTAIN_STRICT as u8) != 0 {
|
||||||
gecko_flags & (NS_STYLE_CONTAIN_ALL_BITS 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);
|
servo_flags.insert(SpecifiedValue::STRICT | SpecifiedValue::STRICT_BITS);
|
||||||
return servo_flags;
|
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 {
|
if gecko_flags & (NS_STYLE_CONTAIN_LAYOUT as u8) != 0 {
|
||||||
servo_flags.insert(SpecifiedValue::LAYOUT);
|
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);
|
servo_flags.insert(SpecifiedValue::STYLE);
|
||||||
}
|
}
|
||||||
if gecko_flags & (NS_STYLE_CONTAIN_PAINT as u8) != 0 {
|
if gecko_flags & (NS_STYLE_CONTAIN_PAINT as u8) != 0 {
|
||||||
servo_flags.insert(SpecifiedValue::PAINT);
|
servo_flags.insert(SpecifiedValue::PAINT);
|
||||||
}
|
}
|
||||||
|
if gecko_flags & (NS_STYLE_CONTAIN_SIZE as u8) != 0 {
|
||||||
|
servo_flags.insert(SpecifiedValue::SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
return servo_flags;
|
return servo_flags;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2501,82 +2501,83 @@ impl Animate for ComputedScale {
|
||||||
/// <https://drafts.csswg.org/css-transforms/#interpolation-of-transforms>
|
/// <https://drafts.csswg.org/css-transforms/#interpolation-of-transforms>
|
||||||
impl Animate for ComputedTransform {
|
impl Animate for ComputedTransform {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn animate(
|
fn animate(&self, other: &Self, procedure: Procedure) -> Result<Self, ()> {
|
||||||
&self,
|
use std::borrow::Cow;
|
||||||
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;
|
|
||||||
|
|
||||||
if procedure == Procedure::Add {
|
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));
|
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() {
|
if this.len() == other.len() {
|
||||||
let is_matched_transforms = this.iter().zip(other).all(|(this, other)| {
|
return true;
|
||||||
is_matched_operation(this, other)
|
|
||||||
});
|
|
||||||
|
|
||||||
if is_matched_transforms {
|
|
||||||
return animate_equal_lists(this, other);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 this = Cow::Borrowed(&self.0);
|
||||||
let mut owned_this = this.clone();
|
let mut other = Cow::Borrowed(&other.0);
|
||||||
let mut owned_other = other.clone();
|
|
||||||
|
|
||||||
if this.is_empty() {
|
if match_operations_if_possible(&mut this, &mut other) {
|
||||||
let this = other_.to_animated_zero()?.0;
|
return Ok(Transform(
|
||||||
if this.iter().zip(other).all(|(this, other)| is_matched_operation(this, other)) {
|
this.iter().zip(other.iter())
|
||||||
return animate_equal_lists(&this, other)
|
.map(|(this, other)| this.animate(other, procedure))
|
||||||
}
|
.collect::<Result<Vec<_>, _>>()?
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match procedure {
|
match procedure {
|
||||||
Procedure::Add => Err(()),
|
Procedure::Add => Err(()),
|
||||||
Procedure::Interpolate { progress } => {
|
Procedure::Interpolate { progress } => {
|
||||||
Ok(Transform(vec![TransformOperation::InterpolateMatrix {
|
Ok(Transform(vec![TransformOperation::InterpolateMatrix {
|
||||||
from_list: Transform(owned_this),
|
from_list: Transform(this.into_owned()),
|
||||||
to_list: Transform(owned_other),
|
to_list: Transform(other.into_owned()),
|
||||||
progress: Percentage(progress as f32),
|
progress: Percentage(progress as f32),
|
||||||
}]))
|
}]))
|
||||||
},
|
},
|
||||||
Procedure::Accumulate { count } => {
|
Procedure::Accumulate { count } => {
|
||||||
Ok(Transform(vec![TransformOperation::AccumulateMatrix {
|
Ok(Transform(vec![TransformOperation::AccumulateMatrix {
|
||||||
from_list: Transform(owned_this),
|
from_list: Transform(this.into_owned()),
|
||||||
to_list: Transform(owned_other),
|
to_list: Transform(other.into_owned()),
|
||||||
count: cmp::min(count, i32::max_value() as u64) as i32,
|
count: cmp::min(count, i32::max_value() as u64) as i32,
|
||||||
}]))
|
}]))
|
||||||
},
|
},
|
||||||
|
|
|
@ -593,7 +593,7 @@ ${helpers.predefined_type("contain",
|
||||||
"specified::Contain::empty()",
|
"specified::Contain::empty()",
|
||||||
animation_value_type="discrete",
|
animation_value_type="discrete",
|
||||||
products="gecko",
|
products="gecko",
|
||||||
flags="FIXPOS_CB",
|
flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
|
||||||
gecko_pref="layout.css.contain.enabled",
|
gecko_pref="layout.css.contain.enabled",
|
||||||
spec="https://drafts.csswg.org/css-contain/#contain-property")}
|
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(
|
${helpers.predefined_type(
|
||||||
"stop-color",
|
"stop-color",
|
||||||
"RGBAColor",
|
"Color",
|
||||||
"RGBA::new(0, 0, 0, 255)",
|
"RGBA::new(0, 0, 0, 255).into()",
|
||||||
products="gecko",
|
products="gecko",
|
||||||
animation_value_type="AnimatedRGBA",
|
animation_value_type="AnimatedRGBA",
|
||||||
spec="https://www.w3.org/TR/SVGTiny12/painting.html#StopColorProperty",
|
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(
|
${helpers.predefined_type(
|
||||||
"flood-color",
|
"flood-color",
|
||||||
"RGBAColor",
|
"Color",
|
||||||
"RGBA::new(0, 0, 0, 255)",
|
"RGBA::new(0, 0, 0, 255).into()",
|
||||||
products="gecko",
|
products="gecko",
|
||||||
animation_value_type="AnimatedRGBA",
|
animation_value_type="AnimatedColor",
|
||||||
spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty",
|
spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
@ -50,10 +50,10 @@ ${helpers.predefined_type("flood-opacity", "Opacity",
|
||||||
|
|
||||||
${helpers.predefined_type(
|
${helpers.predefined_type(
|
||||||
"lighting-color",
|
"lighting-color",
|
||||||
"RGBAColor",
|
"Color",
|
||||||
"RGBA::new(255, 255, 255, 255)",
|
"RGBA::new(255, 255, 255, 255).into()",
|
||||||
products="gecko",
|
products="gecko",
|
||||||
animation_value_type="AnimatedRGBA",
|
animation_value_type="AnimatedColor",
|
||||||
spec="https://www.w3.org/TR/SVG/filters.html#LightingColorProperty",
|
spec="https://www.w3.org/TR/SVG/filters.html#LightingColorProperty",
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
|
@ -1422,10 +1422,12 @@ impl UnparsedValue {
|
||||||
.and_then(|css| {
|
.and_then(|css| {
|
||||||
// As of this writing, only the base URL is used for property
|
// As of this writing, only the base URL is used for property
|
||||||
// values.
|
// values.
|
||||||
|
//
|
||||||
|
// FIXME(emilio): These bits are slightly fishy.
|
||||||
let context = ParserContext::new(
|
let context = ParserContext::new(
|
||||||
Origin::Author,
|
Origin::Author,
|
||||||
&self.url_data,
|
&self.url_data,
|
||||||
None,
|
Some(CssRuleType::Style),
|
||||||
ParsingMode::DEFAULT,
|
ParsingMode::DEFAULT,
|
||||||
quirks_mode,
|
quirks_mode,
|
||||||
);
|
);
|
||||||
|
@ -1595,7 +1597,7 @@ impl PropertyId {
|
||||||
/// Returns a given property from the string `s`.
|
/// Returns a given property from the string `s`.
|
||||||
///
|
///
|
||||||
/// Returns Err(()) for unknown non-custom properties.
|
/// 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
|
// FIXME(https://github.com/rust-lang/rust/issues/33156): remove this
|
||||||
// enum and use PropertyId when stable Rust allows destructors in
|
// enum and use PropertyId when stable Rust allows destructors in
|
||||||
// statics.
|
// statics.
|
||||||
|
@ -1639,13 +1641,39 @@ impl PropertyId {
|
||||||
PropertyId::ShorthandAlias(id, alias)
|
PropertyId::ShorthandAlias(id, alias)
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
return ::custom_properties::parse_name(property_name).map(|name| {
|
let name = ::custom_properties::parse_name(property_name)?;
|
||||||
PropertyId::Custom(::custom_properties::Name::from(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.
|
/// Returns a property id from Gecko's nsCSSPropertyID.
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
#[allow(non_upper_case_globals)]
|
#[allow(non_upper_case_globals)]
|
||||||
|
@ -1972,12 +2000,7 @@ impl PropertyDeclaration {
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<(), ParseError<'i>> {
|
) -> Result<(), ParseError<'i>> {
|
||||||
assert!(declarations.is_empty());
|
assert!(declarations.is_empty());
|
||||||
|
debug_assert!(id.allowed_in(context), "{:?}", id);
|
||||||
if !id.allowed_in(context) {
|
|
||||||
return Err(input.new_custom_error(
|
|
||||||
StyleParseErrorKind::UnknownProperty(name)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
let start = input.state();
|
let start = input.state();
|
||||||
match id {
|
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");
|
debug!("Failing to insert into the cache: NAC");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -656,7 +656,7 @@ impl<E: TElement> StyleSharingCache<E> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if target.is_native_anonymous() {
|
if target.is_in_native_anonymous_subtree() {
|
||||||
debug!("{:?} Cannot share style: NAC", target.element);
|
debug!("{:?} Cannot share style: NAC", target.element);
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -681,7 +681,7 @@ impl<E: TElement> StyleSharingCache<E> {
|
||||||
nth_index_cache: &mut NthIndexCache,
|
nth_index_cache: &mut NthIndexCache,
|
||||||
selector_flags_map: &mut SelectorFlagsMap<E>,
|
selector_flags_map: &mut SelectorFlagsMap<E>,
|
||||||
) -> Option<ResolvedElementStyles> {
|
) -> 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
|
// Check that we have the same parent, or at least that the parents
|
||||||
// share styles and permit sharing across their children. The latter
|
// 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
|
// Before doing the cascade, check the sharing cache and see if we can
|
||||||
// reuse the style via rule node identity.
|
// reuse the style via rule node identity.
|
||||||
let may_reuse =
|
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 {
|
if may_reuse {
|
||||||
let cached = self.context.thread_local.sharing_cache.lookup_by_rules(
|
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>,
|
name: CowRcStr<'i>,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<(), ParseError<'i>> {
|
) -> Result<(), ParseError<'i>> {
|
||||||
let id = PropertyId::parse(&name).map_err(|()| {
|
let id = match PropertyId::parse(&name, self.context) {
|
||||||
input.new_custom_error(StyleParseErrorKind::UnknownProperty(name.clone()))
|
Ok(id) => id,
|
||||||
})?;
|
Err(()) => return Err(input.new_custom_error(
|
||||||
|
StyleParseErrorKind::UnknownProperty(name.clone())
|
||||||
|
)),
|
||||||
|
};
|
||||||
|
|
||||||
// TODO(emilio): Shouldn't this use parse_entirely?
|
// TODO(emilio): Shouldn't this use parse_entirely?
|
||||||
PropertyDeclaration::parse_into(self.declarations, id, name, self.context, input)?;
|
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::namespace_rule::NamespaceRule;
|
||||||
pub use self::origin::{Origin, OriginSet, OriginSetIterator, PerOrigin, PerOriginIter};
|
pub use self::origin::{Origin, OriginSet, OriginSetIterator, PerOrigin, PerOriginIter};
|
||||||
pub use self::page_rule::PageRule;
|
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::rule_list::{CssRules, CssRulesHelpers};
|
||||||
pub use self::rules_iterator::{AllRules, EffectiveRules};
|
pub use self::rules_iterator::{AllRules, EffectiveRules};
|
||||||
pub use self::rules_iterator::{NestedRuleIterationCondition, RulesIterator};
|
pub use self::rules_iterator::{NestedRuleIterationCondition, RulesIterator};
|
||||||
|
@ -178,12 +178,6 @@ pub enum CssRuleType {
|
||||||
Viewport = 15,
|
Viewport = 15,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
|
||||||
pub enum SingleRuleParseError {
|
|
||||||
Syntax,
|
|
||||||
Hierarchy,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
pub enum RulesMutateError {
|
pub enum RulesMutateError {
|
||||||
Syntax,
|
Syntax,
|
||||||
|
@ -192,15 +186,6 @@ pub enum RulesMutateError {
|
||||||
InvalidState,
|
InvalidState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SingleRuleParseError> for RulesMutateError {
|
|
||||||
fn from(other: SingleRuleParseError) -> Self {
|
|
||||||
match other {
|
|
||||||
SingleRuleParseError::Syntax => RulesMutateError::Syntax,
|
|
||||||
SingleRuleParseError::Hierarchy => RulesMutateError::HierarchyRequest,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CssRule {
|
impl CssRule {
|
||||||
/// Returns the CSSOM rule type of this rule.
|
/// Returns the CSSOM rule type of this rule.
|
||||||
pub fn rule_type(&self) -> CssRuleType {
|
pub fn rule_type(&self) -> CssRuleType {
|
||||||
|
@ -236,11 +221,12 @@ impl CssRule {
|
||||||
/// Input state is None for a nested rule
|
/// Input state is None for a nested rule
|
||||||
pub fn parse(
|
pub fn parse(
|
||||||
css: &str,
|
css: &str,
|
||||||
|
insert_rule_context: InsertRuleContext,
|
||||||
parent_stylesheet_contents: &StylesheetContents,
|
parent_stylesheet_contents: &StylesheetContents,
|
||||||
shared_lock: &SharedRwLock,
|
shared_lock: &SharedRwLock,
|
||||||
state: Option<State>,
|
state: State,
|
||||||
loader: Option<&StylesheetLoader>,
|
loader: Option<&StylesheetLoader>,
|
||||||
) -> Result<(Self, State), SingleRuleParseError> {
|
) -> Result<Self, RulesMutateError> {
|
||||||
let url_data = parent_stylesheet_contents.url_data.read();
|
let url_data = parent_stylesheet_contents.url_data.read();
|
||||||
let error_reporter = NullReporter;
|
let error_reporter = NullReporter;
|
||||||
let context = ParserContext::new(
|
let context = ParserContext::new(
|
||||||
|
@ -257,28 +243,23 @@ impl CssRule {
|
||||||
let mut guard = parent_stylesheet_contents.namespaces.write();
|
let mut guard = parent_stylesheet_contents.namespaces.write();
|
||||||
|
|
||||||
// nested rules are in the body state
|
// nested rules are in the body state
|
||||||
let state = state.unwrap_or(State::Body);
|
|
||||||
let mut rule_parser = TopLevelRuleParser {
|
let mut rule_parser = TopLevelRuleParser {
|
||||||
stylesheet_origin: parent_stylesheet_contents.origin,
|
stylesheet_origin: parent_stylesheet_contents.origin,
|
||||||
context: context,
|
context,
|
||||||
error_context: ParserErrorContext {
|
error_context: ParserErrorContext {
|
||||||
error_reporter: &error_reporter,
|
error_reporter: &error_reporter,
|
||||||
},
|
},
|
||||||
shared_lock: &shared_lock,
|
shared_lock: &shared_lock,
|
||||||
loader: loader,
|
loader,
|
||||||
state: state,
|
state,
|
||||||
had_hierarchy_error: false,
|
dom_error: None,
|
||||||
namespaces: &mut *guard,
|
namespaces: &mut *guard,
|
||||||
|
insert_rule_context: Some(insert_rule_context),
|
||||||
};
|
};
|
||||||
|
|
||||||
parse_one_rule(&mut input, &mut rule_parser)
|
parse_one_rule(&mut input, &mut rule_parser)
|
||||||
.map(|result| (result, rule_parser.state))
|
|
||||||
.map_err(|_| {
|
.map_err(|_| {
|
||||||
if rule_parser.take_had_hierarchy_error() {
|
rule_parser.dom_error.unwrap_or(RulesMutateError::Syntax)
|
||||||
SingleRuleParseError::Hierarchy
|
|
||||||
} else {
|
|
||||||
SingleRuleParseError::Syntax
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ use std::fmt::{self, Write};
|
||||||
use str::CssStringWriter;
|
use str::CssStringWriter;
|
||||||
use stylesheets::{CssRule, RulesMutateError};
|
use stylesheets::{CssRule, RulesMutateError};
|
||||||
use stylesheets::loader::StylesheetLoader;
|
use stylesheets::loader::StylesheetLoader;
|
||||||
use stylesheets::rule_parser::State;
|
use stylesheets::rule_parser::{InsertRuleContext, State};
|
||||||
use stylesheets::stylesheet::StylesheetContents;
|
use stylesheets::stylesheet::StylesheetContents;
|
||||||
|
|
||||||
/// A list of CSS rules.
|
/// A list of CSS rules.
|
||||||
|
@ -141,7 +141,7 @@ impl CssRulesHelpers for RawOffsetArc<Locked<CssRules>> {
|
||||||
nested: bool,
|
nested: bool,
|
||||||
loader: Option<&StylesheetLoader>,
|
loader: Option<&StylesheetLoader>,
|
||||||
) -> Result<CssRule, RulesMutateError> {
|
) -> Result<CssRule, RulesMutateError> {
|
||||||
let state = {
|
let new_rule = {
|
||||||
let read_guard = lock.read();
|
let read_guard = lock.read();
|
||||||
let rules = self.read_with(&read_guard);
|
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
|
// Computes the parser state at the given index
|
||||||
if nested {
|
let state = if nested {
|
||||||
None
|
State::Body
|
||||||
} else if index == 0 {
|
} else if index == 0 {
|
||||||
Some(State::Start)
|
State::Start
|
||||||
} else {
|
} 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
|
let insert_rule_context = InsertRuleContext {
|
||||||
// XXXManishearth should we also store the namespace map?
|
rule_list: &rules.0,
|
||||||
let (new_rule, new_state) =
|
index,
|
||||||
CssRule::parse(&rule, parent_stylesheet_contents, lock, state, loader)?;
|
};
|
||||||
|
|
||||||
|
// Steps 3, 4, 5, 6
|
||||||
|
CssRule::parse(
|
||||||
|
&rule,
|
||||||
|
insert_rule_context,
|
||||||
|
parent_stylesheet_contents,
|
||||||
|
lock,
|
||||||
|
state,
|
||||||
|
loader,
|
||||||
|
)?
|
||||||
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut write_guard = lock.write();
|
let mut write_guard = lock.write();
|
||||||
let rules = self.write_with(&mut write_guard);
|
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());
|
rules.0.insert(index, new_rule.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ use servo_arc::Arc;
|
||||||
use shared_lock::{Locked, SharedRwLock};
|
use shared_lock::{Locked, SharedRwLock};
|
||||||
use str::starts_with_ignore_ascii_case;
|
use str::starts_with_ignore_ascii_case;
|
||||||
use style_traits::{ParseError, StyleParseErrorKind};
|
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::{DocumentRule, FontFeatureValuesRule, KeyframesRule, MediaRule};
|
||||||
use stylesheets::{NamespaceRule, PageRule, StyleRule, SupportsRule, ViewportRule};
|
use stylesheets::{NamespaceRule, PageRule, StyleRule, SupportsRule, ViewportRule};
|
||||||
use stylesheets::document_rule::DocumentCondition;
|
use stylesheets::document_rule::DocumentCondition;
|
||||||
|
@ -31,6 +31,14 @@ use stylesheets::viewport_rule;
|
||||||
use values::{CssUrl, CustomIdent, KeyframesName};
|
use values::{CssUrl, CustomIdent, KeyframesName};
|
||||||
use values::computed::font::FamilyName;
|
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.
|
/// The parser for the top-level rules in a stylesheet.
|
||||||
pub struct TopLevelRuleParser<'a, R: 'a> {
|
pub struct TopLevelRuleParser<'a, R: 'a> {
|
||||||
/// The origin of the stylesheet we're parsing.
|
/// 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
|
/// 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
|
/// place (e.g. an @import rule was found while in the `Body` state). Reset
|
||||||
/// to `false` when `take_had_hierarchy_error` is called.
|
/// 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
|
/// 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
|
/// will be taken out after parsing namespace rules, and that reference will
|
||||||
/// be moved to `ParserContext`.
|
/// be moved to `ParserContext`.
|
||||||
pub namespaces: &'a mut Namespaces,
|
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> {
|
impl<'b, R> TopLevelRuleParser<'b, R> {
|
||||||
|
@ -74,14 +84,43 @@ impl<'b, R> TopLevelRuleParser<'b, R> {
|
||||||
self.state
|
self.state
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns whether we previously tried to parse a rule that was invalid
|
/// Checks whether we can parse a rule that would transition us to
|
||||||
/// due to being in the wrong place (e.g. an @import rule was found after
|
/// `new_state`.
|
||||||
/// a regular style rule). The state of this flag is reset when this
|
///
|
||||||
/// function is called.
|
/// This is usually a simple branch, but we may need more bookkeeping if
|
||||||
pub fn take_had_hierarchy_error(&mut self) -> bool {
|
/// doing `insertRule` from CSSOM.
|
||||||
let had_hierarchy_error = self.had_hierarchy_error;
|
fn check_state(&mut self, new_state: State) -> bool {
|
||||||
self.had_hierarchy_error = false;
|
if self.state > new_state {
|
||||||
had_hierarchy_error
|
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();
|
let location = input.current_source_location();
|
||||||
match_ignore_ascii_case! { &*name,
|
match_ignore_ascii_case! { &*name,
|
||||||
"import" => {
|
"import" => {
|
||||||
if self.state > State::Imports {
|
if !self.check_state(State::Imports) {
|
||||||
// "@import must be before any rule but @charset"
|
|
||||||
self.had_hierarchy_error = true;
|
|
||||||
return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule))
|
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));
|
return Ok(AtRuleType::WithoutBlock(prelude));
|
||||||
},
|
},
|
||||||
"namespace" => {
|
"namespace" => {
|
||||||
if self.state > State::Namespaces {
|
if !self.check_state(State::Namespaces) {
|
||||||
// "@namespace must be before any rule but @charset and @import"
|
|
||||||
self.had_hierarchy_error = true;
|
|
||||||
return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedNamespaceRule))
|
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
|
// @charset is removed by rust-cssparser if it’s the first rule in the stylesheet
|
||||||
// anything left is invalid.
|
// anything left is invalid.
|
||||||
"charset" => {
|
"charset" => {
|
||||||
self.had_hierarchy_error = true;
|
self.dom_error = Some(RulesMutateError::HierarchyRequest);
|
||||||
return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedCharsetRule))
|
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)
|
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;
|
self.state = State::Imports;
|
||||||
CssRule::Import(import_rule)
|
CssRule::Import(import_rule)
|
||||||
},
|
},
|
||||||
AtRuleNonBlockPrelude::Namespace(prefix, url, location) => {
|
AtRuleNonBlockPrelude::Namespace(prefix, url, source_location) => {
|
||||||
let opt_prefix = if let Some(prefix) = prefix {
|
let prefix = if let Some(prefix) = prefix {
|
||||||
self.namespaces.prefixes.insert(prefix.clone(), url.clone());
|
self.namespaces.prefixes.insert(prefix.clone(), url.clone());
|
||||||
Some(prefix)
|
Some(prefix)
|
||||||
} else {
|
} else {
|
||||||
|
@ -240,9 +279,9 @@ impl<'a, 'i, R: ParseErrorReporter> AtRuleParser<'i> for TopLevelRuleParser<'a,
|
||||||
|
|
||||||
self.state = State::Namespaces;
|
self.state = State::Namespaces;
|
||||||
CssRule::Namespace(Arc::new(self.shared_lock.wrap(NamespaceRule {
|
CssRule::Namespace(Arc::new(self.shared_lock.wrap(NamespaceRule {
|
||||||
prefix: opt_prefix,
|
prefix,
|
||||||
url: url,
|
url,
|
||||||
source_location: location,
|
source_location,
|
||||||
})))
|
})))
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -264,6 +303,10 @@ impl<'a, 'i, R: ParseErrorReporter> QualifiedRuleParser<'i> for TopLevelRulePars
|
||||||
&mut self,
|
&mut self,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<QualifiedRuleParserPrelude, ParseError<'i>> {
|
) -> 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)
|
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 {
|
Ok(CssRule::Media(Arc::new(self.shared_lock.wrap(MediaRule {
|
||||||
media_queries: media_queries,
|
media_queries,
|
||||||
rules: self.parse_nested_rules(input, CssRuleType::Media),
|
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(
|
let eval_context = ParserContext::new_with_rule_type(
|
||||||
self.context,
|
self.context,
|
||||||
CssRuleType::Style,
|
CssRuleType::Style,
|
||||||
self.namespaces,
|
self.namespaces,
|
||||||
);
|
);
|
||||||
|
|
||||||
let enabled = cond.eval(&eval_context);
|
let enabled = condition.eval(&eval_context);
|
||||||
Ok(CssRule::Supports(Arc::new(self.shared_lock.wrap(
|
Ok(CssRule::Supports(Arc::new(self.shared_lock.wrap(
|
||||||
SupportsRule {
|
SupportsRule {
|
||||||
condition: cond,
|
condition,
|
||||||
rules: self.parse_nested_rules(input, CssRuleType::Supports),
|
rules: self.parse_nested_rules(input, CssRuleType::Supports),
|
||||||
enabled: enabled,
|
enabled,
|
||||||
source_location: location,
|
source_location,
|
||||||
},
|
},
|
||||||
))))
|
))))
|
||||||
},
|
},
|
||||||
|
@ -498,7 +541,7 @@ impl<'a, 'b, 'i, R: ParseErrorReporter> AtRuleParser<'i> for NestedRuleParser<'a
|
||||||
ViewportRule::parse(&context, self.error_context, input)?,
|
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(
|
let context = ParserContext::new_with_rule_type(
|
||||||
self.context,
|
self.context,
|
||||||
CssRuleType::Keyframes,
|
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(
|
Ok(CssRule::Keyframes(Arc::new(self.shared_lock.wrap(
|
||||||
KeyframesRule {
|
KeyframesRule {
|
||||||
name: name,
|
name,
|
||||||
keyframes: parse_keyframe_list(
|
keyframes: parse_keyframe_list(
|
||||||
&context,
|
&context,
|
||||||
self.error_context,
|
self.error_context,
|
||||||
input,
|
input,
|
||||||
self.shared_lock,
|
self.shared_lock,
|
||||||
),
|
),
|
||||||
vendor_prefix: prefix,
|
vendor_prefix,
|
||||||
source_location: location,
|
source_location,
|
||||||
},
|
},
|
||||||
))))
|
))))
|
||||||
},
|
},
|
||||||
AtRuleBlockPrelude::Page(location) => {
|
AtRuleBlockPrelude::Page(source_location) => {
|
||||||
let context = ParserContext::new_with_rule_type(
|
let context = ParserContext::new_with_rule_type(
|
||||||
self.context,
|
self.context,
|
||||||
CssRuleType::Page,
|
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);
|
parse_property_declaration_list(&context, self.error_context, input);
|
||||||
Ok(CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule {
|
Ok(CssRule::Page(Arc::new(self.shared_lock.wrap(PageRule {
|
||||||
block: Arc::new(self.shared_lock.wrap(declarations)),
|
block: Arc::new(self.shared_lock.wrap(declarations)),
|
||||||
source_location: location,
|
source_location,
|
||||||
}))))
|
}))))
|
||||||
},
|
},
|
||||||
AtRuleBlockPrelude::Document(cond, location) => {
|
AtRuleBlockPrelude::Document(condition, source_location) => {
|
||||||
if cfg!(feature = "gecko") {
|
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 {
|
|
||||||
unreachable!()
|
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),
|
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)?;
|
let selectors = SelectorList::parse(&selector_parser, input)?;
|
||||||
|
|
||||||
Ok(QualifiedRuleParserPrelude {
|
Ok(QualifiedRuleParserPrelude { selectors, source_location, })
|
||||||
selectors: selectors,
|
|
||||||
source_location: location,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_block<'t>(
|
fn parse_block<'t>(
|
||||||
|
|
|
@ -363,13 +363,14 @@ impl Stylesheet {
|
||||||
|
|
||||||
let rule_parser = TopLevelRuleParser {
|
let rule_parser = TopLevelRuleParser {
|
||||||
stylesheet_origin: origin,
|
stylesheet_origin: origin,
|
||||||
shared_lock: shared_lock,
|
shared_lock,
|
||||||
loader: stylesheet_loader,
|
loader: stylesheet_loader,
|
||||||
context: context,
|
context,
|
||||||
error_context: error_context,
|
error_context,
|
||||||
state: State::Start,
|
state: State::Start,
|
||||||
had_hierarchy_error: false,
|
dom_error: None,
|
||||||
namespaces: namespaces,
|
insert_rule_context: None,
|
||||||
|
namespaces,
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -315,12 +315,12 @@ impl Declaration {
|
||||||
|
|
||||||
let mut input = ParserInput::new(&self.0);
|
let mut input = ParserInput::new(&self.0);
|
||||||
let mut input = Parser::new(&mut input);
|
let mut input = Parser::new(&mut input);
|
||||||
input
|
input.parse_entirely(|input| -> Result<(), CssParseError<()>> {
|
||||||
.parse_entirely(|input| -> Result<(), CssParseError<()>> {
|
|
||||||
let prop = input.expect_ident_cloned().unwrap();
|
let prop = input.expect_ident_cloned().unwrap();
|
||||||
input.expect_colon().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();
|
let mut declarations = SourcePropertyDeclaration::new();
|
||||||
input.parse_until_before(Delimiter::Bang, |input| {
|
input.parse_until_before(Delimiter::Bang, |input| {
|
||||||
|
|
|
@ -433,9 +433,10 @@ pub fn recalc_style_at<E, D, F>(
|
||||||
if compute_self {
|
if compute_self {
|
||||||
child_cascade_requirement = compute_style(traversal_data, context, element, data);
|
child_cascade_requirement = compute_style(traversal_data, context, element, data);
|
||||||
|
|
||||||
if element.is_native_anonymous() {
|
if element.is_in_native_anonymous_subtree() {
|
||||||
// We must always cascade native anonymous subtrees, since they inherit
|
// We must always cascade native anonymous subtrees, since they
|
||||||
// styles from their first non-NAC ancestor.
|
// 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 = cmp::max(
|
||||||
child_cascade_requirement,
|
child_cascade_requirement,
|
||||||
ChildCascadeRequirement::MustCascadeChildren,
|
ChildCascadeRequirement::MustCascadeChildren,
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
use Atom;
|
use Atom;
|
||||||
use cssparser::Parser;
|
use cssparser::Parser;
|
||||||
use parser::{Parse, ParserContext};
|
use parser::{Parse, ParserContext};
|
||||||
|
use properties::{LonghandId, PropertyId, PropertyFlags, PropertyDeclarationId};
|
||||||
use selectors::parser::SelectorParseErrorKind;
|
use selectors::parser::SelectorParseErrorKind;
|
||||||
use std::fmt::{self, Write};
|
use std::fmt::{self, Write};
|
||||||
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
|
||||||
|
@ -382,7 +383,15 @@ pub enum WillChange {
|
||||||
Auto,
|
Auto,
|
||||||
/// <custom-ident>
|
/// <custom-ident>
|
||||||
#[css(comma)]
|
#[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 {
|
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 {
|
impl Parse for WillChange {
|
||||||
/// auto | <animateable-feature>#
|
/// auto | <animateable-feature>#
|
||||||
fn parse<'i, 't>(
|
fn parse<'i, 't>(
|
||||||
_context: &ParserContext,
|
context: &ParserContext,
|
||||||
input: &mut Parser<'i, 't>,
|
input: &mut Parser<'i, 't>,
|
||||||
) -> Result<WillChange, ParseError<'i>> {
|
) -> Result<WillChange, ParseError<'i>> {
|
||||||
if input
|
if input
|
||||||
|
@ -406,18 +473,28 @@ impl Parse for WillChange {
|
||||||
return Ok(WillChange::Auto);
|
return Ok(WillChange::Auto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut bits = WillChangeBits::empty();
|
||||||
let custom_idents = input.parse_comma_separated(|i| {
|
let custom_idents = input.parse_comma_separated(|i| {
|
||||||
let location = i.current_source_location();
|
let location = i.current_source_location();
|
||||||
CustomIdent::from_ident(
|
let parser_ident = i.expect_ident()?;
|
||||||
|
let ident = CustomIdent::from_ident(
|
||||||
location,
|
location,
|
||||||
i.expect_ident()?,
|
parser_ident,
|
||||||
&["will-change", "none", "all", "auto"],
|
&["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(
|
Ok(WillChange::AnimateableFeatures {
|
||||||
custom_idents.into_boxed_slice(),
|
features: custom_idents.into_boxed_slice(),
|
||||||
))
|
bits,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,19 +602,25 @@ pub fn assert_touch_action_matches() {
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
#[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue)]
|
#[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
|
/// Constants for contain: https://drafts.csswg.org/css-contain/#contain-property
|
||||||
pub struct Contain: u8 {
|
pub struct Contain: u8 {
|
||||||
|
/// 'size' variant, turns on size containment
|
||||||
|
const SIZE = 0x01;
|
||||||
/// `layout` variant, turns on layout containment
|
/// `layout` variant, turns on layout containment
|
||||||
const LAYOUT = 0x01;
|
const LAYOUT = 0x02;
|
||||||
/// `style` variant, turns on style containment
|
/// `style` variant, turns on style containment
|
||||||
const STYLE = 0x02;
|
const STYLE = 0x04;
|
||||||
/// `paint` variant, turns on paint containment
|
/// `paint` variant, turns on paint containment
|
||||||
const PAINT = 0x04;
|
const PAINT = 0x08;
|
||||||
/// `strict` variant, turns on all types of containment
|
/// `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
|
/// 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) {
|
if self.contains(Contain::STRICT) {
|
||||||
return dest.write_str("strict");
|
return dest.write_str("strict");
|
||||||
}
|
}
|
||||||
|
if self.contains(Contain::CONTENT) {
|
||||||
|
return dest.write_str("content");
|
||||||
|
}
|
||||||
|
|
||||||
let mut has_any = false;
|
let mut has_any = false;
|
||||||
macro_rules! maybe_write_value {
|
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::LAYOUT => "layout");
|
||||||
maybe_write_value!(Contain::STYLE => "style");
|
maybe_write_value!(Contain::STYLE => "style");
|
||||||
maybe_write_value!(Contain::PAINT => "paint");
|
maybe_write_value!(Contain::PAINT => "paint");
|
||||||
|
@ -583,10 +670,12 @@ impl Parse for Contain {
|
||||||
let mut result = Contain::empty();
|
let mut result = Contain::empty();
|
||||||
while let Ok(name) = input.try(|i| i.expect_ident_cloned()) {
|
while let Ok(name) = input.try(|i| i.expect_ident_cloned()) {
|
||||||
let flag = match_ignore_ascii_case! { &name,
|
let flag = match_ignore_ascii_case! { &name,
|
||||||
|
"size" => Some(Contain::SIZE),
|
||||||
"layout" => Some(Contain::LAYOUT),
|
"layout" => Some(Contain::LAYOUT),
|
||||||
"style" => Some(Contain::STYLE),
|
"style" => Some(Contain::STYLE),
|
||||||
"paint" => Some(Contain::PAINT),
|
"paint" => Some(Contain::PAINT),
|
||||||
"strict" if result.is_empty() => return Ok(Contain::STRICT | Contain::STRICT_BITS),
|
"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" if result.is_empty() => return Ok(result),
|
||||||
_ => None
|
_ => None
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue