Auto merge of #28217 - servo:gecko-sync, r=emilio,jdm

style: Sync changes from mozilla-central.
This commit is contained in:
bors-servo 2021-02-27 08:23:57 -05:00 committed by GitHub
commit b196bfeeeb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
206 changed files with 4276 additions and 3864 deletions

7
Cargo.lock generated
View file

@ -1,5 +1,7 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3
[[package]] [[package]]
name = "accountable-refcell" name = "accountable-refcell"
version = "0.2.0" version = "0.2.0"
@ -1171,9 +1173,9 @@ dependencies = [
[[package]] [[package]]
name = "cssparser" name = "cssparser"
version = "0.27.2" version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "754b69d351cdc2d8ee09ae203db831e005560fc6030da058f86ad60c92a9cb0a" checksum = "1db8599a9761b371751fbf13e076fa03c6e1a78f8c5288e6ab9467f10a2322c1"
dependencies = [ dependencies = [
"cssparser-macros", "cssparser-macros",
"dtoa-short", "dtoa-short",
@ -5214,7 +5216,6 @@ dependencies = [
"precomputed-hash", "precomputed-hash",
"servo_arc", "servo_arc",
"smallvec 1.6.1", "smallvec 1.6.1",
"thin-slice",
"to_shmem", "to_shmem",
"to_shmem_derive", "to_shmem_derive",
] ]

View file

@ -20,7 +20,7 @@ bitflags = "1.0"
byteorder = "1" byteorder = "1"
canvas_traits = { path = "../canvas_traits" } canvas_traits = { path = "../canvas_traits" }
crossbeam-channel = "0.4" crossbeam-channel = "0.4"
cssparser = "0.27" cssparser = "0.28"
euclid = "0.20" euclid = "0.20"
font-kit = "0.10" font-kit = "0.10"
fnv = "1.0" fnv = "1.0"

View file

@ -16,7 +16,7 @@ xr-profile = ["webxr-api/profile", "time"]
[dependencies] [dependencies]
crossbeam-channel = "0.4" crossbeam-channel = "0.4"
cssparser = "0.27" cssparser = "0.28"
euclid = "0.20" euclid = "0.20"
ipc-channel = "0.14" ipc-channel = "0.14"
lazy_static = "1" lazy_static = "1"

View file

@ -154,19 +154,19 @@ pub fn fmap_trait_output(input: &DeriveInput, trait_path: &Path, trait_output: &
segment.into() segment.into()
} }
pub fn map_type_params<F>(ty: &Type, params: &[&TypeParam], f: &mut F) -> Type pub fn map_type_params<F>(ty: &Type, params: &[&TypeParam], self_type: &Path, f: &mut F) -> Type
where where
F: FnMut(&Ident) -> Type, F: FnMut(&Ident) -> Type,
{ {
match *ty { match *ty {
Type::Slice(ref inner) => Type::from(TypeSlice { Type::Slice(ref inner) => Type::from(TypeSlice {
elem: Box::new(map_type_params(&inner.elem, params, f)), elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),
..inner.clone() ..inner.clone()
}), }),
Type::Array(ref inner) => { Type::Array(ref inner) => {
//ref ty, ref expr) => { //ref ty, ref expr) => {
Type::from(TypeArray { Type::from(TypeArray {
elem: Box::new(map_type_params(&inner.elem, params, f)), elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),
..inner.clone() ..inner.clone()
}) })
}, },
@ -175,7 +175,7 @@ where
elems: inner elems: inner
.elems .elems
.iter() .iter()
.map(|ty| map_type_params(&ty, params, f)) .map(|ty| map_type_params(&ty, params, self_type, f))
.collect(), .collect(),
..inner.clone() ..inner.clone()
}), }),
@ -187,10 +187,16 @@ where
if params.iter().any(|ref param| &param.ident == ident) { if params.iter().any(|ref param| &param.ident == ident) {
return f(ident); return f(ident);
} }
if ident == "Self" {
return Type::from(TypePath {
qself: None,
path: self_type.clone(),
});
}
} }
Type::from(TypePath { Type::from(TypePath {
qself: None, qself: None,
path: map_type_params_in_path(path, params, f), path: map_type_params_in_path(path, params, self_type, f),
}) })
}, },
Type::Path(TypePath { Type::Path(TypePath {
@ -198,25 +204,30 @@ where
ref path, ref path,
}) => Type::from(TypePath { }) => Type::from(TypePath {
qself: qself.as_ref().map(|qself| QSelf { qself: qself.as_ref().map(|qself| QSelf {
ty: Box::new(map_type_params(&qself.ty, params, f)), ty: Box::new(map_type_params(&qself.ty, params, self_type, f)),
position: qself.position, position: qself.position,
..qself.clone() ..qself.clone()
}), }),
path: map_type_params_in_path(path, params, f), path: map_type_params_in_path(path, params, self_type, f),
}), }),
Type::Paren(ref inner) => Type::from(TypeParen { Type::Paren(ref inner) => Type::from(TypeParen {
elem: Box::new(map_type_params(&inner.elem, params, f)), elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),
..inner.clone() ..inner.clone()
}), }),
Type::Group(ref inner) => Type::from(TypeGroup { Type::Group(ref inner) => Type::from(TypeGroup {
elem: Box::new(map_type_params(&inner.elem, params, f)), elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),
..inner.clone() ..inner.clone()
}), }),
ref ty => panic!("type {:?} cannot be mapped yet", ty), ref ty => panic!("type {:?} cannot be mapped yet", ty),
} }
} }
fn map_type_params_in_path<F>(path: &Path, params: &[&TypeParam], f: &mut F) -> Path fn map_type_params_in_path<F>(
path: &Path,
params: &[&TypeParam],
self_type: &Path,
f: &mut F,
) -> Path
where where
F: FnMut(&Ident) -> Type, F: FnMut(&Ident) -> Type,
{ {
@ -235,12 +246,12 @@ where
.iter() .iter()
.map(|arg| match arg { .map(|arg| match arg {
ty @ &GenericArgument::Lifetime(_) => ty.clone(), ty @ &GenericArgument::Lifetime(_) => ty.clone(),
&GenericArgument::Type(ref data) => { &GenericArgument::Type(ref data) => GenericArgument::Type(
GenericArgument::Type(map_type_params(data, params, f)) map_type_params(data, params, self_type, f),
}, ),
&GenericArgument::Binding(ref data) => { &GenericArgument::Binding(ref data) => {
GenericArgument::Binding(Binding { GenericArgument::Binding(Binding {
ty: map_type_params(&data.ty, params, f), ty: map_type_params(&data.ty, params, self_type, f),
..data.clone() ..data.clone()
}) })
}, },

View file

@ -71,8 +71,8 @@ use style::logical_geometry::Direction;
use style::properties::ComputedValues; use style::properties::ComputedValues;
use style::selector_parser::{PseudoElement, RestyleDamage}; use style::selector_parser::{PseudoElement, RestyleDamage};
use style::servo::restyle_damage::ServoRestyleDamage; use style::servo::restyle_damage::ServoRestyleDamage;
use style::values::computed::Image;
use style::values::generics::counters::ContentItem; use style::values::generics::counters::ContentItem;
use style::values::generics::url::UrlOrNone as ImageUrlOrNone;
/// The results of flow construction for a DOM node. /// The results of flow construction for a DOM node.
#[derive(Clone)] #[derive(Clone)]
@ -1506,9 +1506,9 @@ where
) -> ConstructionResult { ) -> ConstructionResult {
let flotation = FloatKind::from_property(flotation); let flotation = FloatKind::from_property(flotation);
let marker_fragments = match node.style(self.style_context()).get_list().list_style_image { let marker_fragments = match node.style(self.style_context()).get_list().list_style_image {
ImageUrlOrNone::Url(ref url_value) => { Image::Url(ref url_value) => {
let image_info = Box::new(ImageFragmentInfo::new( let image_info = Box::new(ImageFragmentInfo::new(
url_value.url().map(|u| u.clone()), url_value.url().cloned(),
None, None,
node, node,
&self.layout_context, &self.layout_context,
@ -1519,7 +1519,13 @@ where
self.layout_context, self.layout_context,
)] )]
}, },
ImageUrlOrNone::None => match ListStyleTypeContent::from_list_style_type( // XXX: Non-None image types unimplemented.
Image::ImageSet(..) |
Image::Rect(..) |
Image::Gradient(..) |
Image::PaintWorklet(..) |
Image::CrossFade(..) |
Image::None => match ListStyleTypeContent::from_list_style_type(
node.style(self.style_context()).get_list().list_style_type, node.style(self.style_context()).get_list().list_style_type,
) { ) {
ListStyleTypeContent::None => Vec::new(), ListStyleTypeContent::None => Vec::new(),

View file

@ -792,6 +792,9 @@ impl Fragment {
); );
} }
}, },
Image::CrossFade(..) | Image::ImageSet(..) => {
unreachable!("Shouldn't be parsed by Servo for now")
},
Image::Rect(ref rect) => { Image::Rect(ref rect) => {
// This is a (boxed) empty enum on non-Gecko // This is a (boxed) empty enum on non-Gecko
match **rect {} match **rect {}

View file

@ -47,6 +47,7 @@ use style::properties::{
}; };
use style::selector_parser::PseudoElement; use style::selector_parser::PseudoElement;
use style::shared_lock::SharedRwLock; use style::shared_lock::SharedRwLock;
use style::stylesheets::{CssRuleType, Origin};
use style_traits::{CSSPixel, ParsingMode, ToCss}; use style_traits::{CSSPixel, ParsingMode, ToCss};
use webrender_api::ExternalScrollId; use webrender_api::ExternalScrollId;
@ -762,10 +763,12 @@ fn create_font_declaration(
&mut declarations, &mut declarations,
property.clone(), property.clone(),
value, value,
Origin::Author,
url_data, url_data,
None, None,
ParsingMode::DEFAULT, ParsingMode::DEFAULT,
quirks_mode, quirks_mode,
CssRuleType::Style,
); );
let declarations = match result { let declarations = match result {
Ok(()) => { Ok(()) => {

View file

@ -16,7 +16,7 @@ doctest = false
app_units = "0.7" app_units = "0.7"
atomic_refcell = "0.1.6" atomic_refcell = "0.1.6"
canvas_traits = { path = "../canvas_traits" } canvas_traits = { path = "../canvas_traits" }
cssparser = "0.27" cssparser = "0.28"
embedder_traits = { path = "../embedder_traits" } embedder_traits = { path = "../embedder_traits" }
euclid = "0.20" euclid = "0.20"
fnv = "1.0" fnv = "1.0"

View file

@ -474,6 +474,9 @@ impl<'a> BuilderForBoxFragment<'a> {
}, },
// Gecko-only value, represented as a (boxed) empty enum on non-Gecko. // Gecko-only value, represented as a (boxed) empty enum on non-Gecko.
Image::Rect(ref rect) => match **rect {}, Image::Rect(ref rect) => match **rect {},
Image::ImageSet(..) | Image::CrossFade(..) => {
unreachable!("Shouldn't be parsed on Servo for now")
},
} }
} }
} }

View file

@ -7,7 +7,7 @@ use crate::dom_traversal::{NodeAndStyleInfo, NodeExt, PseudoElementContentItem};
use crate::replaced::ReplacedContent; use crate::replaced::ReplacedContent;
use style::properties::longhands::list_style_type::computed_value::T as ListStyleType; use style::properties::longhands::list_style_type::computed_value::T as ListStyleType;
use style::properties::style_structs; use style::properties::style_structs;
use style::values::computed::url::UrlOrNone; use style::values::computed::Image;
/// https://drafts.csswg.org/css-lists/#content-property /// https://drafts.csswg.org/css-lists/#content-property
pub(crate) fn make_marker<'dom, Node>( pub(crate) fn make_marker<'dom, Node>(
@ -21,13 +21,18 @@ where
// https://drafts.csswg.org/css-lists/#marker-image // https://drafts.csswg.org/css-lists/#marker-image
let marker_image = || match &style.list_style_image { let marker_image = || match &style.list_style_image {
UrlOrNone::Url(url) => Some(vec![ Image::Url(url) => Some(vec![
PseudoElementContentItem::Replaced(ReplacedContent::from_image_url( PseudoElementContentItem::Replaced(ReplacedContent::from_image_url(
info.node, context, url, info.node, context, url,
)?), )?),
PseudoElementContentItem::Text(" ".into()), PseudoElementContentItem::Text(" ".into()),
]), ]),
UrlOrNone::None => None, // XXX: Non-None image types unimplemented.
Image::ImageSet(..) |
Image::Rect(..) |
Image::Gradient(..) |
Image::CrossFade(..) |
Image::None => None,
}; };
marker_image().or_else(|| { marker_image().or_else(|| {
Some(vec![PseudoElementContentItem::Text( Some(vec![PseudoElementContentItem::Text(

View file

@ -90,6 +90,7 @@ use style::shared_lock::{
}; };
use style::str::is_whitespace; use style::str::is_whitespace;
use style::stylist::CascadeData; use style::stylist::CascadeData;
use style::values::{AtomIdent, AtomString};
use style::CaseSensitivityExt; use style::CaseSensitivityExt;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -500,8 +501,8 @@ impl<'le> TElement for ServoLayoutElement<'le> {
} }
#[inline] #[inline]
fn has_attr(&self, namespace: &Namespace, attr: &LocalName) -> bool { fn has_attr(&self, namespace: &style::Namespace, attr: &style::LocalName) -> bool {
self.get_attr(namespace, attr).is_some() self.get_attr(&**namespace, &**attr).is_some()
} }
#[inline] #[inline]
@ -512,11 +513,11 @@ impl<'le> TElement for ServoLayoutElement<'le> {
#[inline(always)] #[inline(always)]
fn each_class<F>(&self, mut callback: F) fn each_class<F>(&self, mut callback: F)
where where
F: FnMut(&Atom), F: FnMut(&AtomIdent),
{ {
if let Some(ref classes) = self.element.get_classes_for_layout() { if let Some(ref classes) = self.element.get_classes_for_layout() {
for class in *classes { for class in *classes {
callback(class) callback(AtomIdent::cast(class))
} }
} }
} }
@ -640,7 +641,7 @@ impl<'le> TElement for ServoLayoutElement<'le> {
fn lang_attr(&self) -> Option<SelectorAttrValue> { fn lang_attr(&self) -> Option<SelectorAttrValue> {
self.get_attr(&ns!(xml), &local_name!("lang")) self.get_attr(&ns!(xml), &local_name!("lang"))
.or_else(|| self.get_attr(&ns!(), &local_name!("lang"))) .or_else(|| self.get_attr(&ns!(), &local_name!("lang")))
.map(|v| String::from(v as &str)) .map(|v| SelectorAttrValue::from(v as &str))
} }
fn match_element_lang( fn match_element_lang(
@ -663,8 +664,8 @@ impl<'le> TElement for ServoLayoutElement<'le> {
// so we can decide when to fall back to the Content-Language check. // so we can decide when to fall back to the Content-Language check.
let element_lang = match override_lang { let element_lang = match override_lang {
Some(Some(lang)) => lang, Some(Some(lang)) => lang,
Some(None) => String::new(), Some(None) => AtomString::default(),
None => self.element.get_lang_for_layout(), None => AtomString::from(&*self.element.get_lang_for_layout()),
}; };
extended_filtering(&element_lang, &*value) extended_filtering(&element_lang, &*value)
} }
@ -813,9 +814,9 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
fn attr_matches( fn attr_matches(
&self, &self,
ns: &NamespaceConstraint<&Namespace>, ns: &NamespaceConstraint<&style::Namespace>,
local_name: &LocalName, local_name: &style::LocalName,
operation: &AttrSelectorOperation<&String>, operation: &AttrSelectorOperation<&AtomString>,
) -> bool { ) -> bool {
match *ns { match *ns {
NamespaceConstraint::Specific(ref ns) => self NamespaceConstraint::Specific(ref ns) => self
@ -945,7 +946,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
} }
#[inline] #[inline]
fn has_id(&self, id: &Atom, case_sensitivity: CaseSensitivity) -> bool { fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
unsafe { unsafe {
(*self.element.id_attribute()) (*self.element.id_attribute())
.as_ref() .as_ref()
@ -954,16 +955,16 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
} }
#[inline] #[inline]
fn is_part(&self, _name: &Atom) -> bool { fn is_part(&self, _name: &AtomIdent) -> bool {
false false
} }
fn imported_part(&self, _: &Atom) -> Option<Atom> { fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
None None
} }
#[inline] #[inline]
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
self.element.has_class_for_layout(name, case_sensitivity) self.element.has_class_for_layout(name, case_sensitivity)
} }
@ -1434,9 +1435,9 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
fn attr_matches( fn attr_matches(
&self, &self,
ns: &NamespaceConstraint<&Namespace>, ns: &NamespaceConstraint<&style::Namespace>,
local_name: &LocalName, local_name: &style::LocalName,
operation: &AttrSelectorOperation<&String>, operation: &AttrSelectorOperation<&AtomString>,
) -> bool { ) -> bool {
match *ns { match *ns {
NamespaceConstraint::Specific(ref ns) => self NamespaceConstraint::Specific(ref ns) => self
@ -1470,23 +1471,23 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
false false
} }
fn has_id(&self, _id: &Atom, _case_sensitivity: CaseSensitivity) -> bool { fn has_id(&self, _id: &AtomIdent, _case_sensitivity: CaseSensitivity) -> bool {
debug!("ServoThreadSafeLayoutElement::has_id called"); debug!("ServoThreadSafeLayoutElement::has_id called");
false false
} }
#[inline] #[inline]
fn is_part(&self, _name: &Atom) -> bool { fn is_part(&self, _name: &AtomIdent) -> bool {
debug!("ServoThreadSafeLayoutElement::is_part called"); debug!("ServoThreadSafeLayoutElement::is_part called");
false false
} }
fn imported_part(&self, _: &Atom) -> Option<Atom> { fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
debug!("ServoThreadSafeLayoutElement::imported_part called"); debug!("ServoThreadSafeLayoutElement::imported_part called");
None None
} }
fn has_class(&self, _name: &Atom, _case_sensitivity: CaseSensitivity) -> bool { fn has_class(&self, _name: &AtomIdent, _case_sensitivity: CaseSensitivity) -> bool {
debug!("ServoThreadSafeLayoutElement::has_class called"); debug!("ServoThreadSafeLayoutElement::has_class called");
false false
} }

View file

@ -506,6 +506,7 @@ impl LayoutThread {
let device = Device::new( let device = Device::new(
MediaType::screen(), MediaType::screen(),
QuirksMode::NoQuirks,
window_size.initial_viewport, window_size.initial_viewport,
window_size.device_pixel_ratio, window_size.device_pixel_ratio,
); );
@ -1267,7 +1268,12 @@ impl LayoutThread {
}; };
let had_used_viewport_units = self.stylist.device().used_viewport_units(); let had_used_viewport_units = self.stylist.device().used_viewport_units();
let device = Device::new(MediaType::screen(), initial_viewport, device_pixel_ratio); let device = Device::new(
MediaType::screen(),
self.stylist.quirks_mode(),
initial_viewport,
device_pixel_ratio,
);
let sheet_origins_affected_by_device_change = self.stylist.set_device(device, &guards); let sheet_origins_affected_by_device_change = self.stylist.set_device(device, &guards);
self.stylist self.stylist

View file

@ -90,6 +90,7 @@ use style::shared_lock::{
}; };
use style::str::is_whitespace; use style::str::is_whitespace;
use style::stylist::CascadeData; use style::stylist::CascadeData;
use style::values::{AtomIdent, AtomString};
use style::CaseSensitivityExt; use style::CaseSensitivityExt;
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
@ -508,7 +509,7 @@ impl<'le> TElement for ServoLayoutElement<'le> {
} }
#[inline] #[inline]
fn has_attr(&self, namespace: &Namespace, attr: &LocalName) -> bool { fn has_attr(&self, namespace: &style::Namespace, attr: &style::LocalName) -> bool {
self.get_attr(namespace, attr).is_some() self.get_attr(namespace, attr).is_some()
} }
@ -520,11 +521,11 @@ impl<'le> TElement for ServoLayoutElement<'le> {
#[inline(always)] #[inline(always)]
fn each_class<F>(&self, mut callback: F) fn each_class<F>(&self, mut callback: F)
where where
F: FnMut(&Atom), F: FnMut(&AtomIdent),
{ {
if let Some(ref classes) = self.element.get_classes_for_layout() { if let Some(ref classes) = self.element.get_classes_for_layout() {
for class in *classes { for class in *classes {
callback(class) callback(AtomIdent::cast(class))
} }
} }
} }
@ -648,7 +649,7 @@ impl<'le> TElement for ServoLayoutElement<'le> {
fn lang_attr(&self) -> Option<SelectorAttrValue> { fn lang_attr(&self) -> Option<SelectorAttrValue> {
self.get_attr(&ns!(xml), &local_name!("lang")) self.get_attr(&ns!(xml), &local_name!("lang"))
.or_else(|| self.get_attr(&ns!(), &local_name!("lang"))) .or_else(|| self.get_attr(&ns!(), &local_name!("lang")))
.map(|v| String::from(v as &str)) .map(|v| SelectorAttrValue::from(v as &str))
} }
fn match_element_lang( fn match_element_lang(
@ -671,8 +672,8 @@ impl<'le> TElement for ServoLayoutElement<'le> {
// so we can decide when to fall back to the Content-Language check. // so we can decide when to fall back to the Content-Language check.
let element_lang = match override_lang { let element_lang = match override_lang {
Some(Some(lang)) => lang, Some(Some(lang)) => lang,
Some(None) => String::new(), Some(None) => AtomString::default(),
None => self.element.get_lang_for_layout(), None => AtomString::from(&*self.element.get_lang_for_layout()),
}; };
extended_filtering(&element_lang, &*value) extended_filtering(&element_lang, &*value)
} }
@ -821,9 +822,9 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
fn attr_matches( fn attr_matches(
&self, &self,
ns: &NamespaceConstraint<&Namespace>, ns: &NamespaceConstraint<&style::Namespace>,
local_name: &LocalName, local_name: &style::LocalName,
operation: &AttrSelectorOperation<&String>, operation: &AttrSelectorOperation<&AtomString>,
) -> bool { ) -> bool {
match *ns { match *ns {
NamespaceConstraint::Specific(ref ns) => self NamespaceConstraint::Specific(ref ns) => self
@ -953,7 +954,7 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
} }
#[inline] #[inline]
fn has_id(&self, id: &Atom, case_sensitivity: CaseSensitivity) -> bool { fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
unsafe { unsafe {
(*self.element.id_attribute()) (*self.element.id_attribute())
.as_ref() .as_ref()
@ -962,16 +963,16 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> {
} }
#[inline] #[inline]
fn is_part(&self, _name: &Atom) -> bool { fn is_part(&self, _name: &AtomIdent) -> bool {
false false
} }
fn imported_part(&self, _: &Atom) -> Option<Atom> { fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
None None
} }
#[inline] #[inline]
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
self.element.has_class_for_layout(name, case_sensitivity) self.element.has_class_for_layout(name, case_sensitivity)
} }
@ -1445,9 +1446,9 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
fn attr_matches( fn attr_matches(
&self, &self,
ns: &NamespaceConstraint<&Namespace>, ns: &NamespaceConstraint<&style::Namespace>,
local_name: &LocalName, local_name: &style::LocalName,
operation: &AttrSelectorOperation<&String>, operation: &AttrSelectorOperation<&AtomString>,
) -> bool { ) -> bool {
match *ns { match *ns {
NamespaceConstraint::Specific(ref ns) => self NamespaceConstraint::Specific(ref ns) => self
@ -1479,23 +1480,23 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> {
false false
} }
fn has_id(&self, _id: &Atom, _case_sensitivity: CaseSensitivity) -> bool { fn has_id(&self, _id: &AtomIdent, _case_sensitivity: CaseSensitivity) -> bool {
debug!("ServoThreadSafeLayoutElement::has_id called"); debug!("ServoThreadSafeLayoutElement::has_id called");
false false
} }
#[inline] #[inline]
fn is_part(&self, _name: &Atom) -> bool { fn is_part(&self, _name: &AtomIdent) -> bool {
debug!("ServoThreadSafeLayoutElement::is_part called"); debug!("ServoThreadSafeLayoutElement::is_part called");
false false
} }
fn imported_part(&self, _: &Atom) -> Option<Atom> { fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
debug!("ServoThreadSafeLayoutElement::imported_part called"); debug!("ServoThreadSafeLayoutElement::imported_part called");
None None
} }
fn has_class(&self, _name: &Atom, _case_sensitivity: CaseSensitivity) -> bool { fn has_class(&self, _name: &AtomIdent, _case_sensitivity: CaseSensitivity) -> bool {
debug!("ServoThreadSafeLayoutElement::has_class called"); debug!("ServoThreadSafeLayoutElement::has_class called");
false false
} }

View file

@ -474,6 +474,7 @@ impl LayoutThread {
// but it will be set correctly when the initial reflow takes place. // but it will be set correctly when the initial reflow takes place.
let device = Device::new( let device = Device::new(
MediaType::screen(), MediaType::screen(),
QuirksMode::NoQuirks,
window_size.initial_viewport, window_size.initial_viewport,
window_size.device_pixel_ratio, window_size.device_pixel_ratio,
); );
@ -952,7 +953,12 @@ impl LayoutThread {
ua_or_user: &ua_or_user_guard, ua_or_user: &ua_or_user_guard,
}; };
let device = Device::new(MediaType::screen(), initial_viewport, device_pixel_ratio); let device = Device::new(
MediaType::screen(),
self.stylist.quirks_mode(),
initial_viewport,
device_pixel_ratio,
);
let sheet_origins_affected_by_device_change = self.stylist.set_device(device, &guards); let sheet_origins_affected_by_device_change = self.stylist.set_device(device, &guards);
self.stylist self.stylist

View file

@ -31,7 +31,7 @@ accountable-refcell = { version = "0.2.0", optional = true }
app_units = "0.7" app_units = "0.7"
content-security-policy = { version = "0.4.0", features = ["serde"], optional = true } content-security-policy = { version = "0.4.0", features = ["serde"], optional = true }
crossbeam-channel = { version = "0.4", optional = true } crossbeam-channel = { version = "0.4", optional = true }
cssparser = "0.27" cssparser = "0.28"
euclid = "0.20" euclid = "0.20"
hashglobe = { path = "../hashglobe" } hashglobe = { path = "../hashglobe" }
hyper = { version = "0.12", optional = true } hyper = { version = "0.12", optional = true }

View file

@ -42,7 +42,7 @@ chrono = "0.4"
content-security-policy = { version = "0.4.0", features = ["serde"] } content-security-policy = { version = "0.4.0", features = ["serde"] }
cookie = "0.11" cookie = "0.11"
crossbeam-channel = "0.4" crossbeam-channel = "0.4"
cssparser = "0.27" cssparser = "0.28"
data-url = "0.1.0" data-url = "0.1.0"
deny_public_fields = { path = "../deny_public_fields" } deny_public_fields = { path = "../deny_public_fields" }
devtools_traits = { path = "../devtools_traits" } devtools_traits = { path = "../devtools_traits" }

View file

@ -21,6 +21,7 @@ use servo_atoms::Atom;
use std::borrow::ToOwned; use std::borrow::ToOwned;
use std::mem; use std::mem;
use style::attr::{AttrIdentifier, AttrValue}; use style::attr::{AttrIdentifier, AttrValue};
use style::values::GenericAtomIdent;
// https://dom.spec.whatwg.org/#interface-attr // https://dom.spec.whatwg.org/#interface-attr
#[dom_struct] #[dom_struct]
@ -46,10 +47,10 @@ impl Attr {
Attr { Attr {
node_: Node::new_inherited(document), node_: Node::new_inherited(document),
identifier: AttrIdentifier { identifier: AttrIdentifier {
local_name: local_name, local_name: GenericAtomIdent(local_name),
name: name, name: GenericAtomIdent(name),
namespace: namespace, namespace: GenericAtomIdent(namespace),
prefix: prefix, prefix: prefix.map(GenericAtomIdent),
}, },
value: DomRefCell::new(value), value: DomRefCell::new(value),
owner: MutNullableDom::new(owner), owner: MutNullableDom::new(owner),
@ -75,17 +76,17 @@ impl Attr {
#[inline] #[inline]
pub fn name(&self) -> &LocalName { pub fn name(&self) -> &LocalName {
&self.identifier.name &self.identifier.name.0
} }
#[inline] #[inline]
pub fn namespace(&self) -> &Namespace { pub fn namespace(&self) -> &Namespace {
&self.identifier.namespace &self.identifier.namespace.0
} }
#[inline] #[inline]
pub fn prefix(&self) -> Option<&Prefix> { pub fn prefix(&self) -> Option<&Prefix> {
self.identifier.prefix.as_ref() Some(&self.identifier.prefix.as_ref()?.0)
} }
} }
@ -105,7 +106,7 @@ impl AttrMethods for Attr {
// https://dom.spec.whatwg.org/#dom-attr-value // https://dom.spec.whatwg.org/#dom-attr-value
fn SetValue(&self, value: DOMString) { fn SetValue(&self, value: DOMString) {
if let Some(owner) = self.owner() { if let Some(owner) = self.owner() {
let value = owner.parse_attribute(&self.identifier.namespace, self.local_name(), value); let value = owner.parse_attribute(self.namespace(), self.local_name(), value);
self.set_value(value, &owner); self.set_value(value, &owner);
} else { } else {
*self.value.borrow_mut() = AttrValue::String(value.into()); *self.value.borrow_mut() = AttrValue::String(value.into());
@ -115,12 +116,12 @@ impl AttrMethods for Attr {
// https://dom.spec.whatwg.org/#dom-attr-name // https://dom.spec.whatwg.org/#dom-attr-name
fn Name(&self) -> DOMString { fn Name(&self) -> DOMString {
// FIXME(ajeffrey): convert directly from LocalName to DOMString // FIXME(ajeffrey): convert directly from LocalName to DOMString
DOMString::from(&*self.identifier.name) DOMString::from(&**self.name())
} }
// https://dom.spec.whatwg.org/#dom-attr-namespaceuri // https://dom.spec.whatwg.org/#dom-attr-namespaceuri
fn GetNamespaceURI(&self) -> Option<DOMString> { fn GetNamespaceURI(&self) -> Option<DOMString> {
match self.identifier.namespace { match *self.namespace() {
ns!() => None, ns!() => None,
ref url => Some(DOMString::from(&**url)), ref url => Some(DOMString::from(&**url)),
} }
@ -170,7 +171,7 @@ impl Attr {
assert_eq!(Some(owner), self.owner().as_deref()); assert_eq!(Some(owner), self.owner().as_deref());
owner.will_mutate_attr(self); owner.will_mutate_attr(self);
self.swap_value(&mut value); self.swap_value(&mut value);
if self.identifier.namespace == ns!() { if *self.namespace() == ns!() {
vtable_for(owner.upcast()) vtable_for(owner.upcast())
.attribute_mutated(self, AttributeMutation::Set(Some(&value))); .attribute_mutated(self, AttributeMutation::Set(Some(&value)));
} }
@ -196,7 +197,7 @@ impl Attr {
/// Sets the owner element. Should be called after the attribute is added /// Sets the owner element. Should be called after the attribute is added
/// or removed from its older parent. /// or removed from its older parent.
pub fn set_owner(&self, owner: Option<&Element>) { pub fn set_owner(&self, owner: Option<&Element>) {
let ns = &self.identifier.namespace; let ns = self.namespace();
match (self.owner(), owner) { match (self.owner(), owner) {
(Some(old), None) => { (Some(old), None) => {
// Already gone from the list of attributes of old owner. // Already gone from the list of attributes of old owner.
@ -218,7 +219,7 @@ impl Attr {
pub fn summarize(&self) -> AttrInfo { pub fn summarize(&self) -> AttrInfo {
AttrInfo { AttrInfo {
namespace: (*self.identifier.namespace).to_owned(), namespace: (**self.namespace()).to_owned(),
name: String::from(self.Name()), name: String::from(self.Name()),
value: String::from(self.Value()), value: String::from(self.Value()),
} }
@ -263,11 +264,11 @@ impl<'dom> AttrHelpersForLayout<'dom> for LayoutDom<'dom, Attr> {
#[inline] #[inline]
fn local_name(self) -> &'dom LocalName { fn local_name(self) -> &'dom LocalName {
unsafe { &self.unsafe_get().identifier.local_name } unsafe { &self.unsafe_get().identifier.local_name.0 }
} }
#[inline] #[inline]
fn namespace(self) -> &'dom Namespace { fn namespace(self) -> &'dom Namespace {
unsafe { &self.unsafe_get().identifier.namespace } unsafe { &self.unsafe_get().identifier.namespace.0 }
} }
} }

View file

@ -14,7 +14,7 @@ use dom_struct::dom_struct;
use style::context::QuirksMode; use style::context::QuirksMode;
use style::parser::ParserContext; use style::parser::ParserContext;
use style::stylesheets::supports_rule::{parse_condition_or_declaration, Declaration}; use style::stylesheets::supports_rule::{parse_condition_or_declaration, Declaration};
use style::stylesheets::CssRuleType; use style::stylesheets::{CssRuleType, Origin};
use style_traits::ParsingMode; use style_traits::ParsingMode;
#[dom_struct] #[dom_struct]
@ -39,7 +39,8 @@ impl CSS {
decl.push_str(&value); decl.push_str(&value);
let decl = Declaration(decl); let decl = Declaration(decl);
let url = win.Document().url(); let url = win.Document().url();
let context = ParserContext::new_for_cssom( let context = ParserContext::new(
Origin::Author,
&url, &url,
Some(CssRuleType::Style), Some(CssRuleType::Style),
ParsingMode::DEFAULT, ParsingMode::DEFAULT,
@ -60,7 +61,8 @@ impl CSS {
}; };
let url = win.Document().url(); let url = win.Document().url();
let context = ParserContext::new_for_cssom( let context = ParserContext::new(
Origin::Author,
&url, &url,
Some(CssRuleType::Style), Some(CssRuleType::Style),
ParsingMode::DEFAULT, ParsingMode::DEFAULT,

View file

@ -18,7 +18,7 @@ use servo_arc::Arc;
use style::media_queries::MediaList as StyleMediaList; use style::media_queries::MediaList as StyleMediaList;
use style::parser::ParserContext; use style::parser::ParserContext;
use style::shared_lock::{Locked, ToCssWithGuard}; use style::shared_lock::{Locked, ToCssWithGuard};
use style::stylesheets::{CssRuleType, MediaRule}; use style::stylesheets::{CssRuleType, MediaRule, Origin};
use style_traits::{ParsingMode, ToCss}; use style_traits::{ParsingMode, ToCss};
#[dom_struct] #[dom_struct]
@ -82,7 +82,8 @@ impl CSSMediaRule {
let window = global.as_window(); let window = global.as_window();
let url = window.get_url(); let url = window.get_url();
let quirks_mode = window.Document().quirks_mode(); let quirks_mode = window.Document().quirks_mode();
let context = ParserContext::new_for_cssom( let context = ParserContext::new(
Origin::Author,
&url, &url,
Some(CssRuleType::Media), Some(CssRuleType::Media),
ParsingMode::DEFAULT, ParsingMode::DEFAULT,

View file

@ -63,7 +63,7 @@ impl CSSNamespaceRuleMethods for CSSNamespaceRule {
// https://drafts.csswg.org/cssom/#dom-cssnamespacerule-namespaceuri // https://drafts.csswg.org/cssom/#dom-cssnamespacerule-namespaceuri
fn NamespaceURI(&self) -> DOMString { fn NamespaceURI(&self) -> DOMString {
let guard = self.cssrule.shared_lock().read(); let guard = self.cssrule.shared_lock().read();
(*self.namespacerule.read_with(&guard).url).into() (**self.namespacerule.read_with(&guard).url).into()
} }
} }

View file

@ -25,6 +25,7 @@ use style::properties::{
}; };
use style::selector_parser::PseudoElement; use style::selector_parser::PseudoElement;
use style::shared_lock::Locked; use style::shared_lock::Locked;
use style::stylesheets::{CssRuleType, Origin};
use style_traits::ParsingMode; use style_traits::ParsingMode;
// http://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface // http://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
@ -302,10 +303,12 @@ impl CSSStyleDeclaration {
&mut declarations, &mut declarations,
id, id,
&value, &value,
Origin::Author,
&self.owner.base_url(), &self.owner.base_url(),
window.css_error_reporter(), window.css_error_reporter(),
ParsingMode::DEFAULT, ParsingMode::DEFAULT,
quirks_mode, quirks_mode,
CssRuleType::Style,
); );
// Step 6 // Step 6
@ -461,6 +464,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
&self.owner.base_url(), &self.owner.base_url(),
window.css_error_reporter(), window.css_error_reporter(),
quirks_mode, quirks_mode,
CssRuleType::Style,
); );
}); });

View file

@ -16,7 +16,7 @@ use servo_arc::Arc;
use style::parser::ParserContext; use style::parser::ParserContext;
use style::shared_lock::{Locked, ToCssWithGuard}; use style::shared_lock::{Locked, ToCssWithGuard};
use style::stylesheets::supports_rule::SupportsCondition; use style::stylesheets::supports_rule::SupportsCondition;
use style::stylesheets::{CssRuleType, SupportsRule}; use style::stylesheets::{CssRuleType, Origin, SupportsRule};
use style_traits::{ParsingMode, ToCss}; use style_traits::{ParsingMode, ToCss};
#[dom_struct] #[dom_struct]
@ -71,7 +71,8 @@ impl CSSSupportsRule {
let win = global.as_window(); let win = global.as_window();
let url = win.Document().url(); let url = win.Document().url();
let quirks_mode = win.Document().quirks_mode(); let quirks_mode = win.Document().quirks_mode();
let context = ParserContext::new_for_cssom( let context = ParserContext::new(
Origin::Author,
&url, &url,
Some(CssRuleType::Supports), Some(CssRuleType::Supports),
ParsingMode::DEFAULT, ParsingMode::DEFAULT,

View file

@ -3454,7 +3454,12 @@ impl Document {
let window_size = self.window().window_size(); let window_size = self.window().window_size();
let viewport_size = window_size.initial_viewport; let viewport_size = window_size.initial_viewport;
let device_pixel_ratio = window_size.device_pixel_ratio; let device_pixel_ratio = window_size.device_pixel_ratio;
Device::new(MediaType::screen(), viewport_size, device_pixel_ratio) Device::new(
MediaType::screen(),
self.quirks_mode(),
viewport_size,
device_pixel_ratio,
)
} }
pub fn salvageable(&self) -> bool { pub fn salvageable(&self) -> bool {
@ -3551,8 +3556,9 @@ impl Document {
} else { } else {
snapshot.other_attributes_changed = true; snapshot.other_attributes_changed = true;
} }
if !snapshot.changed_attrs.contains(attr.local_name()) { let local_name = style::LocalName::cast(attr.local_name());
snapshot.changed_attrs.push(attr.local_name().clone()); if !snapshot.changed_attrs.contains(local_name) {
snapshot.changed_attrs.push(local_name.clone());
} }
if snapshot.attrs.is_none() { if snapshot.attrs.is_none() {
let attrs = el let attrs = el

View file

@ -129,9 +129,10 @@ use style::selector_parser::{
NonTSPseudoClass, PseudoElement, RestyleDamage, SelectorImpl, SelectorParser, NonTSPseudoClass, PseudoElement, RestyleDamage, SelectorImpl, SelectorParser,
}; };
use style::shared_lock::{Locked, SharedRwLock}; use style::shared_lock::{Locked, SharedRwLock};
use style::stylesheets::CssRuleType;
use style::thread_state; use style::thread_state;
use style::values::generics::NonNegative; use style::values::generics::NonNegative;
use style::values::{computed, specified, CSSFloat}; use style::values::{computed, specified, AtomIdent, AtomString, CSSFloat};
use style::CaseSensitivityExt; use style::CaseSensitivityExt;
use xml5ever::serialize as xmlSerialize; use xml5ever::serialize as xmlSerialize;
use xml5ever::serialize::SerializeOpts as XmlSerializeOpts; use xml5ever::serialize::SerializeOpts as XmlSerializeOpts;
@ -568,7 +569,7 @@ pub fn get_attr_for_layout<'dom>(
pub trait LayoutElementHelpers<'dom> { pub trait LayoutElementHelpers<'dom> {
fn attrs(self) -> &'dom [LayoutDom<'dom, Attr>]; fn attrs(self) -> &'dom [LayoutDom<'dom, Attr>];
fn has_class_for_layout(self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool; fn has_class_for_layout(self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool;
fn get_classes_for_layout(self) -> Option<&'dom [Atom]>; fn get_classes_for_layout(self) -> Option<&'dom [Atom]>;
fn synthesize_presentational_hints_for_legacy_attributes<V>(self, hints: &mut V) fn synthesize_presentational_hints_for_legacy_attributes<V>(self, hints: &mut V)
@ -616,7 +617,7 @@ impl<'dom> LayoutElementHelpers<'dom> for LayoutDom<'dom, Element> {
} }
#[inline] #[inline]
fn has_class_for_layout(self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { fn has_class_for_layout(self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
get_attr_for_layout(self, &ns!(), &local_name!("class")).map_or(false, |attr| { get_attr_for_layout(self, &ns!(), &local_name!("class")).map_or(false, |attr| {
attr.as_tokens() attr.as_tokens()
.unwrap() .unwrap()
@ -2851,6 +2852,7 @@ impl VirtualMethods for Element {
&doc.base_url(), &doc.base_url(),
win.css_error_reporter(), win.css_error_reporter(),
doc.quirks_mode(), doc.quirks_mode(),
CssRuleType::Style,
))) )))
}; };
@ -3135,16 +3137,16 @@ impl<'a> SelectorsElement for DomRoot<Element> {
fn attr_matches( fn attr_matches(
&self, &self,
ns: &NamespaceConstraint<&Namespace>, ns: &NamespaceConstraint<&style::Namespace>,
local_name: &LocalName, local_name: &style::LocalName,
operation: &AttrSelectorOperation<&String>, operation: &AttrSelectorOperation<&AtomString>,
) -> bool { ) -> bool {
match *ns { match *ns {
NamespaceConstraint::Specific(ref ns) => self NamespaceConstraint::Specific(ref ns) => self
.get_attribute(ns, local_name) .get_attribute(ns, local_name)
.map_or(false, |attr| attr.value().eval_selector(operation)), .map_or(false, |attr| attr.value().eval_selector(operation)),
NamespaceConstraint::Any => self.attrs.borrow().iter().any(|attr| { NamespaceConstraint::Any => self.attrs.borrow().iter().any(|attr| {
attr.local_name() == local_name && attr.value().eval_selector(operation) *attr.local_name() == **local_name && attr.value().eval_selector(operation)
}), }),
} }
} }
@ -3240,23 +3242,23 @@ impl<'a> SelectorsElement for DomRoot<Element> {
} }
} }
fn has_id(&self, id: &Atom, case_sensitivity: CaseSensitivity) -> bool { fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
self.id_attribute self.id_attribute
.borrow() .borrow()
.as_ref() .as_ref()
.map_or(false, |atom| case_sensitivity.eq_atom(id, atom)) .map_or(false, |atom| case_sensitivity.eq_atom(&*id, atom))
} }
fn is_part(&self, _name: &Atom) -> bool { fn is_part(&self, _name: &AtomIdent) -> bool {
false false
} }
fn imported_part(&self, _: &Atom) -> Option<Atom> { fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
None None
} }
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
Element::has_class(&**self, name, case_sensitivity) Element::has_class(&**self, &name, case_sensitivity)
} }
fn is_html_element_in_html_document(&self) -> bool { fn is_html_element_in_html_document(&self) -> bool {

View file

@ -39,7 +39,7 @@ use style::attr::AttrValue;
use style::media_queries::MediaList; use style::media_queries::MediaList;
use style::parser::ParserContext as CssParserContext; use style::parser::ParserContext as CssParserContext;
use style::str::HTML_SPACE_CHARACTERS; use style::str::HTML_SPACE_CHARACTERS;
use style::stylesheets::{CssRuleType, Stylesheet}; use style::stylesheets::{CssRuleType, Origin, Stylesheet};
use style_traits::ParsingMode; use style_traits::ParsingMode;
#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)] #[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
@ -310,7 +310,8 @@ impl HTMLLinkElement {
// FIXME(emilio): This looks somewhat fishy, since we use the context // FIXME(emilio): This looks somewhat fishy, since we use the context
// only to parse the media query list, CssRuleType::Media doesn't make // only to parse the media query list, CssRuleType::Media doesn't make
// much sense. // much sense.
let context = CssParserContext::new_for_cssom( let context = CssParserContext::new(
Origin::Author,
&doc_url, &doc_url,
Some(CssRuleType::Media), Some(CssRuleType::Media),
ParsingMode::DEFAULT, ParsingMode::DEFAULT,

View file

@ -96,7 +96,8 @@ impl HTMLStyleElement {
.expect("Element.textContent must be a string"); .expect("Element.textContent must be a string");
let url = window.get_url(); let url = window.get_url();
let css_error_reporter = window.css_error_reporter(); let css_error_reporter = window.css_error_reporter();
let context = CssParserContext::new_for_cssom( let context = CssParserContext::new(
Origin::Author,
&url, &url,
Some(CssRuleType::Media), Some(CssRuleType::Media),
ParsingMode::DEFAULT, ParsingMode::DEFAULT,

View file

@ -16,7 +16,7 @@ use style::media_queries::MediaList as StyleMediaList;
use style::media_queries::MediaQuery; use style::media_queries::MediaQuery;
use style::parser::ParserContext; use style::parser::ParserContext;
use style::shared_lock::{Locked, SharedRwLock}; use style::shared_lock::{Locked, SharedRwLock};
use style::stylesheets::CssRuleType; use style::stylesheets::{CssRuleType, Origin};
use style_traits::{ParsingMode, ToCss}; use style_traits::{ParsingMode, ToCss};
#[dom_struct] #[dom_struct]
@ -81,7 +81,8 @@ impl MediaListMethods for MediaList {
let window = global.as_window(); let window = global.as_window();
let url = window.get_url(); let url = window.get_url();
let quirks_mode = window.Document().quirks_mode(); let quirks_mode = window.Document().quirks_mode();
let context = ParserContext::new_for_cssom( let context = ParserContext::new(
Origin::Author,
&url, &url,
Some(CssRuleType::Media), Some(CssRuleType::Media),
ParsingMode::DEFAULT, ParsingMode::DEFAULT,
@ -122,7 +123,8 @@ impl MediaListMethods for MediaList {
let win = global.as_window(); let win = global.as_window();
let url = win.get_url(); let url = win.get_url();
let quirks_mode = win.Document().quirks_mode(); let quirks_mode = win.Document().quirks_mode();
let context = ParserContext::new_for_cssom( let context = ParserContext::new(
Origin::Author,
&url, &url,
Some(CssRuleType::Media), Some(CssRuleType::Media),
ParsingMode::DEFAULT, ParsingMode::DEFAULT,
@ -159,7 +161,8 @@ impl MediaListMethods for MediaList {
let win = global.as_window(); let win = global.as_window();
let url = win.get_url(); let url = win.get_url();
let quirks_mode = win.Document().quirks_mode(); let quirks_mode = win.Document().quirks_mode();
let context = ParserContext::new_for_cssom( let context = ParserContext::new(
Origin::Author,
&url, &url,
Some(CssRuleType::Media), Some(CssRuleType::Media),
ParsingMode::DEFAULT, ParsingMode::DEFAULT,

View file

@ -142,7 +142,7 @@ use style::properties::style_structs::Font;
use style::properties::{PropertyId, ShorthandId}; use style::properties::{PropertyId, ShorthandId};
use style::selector_parser::PseudoElement; use style::selector_parser::PseudoElement;
use style::str::HTML_SPACE_CHARACTERS; use style::str::HTML_SPACE_CHARACTERS;
use style::stylesheets::CssRuleType; use style::stylesheets::{CssRuleType, Origin};
use style_traits::{CSSPixel, DevicePixel, ParsingMode}; use style_traits::{CSSPixel, DevicePixel, ParsingMode};
use url::Position; use url::Position;
use webrender_api::units::{DeviceIntPoint, DeviceIntSize, LayoutPixel}; use webrender_api::units::{DeviceIntPoint, DeviceIntSize, LayoutPixel};
@ -1307,7 +1307,8 @@ impl WindowMethods for Window {
let mut parser = Parser::new(&mut input); let mut parser = Parser::new(&mut input);
let url = self.get_url(); let url = self.get_url();
let quirks_mode = self.Document().quirks_mode(); let quirks_mode = self.Document().quirks_mode();
let context = CssParserContext::new_for_cssom( let context = CssParserContext::new(
Origin::Author,
&url, &url,
Some(CssRuleType::Media), Some(CssRuleType::Media),
ParsingMode::DEFAULT, ParsingMode::DEFAULT,

View file

@ -19,7 +19,7 @@ bench = []
[dependencies] [dependencies]
bitflags = "1.0" bitflags = "1.0"
cssparser = "0.27" cssparser = "0.28"
derive_more = "0.99" derive_more = "0.99"
fxhash = "0.2" fxhash = "0.2"
log = "0.4" log = "0.4"
@ -27,7 +27,6 @@ phf = "0.8"
precomputed-hash = "0.1" precomputed-hash = "0.1"
servo_arc = { version = "0.1", path = "../servo_arc" } servo_arc = { version = "0.1", path = "../servo_arc" }
smallvec = "1.0" smallvec = "1.0"
thin-slice = "0.1.0"
to_shmem = { path = "../to_shmem" } to_shmem = { path = "../to_shmem" }
to_shmem_derive = { path = "../to_shmem_derive" } to_shmem_derive = { path = "../to_shmem_derive" }

View file

@ -134,7 +134,7 @@ impl AttrSelectorOperator {
} }
/// The definition of whitespace per CSS Selectors Level 3 § 4. /// The definition of whitespace per CSS Selectors Level 3 § 4.
pub static SELECTOR_WHITESPACE: &'static [char] = &[' ', '\t', '\n', '\r', '\x0C']; pub static SELECTOR_WHITESPACE: &[char] = &[' ', '\t', '\n', '\r', '\x0C'];
#[derive(Clone, Copy, Debug, Eq, PartialEq, ToShmem)] #[derive(Clone, Copy, Debug, Eq, PartialEq, ToShmem)]
pub enum ParsedCaseSensitivity { pub enum ParsedCaseSensitivity {

View file

@ -27,7 +27,7 @@ fn main() {
} }
/// <https://html.spec.whatwg.org/multipage/#selectors> /// <https://html.spec.whatwg.org/multipage/#selectors>
static ASCII_CASE_INSENSITIVE_HTML_ATTRIBUTES: &'static str = r#" static ASCII_CASE_INSENSITIVE_HTML_ATTRIBUTES: &str = r#"
accept accept
accept-charset accept-charset
align align

View file

@ -17,7 +17,7 @@
//! is non-trivial. This module encapsulates those details and presents an //! is non-trivial. This module encapsulates those details and presents an
//! easy-to-use API for the parser. //! easy-to-use API for the parser.
use crate::parser::{Combinator, Component, NonTSPseudoClass, SelectorImpl}; use crate::parser::{Combinator, Component, SelectorImpl};
use crate::sink::Push; use crate::sink::Push;
use servo_arc::{Arc, HeaderWithLength, ThinArc}; use servo_arc::{Arc, HeaderWithLength, ThinArc};
use smallvec::{self, SmallVec}; use smallvec::{self, SmallVec};
@ -322,15 +322,11 @@ where
Component::NthLastOfType(..) | Component::NthLastOfType(..) |
Component::FirstOfType | Component::FirstOfType |
Component::LastOfType | Component::LastOfType |
Component::OnlyOfType => { Component::OnlyOfType |
Component::NonTSPseudoClass(..) => {
specificity.class_like_selectors += 1; specificity.class_like_selectors += 1;
}, },
Component::NonTSPseudoClass(ref pseudo) => { Component::Negation(ref list) | Component::Is(ref list) => {
if !pseudo.has_zero_specificity() {
specificity.class_like_selectors += 1;
}
},
Component::Is(ref list) => {
// https://drafts.csswg.org/selectors/#specificity-rules: // https://drafts.csswg.org/selectors/#specificity-rules:
// //
// The specificity of an :is() pseudo-class is replaced by the // The specificity of an :is() pseudo-class is replaced by the
@ -350,11 +346,6 @@ where
Component::Namespace(..) => { Component::Namespace(..) => {
// Does not affect specificity // Does not affect specificity
}, },
Component::Negation(ref negated) => {
for ss in negated.iter() {
simple_selector_specificity(&ss, specificity);
}
},
} }
} }

View file

@ -238,10 +238,10 @@ where
where where
F: FnOnce(&mut Self) -> R, F: FnOnce(&mut Self) -> R,
{ {
debug_assert!(!self.in_negation, "Someone messed up parsing?"); let old_in_negation = self.in_negation;
self.in_negation = true; self.in_negation = true;
let result = self.nest(f); let result = self.nest(f);
self.in_negation = false; self.in_negation = old_in_negation;
result result
} }
@ -286,6 +286,6 @@ where
/// against. /// against.
#[inline] #[inline]
pub fn shadow_host(&self) -> Option<OpaqueElement> { pub fn shadow_host(&self) -> Option<OpaqueElement> {
self.current_host.clone() self.current_host
} }
} }

View file

@ -18,7 +18,6 @@ extern crate phf;
extern crate precomputed_hash; extern crate precomputed_hash;
extern crate servo_arc; extern crate servo_arc;
extern crate smallvec; extern crate smallvec;
extern crate thin_slice;
extern crate to_shmem; extern crate to_shmem;
#[macro_use] #[macro_use]
extern crate to_shmem_derive; extern crate to_shmem_derive;

View file

@ -334,10 +334,7 @@ where
let result = let result =
matches_complex_selector_internal(iter, element, context, flags_setter, Rightmost::Yes); matches_complex_selector_internal(iter, element, context, flags_setter, Rightmost::Yes);
match result { matches!(result, SelectorMatchingResult::Matched)
SelectorMatchingResult::Matched => true,
_ => false,
}
} }
#[inline] #[inline]
@ -751,7 +748,7 @@ where
&NamespaceConstraint::Specific(&crate::parser::namespace_empty_string::<E::Impl>()), &NamespaceConstraint::Specific(&crate::parser::namespace_empty_string::<E::Impl>()),
local_name, local_name,
&AttrSelectorOperation::WithValue { &AttrSelectorOperation::WithValue {
operator: operator, operator,
case_sensitivity: case_sensitivity.to_unconditional(is_html), case_sensitivity: case_sensitivity.to_unconditional(is_html),
expected_value: value, expected_value: value,
}, },
@ -780,9 +777,9 @@ where
case_sensitivity, case_sensitivity,
ref expected_value, ref expected_value,
} => AttrSelectorOperation::WithValue { } => AttrSelectorOperation::WithValue {
operator: operator, operator,
case_sensitivity: case_sensitivity.to_unconditional(is_html), case_sensitivity: case_sensitivity.to_unconditional(is_html),
expected_value: expected_value, expected_value,
}, },
}, },
) )
@ -853,14 +850,13 @@ where
} }
false false
}), }),
Component::Negation(ref negated) => context.shared.nest_for_negation(|context| { Component::Negation(ref list) => context.shared.nest_for_negation(|context| {
let mut local_context = LocalMatchingContext { for selector in &**list {
matches_hover_and_active_quirk: MatchesHoverAndActiveQuirk::No, if matches_complex_selector(selector.iter(), element, context, flags_setter) {
shared: context, return false;
}; }
!negated }
.iter() true
.all(|ss| matches_simple_selector(ss, element, &mut local_context, flags_setter))
}), }),
} }
} }
@ -912,13 +908,10 @@ where
let index = if let Some(i) = cache.as_mut().and_then(|c| c.lookup(element.opaque())) { let index = if let Some(i) = cache.as_mut().and_then(|c| c.lookup(element.opaque())) {
i i
} else { } else {
let i = nth_child_index( let i = nth_child_index(element, is_of_type, is_from_end, cache.as_deref_mut());
element, if let Some(c) = cache.as_mut() {
is_of_type, c.insert(element.opaque(), i)
is_from_end, }
cache.as_mut().map(|s| &mut **s),
);
cache.as_mut().map(|c| c.insert(element.opaque(), i));
i i
}; };
debug_assert_eq!( debug_assert_eq!(

View file

@ -37,7 +37,7 @@ pub struct NthIndexCacheInner(FxHashMap<OpaqueElement, i32>);
impl NthIndexCacheInner { impl NthIndexCacheInner {
/// Does a lookup for a given element in the cache. /// Does a lookup for a given element in the cache.
pub fn lookup(&mut self, el: OpaqueElement) -> Option<i32> { pub fn lookup(&mut self, el: OpaqueElement) -> Option<i32> {
self.0.get(&el).map(|x| *x) self.0.get(&el).copied()
} }
/// Inserts an entry into the cache. /// Inserts an entry into the cache.

View file

@ -10,18 +10,17 @@ use crate::builder::{SelectorBuilder, SelectorFlags, SpecificityAndFlags};
use crate::context::QuirksMode; use crate::context::QuirksMode;
use crate::sink::Push; use crate::sink::Push;
pub use crate::visitor::SelectorVisitor; pub use crate::visitor::SelectorVisitor;
use cssparser::{parse_nth, serialize_identifier}; use cssparser::parse_nth;
use cssparser::{BasicParseError, BasicParseErrorKind, ParseError, ParseErrorKind}; use cssparser::{BasicParseError, BasicParseErrorKind, ParseError, ParseErrorKind};
use cssparser::{CowRcStr, Delimiter, SourceLocation}; use cssparser::{CowRcStr, Delimiter, SourceLocation};
use cssparser::{CssStringWriter, Parser as CssParser, ToCss, Token}; use cssparser::{Parser as CssParser, ToCss, Token};
use precomputed_hash::PrecomputedHash; use precomputed_hash::PrecomputedHash;
use servo_arc::ThinArc; use servo_arc::ThinArc;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::borrow::{Borrow, Cow}; use std::borrow::{Borrow, Cow};
use std::fmt::{self, Debug, Display, Write}; use std::fmt::{self, Debug};
use std::iter::Rev; use std::iter::Rev;
use std::slice; use std::slice;
use thin_slice::ThinBoxedSlice;
/// A trait that represents a pseudo-element. /// A trait that represents a pseudo-element.
pub trait PseudoElement: Sized + ToCss { pub trait PseudoElement: Sized + ToCss {
@ -53,9 +52,6 @@ pub trait NonTSPseudoClass: Sized + ToCss {
/// https://drafts.csswg.org/selectors-4/#useraction-pseudos /// https://drafts.csswg.org/selectors-4/#useraction-pseudos
fn is_user_action_state(&self) -> bool; fn is_user_action_state(&self) -> bool;
/// Whether this pseudo-class has zero specificity.
fn has_zero_specificity(&self) -> bool;
fn visit<V>(&self, _visitor: &mut V) -> bool fn visit<V>(&self, _visitor: &mut V) -> bool
where where
V: SelectorVisitor<Impl = Self::Impl>, V: SelectorVisitor<Impl = Self::Impl>,
@ -79,9 +75,10 @@ fn to_ascii_lowercase(s: &str) -> Cow<str> {
bitflags! { bitflags! {
/// Flags that indicate at which point of parsing a selector are we. /// Flags that indicate at which point of parsing a selector are we.
struct SelectorParsingState: u8 { struct SelectorParsingState: u8 {
/// Whether we're inside a negation. If we're inside a negation, we're /// Whether we should avoid adding default namespaces to selectors that
/// not allowed to add another negation or such, for example. /// aren't type or universal selectors.
const INSIDE_NEGATION = 1 << 0; const SKIP_DEFAULT_NAMESPACE = 1 << 0;
/// Whether we've parsed a ::slotted() pseudo-element already. /// Whether we've parsed a ::slotted() pseudo-element already.
/// ///
/// If so, then we can only parse a subset of pseudo-elements, and /// If so, then we can only parse a subset of pseudo-elements, and
@ -165,7 +162,6 @@ pub enum SelectorParseErrorKind<'i> {
NoQualifiedNameInAttributeSelector(Token<'i>), NoQualifiedNameInAttributeSelector(Token<'i>),
EmptySelector, EmptySelector,
DanglingCombinator, DanglingCombinator,
NonSimpleSelectorInNegation,
NonCompoundSelector, NonCompoundSelector,
NonPseudoElementAfterSlotted, NonPseudoElementAfterSlotted,
InvalidPseudoElementAfterSlotted, InvalidPseudoElementAfterSlotted,
@ -183,7 +179,6 @@ pub enum SelectorParseErrorKind<'i> {
InvalidQualNameInAttr(Token<'i>), InvalidQualNameInAttr(Token<'i>),
ExplicitNamespaceUnexpectedToken(Token<'i>), ExplicitNamespaceUnexpectedToken(Token<'i>),
ClassNeedsIdent(Token<'i>), ClassNeedsIdent(Token<'i>),
EmptyNegation,
} }
macro_rules! with_all_bounds { macro_rules! with_all_bounds {
@ -202,8 +197,6 @@ macro_rules! with_all_bounds {
type ExtraMatchingData: Sized + Default + 'static; type ExtraMatchingData: Sized + Default + 'static;
type AttrValue: $($InSelector)*; type AttrValue: $($InSelector)*;
type Identifier: $($InSelector)*; type Identifier: $($InSelector)*;
type ClassName: $($InSelector)*;
type PartName: $($InSelector)*;
type LocalName: $($InSelector)* + Borrow<Self::BorrowedLocalName>; type LocalName: $($InSelector)* + Borrow<Self::BorrowedLocalName>;
type NamespaceUrl: $($CommonBounds)* + Default + Borrow<Self::BorrowedNamespaceUrl>; type NamespaceUrl: $($CommonBounds)* + Default + Borrow<Self::BorrowedNamespaceUrl>;
type NamespacePrefix: $($InSelector)* + Default; type NamespacePrefix: $($InSelector)* + Default;
@ -223,7 +216,7 @@ macro_rules! with_all_bounds {
macro_rules! with_bounds { macro_rules! with_bounds {
( [ $( $CommonBounds: tt )* ] [ $( $FromStr: tt )* ]) => { ( [ $( $CommonBounds: tt )* ] [ $( $FromStr: tt )* ]) => {
with_all_bounds! { with_all_bounds! {
[$($CommonBounds)* + $($FromStr)* + Display] [$($CommonBounds)* + $($FromStr)* + ToCss]
[$($CommonBounds)*] [$($CommonBounds)*]
[$($FromStr)*] [$($FromStr)*]
} }
@ -254,6 +247,16 @@ pub trait Parser<'i> {
false false
} }
/// The error recovery that selector lists inside :is() and :where() have.
fn is_and_where_error_recovery(&self) -> ParseErrorRecovery {
ParseErrorRecovery::IgnoreInvalidSelector
}
/// Whether the given function name is an alias for the `:is()` function.
fn is_is_alias(&self, _name: &str) -> bool {
false
}
/// Whether to parse the `:host` pseudo-class. /// Whether to parse the `:host` pseudo-class.
fn parse_host(&self) -> bool { fn parse_host(&self) -> bool {
false false
@ -327,6 +330,17 @@ pub struct SelectorList<Impl: SelectorImpl>(
#[shmem(field_bound)] pub SmallVec<[Selector<Impl>; 1]>, #[shmem(field_bound)] pub SmallVec<[Selector<Impl>; 1]>,
); );
/// How to treat invalid selectors in a selector list.
pub enum ParseErrorRecovery {
/// Discard the entire selector list, this is the default behavior for
/// almost all of CSS.
DiscardList,
/// Ignore invalid selectors, potentially creating an empty selector list.
///
/// This is the error recovery mode of :is() and :where()
IgnoreInvalidSelector,
}
impl<Impl: SelectorImpl> SelectorList<Impl> { impl<Impl: SelectorImpl> SelectorList<Impl> {
/// Parse a comma-separated list of Selectors. /// Parse a comma-separated list of Selectors.
/// <https://drafts.csswg.org/selectors/#grouping> /// <https://drafts.csswg.org/selectors/#grouping>
@ -339,26 +353,47 @@ impl<Impl: SelectorImpl> SelectorList<Impl> {
where where
P: Parser<'i, Impl = Impl>, P: Parser<'i, Impl = Impl>,
{ {
Self::parse_with_state(parser, input, SelectorParsingState::empty()) Self::parse_with_state(
parser,
input,
SelectorParsingState::empty(),
ParseErrorRecovery::DiscardList,
)
} }
#[inline]
fn parse_with_state<'i, 't, P>( fn parse_with_state<'i, 't, P>(
parser: &P, parser: &P,
input: &mut CssParser<'i, 't>, input: &mut CssParser<'i, 't>,
state: SelectorParsingState, state: SelectorParsingState,
recovery: ParseErrorRecovery,
) -> Result<Self, ParseError<'i, P::Error>> ) -> Result<Self, ParseError<'i, P::Error>>
where where
P: Parser<'i, Impl = Impl>, P: Parser<'i, Impl = Impl>,
{ {
let mut values = SmallVec::new(); let mut values = SmallVec::new();
loop { loop {
values.push(input.parse_until_before(Delimiter::Comma, |input| { let selector = input.parse_until_before(Delimiter::Comma, |input| {
parse_selector(parser, input, state) parse_selector(parser, input, state)
})?); });
match input.next() {
Err(_) => return Ok(SelectorList(values)), let was_ok = selector.is_ok();
Ok(&Token::Comma) => continue, match selector {
Ok(_) => unreachable!(), Ok(selector) => values.push(selector),
Err(err) => match recovery {
ParseErrorRecovery::DiscardList => return Err(err),
ParseErrorRecovery::IgnoreInvalidSelector => {},
},
}
loop {
match input.next() {
Err(_) => return Ok(SelectorList(values)),
Ok(&Token::Comma) => break,
Ok(_) => {
debug_assert!(!was_ok, "Shouldn't have got a selector if getting here");
},
}
} }
} }
} }
@ -386,22 +421,6 @@ where
) )
} }
/// Parse a comma separated list of compound selectors.
pub fn parse_compound_selector_list<'i, 't, P, Impl>(
parser: &P,
input: &mut CssParser<'i, 't>,
) -> Result<Box<[Selector<Impl>]>, ParseError<'i, P::Error>>
where
P: Parser<'i, Impl = Impl>,
Impl: SelectorImpl,
{
input
.parse_comma_separated(|input| {
parse_inner_compound_selector(parser, input, SelectorParsingState::empty())
})
.map(|selectors| selectors.into_boxed_slice())
}
/// Ancestor hashes for the bloom filter. We precompute these and store them /// Ancestor hashes for the bloom filter. We precompute these and store them
/// inline with selectors to optimize cache performance during matching. /// inline with selectors to optimize cache performance during matching.
/// This matters a lot. /// This matters a lot.
@ -430,7 +449,6 @@ fn collect_ancestor_hashes<Impl: SelectorImpl>(
) -> bool ) -> bool
where where
Impl::Identifier: PrecomputedHash, Impl::Identifier: PrecomputedHash,
Impl::ClassName: PrecomputedHash,
Impl::LocalName: PrecomputedHash, Impl::LocalName: PrecomputedHash,
Impl::NamespaceUrl: PrecomputedHash, Impl::NamespaceUrl: PrecomputedHash,
{ {
@ -461,10 +479,10 @@ where
// :where and :is OR their selectors, so we can't put any hash // :where and :is OR their selectors, so we can't put any hash
// in the filter if there's more than one selector, as that'd // in the filter if there's more than one selector, as that'd
// exclude elements that may match one of the other selectors. // exclude elements that may match one of the other selectors.
if list.len() == 1 { if list.len() == 1 &&
if !collect_ancestor_hashes(list[0].iter(), quirks_mode, hashes, len) { !collect_ancestor_hashes(list[0].iter(), quirks_mode, hashes, len)
return false; {
} return false;
} }
continue; continue;
}, },
@ -484,7 +502,6 @@ impl AncestorHashes {
pub fn new<Impl: SelectorImpl>(selector: &Selector<Impl>, quirks_mode: QuirksMode) -> Self pub fn new<Impl: SelectorImpl>(selector: &Selector<Impl>, quirks_mode: QuirksMode) -> Self
where where
Impl::Identifier: PrecomputedHash, Impl::Identifier: PrecomputedHash,
Impl::ClassName: PrecomputedHash,
Impl::LocalName: PrecomputedHash, Impl::LocalName: PrecomputedHash,
Impl::NamespaceUrl: PrecomputedHash, Impl::NamespaceUrl: PrecomputedHash,
{ {
@ -563,7 +580,7 @@ impl<Impl: SelectorImpl> Selector<Impl> {
} }
#[inline] #[inline]
pub fn parts(&self) -> Option<&[Impl::PartName]> { pub fn parts(&self) -> Option<&[Impl::Identifier]> {
if !self.is_part() { if !self.is_part() {
return None; return None;
} }
@ -659,7 +676,7 @@ impl<Impl: SelectorImpl> Selector<Impl> {
pub fn iter_from(&self, offset: usize) -> SelectorIter<Impl> { pub fn iter_from(&self, offset: usize) -> SelectorIter<Impl> {
let iter = self.0.slice[offset..].iter(); let iter = self.0.slice[offset..].iter();
SelectorIter { SelectorIter {
iter: iter, iter,
next_combinator: None, next_combinator: None,
} }
} }
@ -982,7 +999,7 @@ pub enum Component<Impl: SelectorImpl> {
LocalName(LocalName<Impl>), LocalName(LocalName<Impl>),
ID(#[shmem(field_bound)] Impl::Identifier), ID(#[shmem(field_bound)] Impl::Identifier),
Class(#[shmem(field_bound)] Impl::ClassName), Class(#[shmem(field_bound)] Impl::Identifier),
AttributeInNoNamespaceExists { AttributeInNoNamespaceExists {
#[shmem(field_bound)] #[shmem(field_bound)]
@ -1002,16 +1019,7 @@ pub enum Component<Impl: SelectorImpl> {
AttributeOther(Box<AttrSelectorWithOptionalNamespace<Impl>>), AttributeOther(Box<AttrSelectorWithOptionalNamespace<Impl>>),
/// Pseudo-classes /// Pseudo-classes
/// Negation(Box<[Selector<Impl>]>),
/// CSS3 Negation only takes a simple simple selector, but we still need to
/// treat it as a compound selector because it might be a type selector
/// which we represent as a namespace and a localname.
///
/// Note: if/when we upgrade this to CSS4, which supports combinators, we
/// need to think about how this should interact with
/// visit_complex_selector, and what the consumers of those APIs should do
/// about the presence of combinators in negation.
Negation(ThinBoxedSlice<Component<Impl>>),
FirstChild, FirstChild,
LastChild, LastChild,
OnlyChild, OnlyChild,
@ -1040,7 +1048,7 @@ pub enum Component<Impl: SelectorImpl> {
Slotted(Selector<Impl>), Slotted(Selector<Impl>),
/// The `::part` pseudo-element. /// The `::part` pseudo-element.
/// https://drafts.csswg.org/css-shadow-parts/#part /// https://drafts.csswg.org/css-shadow-parts/#part
Part(#[shmem(field_bound)] Box<[Impl::PartName]>), Part(#[shmem(field_bound)] Box<[Impl::Identifier]>),
/// The `:host` pseudo-class: /// The `:host` pseudo-class:
/// ///
/// https://drafts.csswg.org/css-scoping/#host-selector /// https://drafts.csswg.org/css-scoping/#host-selector
@ -1087,16 +1095,13 @@ impl<Impl: SelectorImpl> Component<Impl> {
pub fn maybe_allowed_after_pseudo_element(&self) -> bool { pub fn maybe_allowed_after_pseudo_element(&self) -> bool {
match *self { match *self {
Component::NonTSPseudoClass(..) => true, Component::NonTSPseudoClass(..) => true,
Component::Negation(ref components) => components Component::Negation(ref selectors) |
.iter() Component::Is(ref selectors) |
.all(|c| c.maybe_allowed_after_pseudo_element()), Component::Where(ref selectors) => selectors.iter().all(|selector| {
Component::Is(ref selectors) | Component::Where(ref selectors) => { selector
selectors.iter().all(|selector| { .iter_raw_match_order()
selector .all(|c| c.maybe_allowed_after_pseudo_element())
.iter_raw_match_order() }),
.all(|c| c.maybe_allowed_after_pseudo_element())
})
},
_ => false, _ => false,
} }
} }
@ -1114,9 +1119,11 @@ impl<Impl: SelectorImpl> Component<Impl> {
*self *self
); );
match *self { match *self {
Component::Negation(ref components) => !components Component::Negation(ref selectors) => !selectors.iter().all(|selector| {
.iter() selector
.all(|c| c.matches_for_stateless_pseudo_element()), .iter_raw_match_order()
.all(|c| c.matches_for_stateless_pseudo_element())
}),
Component::Is(ref selectors) | Component::Where(ref selectors) => { Component::Is(ref selectors) | Component::Where(ref selectors) => {
selectors.iter().any(|selector| { selectors.iter().any(|selector| {
selector selector
@ -1148,14 +1155,6 @@ impl<Impl: SelectorImpl> Component<Impl> {
return false; return false;
} }
}, },
Negation(ref negated) => {
for component in negated.iter() {
if !component.visit(visitor) {
return false;
}
}
},
AttributeInNoNamespaceExists { AttributeInNoNamespaceExists {
ref local_name, ref local_name,
ref local_name_lower, ref local_name_lower,
@ -1205,7 +1204,7 @@ impl<Impl: SelectorImpl> Component<Impl> {
} }
}, },
Is(ref list) | Where(ref list) => { Negation(ref list) | Is(ref list) | Where(ref list) => {
if !visitor.visit_selector_list(&list) { if !visitor.visit_selector_list(&list) {
return false; return false;
} }
@ -1249,18 +1248,18 @@ impl<Impl: SelectorImpl> Debug for LocalName<Impl> {
} }
} }
fn serialize_selector_list<'a, Impl, I, W>(mut iter: I, dest: &mut W) -> fmt::Result fn serialize_selector_list<'a, Impl, I, W>(iter: I, dest: &mut W) -> fmt::Result
where where
Impl: SelectorImpl, Impl: SelectorImpl,
I: Iterator<Item = &'a Selector<Impl>>, I: Iterator<Item = &'a Selector<Impl>>,
W: fmt::Write, W: fmt::Write,
{ {
let first = iter let mut first = true;
.next()
.expect("Empty SelectorList, should contain at least one selector");
first.to_css(dest)?;
for selector in iter { for selector in iter {
dest.write_str(", ")?; if !first {
dest.write_str(", ")?;
}
first = false;
selector.to_css(dest)?; selector.to_css(dest)?;
} }
Ok(()) Ok(())
@ -1454,18 +1453,18 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
if i != 0 { if i != 0 {
dest.write_char(' ')?; dest.write_char(' ')?;
} }
display_to_css_identifier(name, dest)?; name.to_css(dest)?;
} }
dest.write_char(')') dest.write_char(')')
}, },
PseudoElement(ref p) => p.to_css(dest), PseudoElement(ref p) => p.to_css(dest),
ID(ref s) => { ID(ref s) => {
dest.write_char('#')?; dest.write_char('#')?;
display_to_css_identifier(s, dest) s.to_css(dest)
}, },
Class(ref s) => { Class(ref s) => {
dest.write_char('.')?; dest.write_char('.')?;
display_to_css_identifier(s, dest) s.to_css(dest)
}, },
LocalName(ref s) => s.to_css(dest), LocalName(ref s) => s.to_css(dest),
ExplicitUniversalType => dest.write_char('*'), ExplicitUniversalType => dest.write_char('*'),
@ -1474,13 +1473,13 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
ExplicitNoNamespace => dest.write_char('|'), ExplicitNoNamespace => dest.write_char('|'),
ExplicitAnyNamespace => dest.write_str("*|"), ExplicitAnyNamespace => dest.write_str("*|"),
Namespace(ref prefix, _) => { Namespace(ref prefix, _) => {
display_to_css_identifier(prefix, dest)?; prefix.to_css(dest)?;
dest.write_char('|') dest.write_char('|')
}, },
AttributeInNoNamespaceExists { ref local_name, .. } => { AttributeInNoNamespaceExists { ref local_name, .. } => {
dest.write_char('[')?; dest.write_char('[')?;
display_to_css_identifier(local_name, dest)?; local_name.to_css(dest)?;
dest.write_char(']') dest.write_char(']')
}, },
AttributeInNoNamespace { AttributeInNoNamespace {
@ -1491,10 +1490,10 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
.. ..
} => { } => {
dest.write_char('[')?; dest.write_char('[')?;
display_to_css_identifier(local_name, dest)?; local_name.to_css(dest)?;
operator.to_css(dest)?; operator.to_css(dest)?;
dest.write_char('"')?; dest.write_char('"')?;
write!(CssStringWriter::new(dest), "{}", value)?; value.to_css(dest)?;
dest.write_char('"')?; dest.write_char('"')?;
match case_sensitivity { match case_sensitivity {
ParsedCaseSensitivity::CaseSensitive | ParsedCaseSensitivity::CaseSensitive |
@ -1507,14 +1506,6 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
AttributeOther(ref attr_selector) => attr_selector.to_css(dest), AttributeOther(ref attr_selector) => attr_selector.to_css(dest),
// Pseudo-classes // Pseudo-classes
Negation(ref arg) => {
dest.write_str(":not(")?;
for component in arg.iter() {
component.to_css(dest)?;
}
dest.write_str(")")
},
FirstChild => dest.write_str(":first-child"), FirstChild => dest.write_str(":first-child"),
LastChild => dest.write_str(":last-child"), LastChild => dest.write_str(":last-child"),
OnlyChild => dest.write_str(":only-child"), OnlyChild => dest.write_str(":only-child"),
@ -1544,10 +1535,11 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
write_affine(dest, a, b)?; write_affine(dest, a, b)?;
dest.write_char(')') dest.write_char(')')
}, },
Is(ref list) | Where(ref list) => { Is(ref list) | Where(ref list) | Negation(ref list) => {
match *self { match *self {
Where(..) => dest.write_str(":where(")?, Where(..) => dest.write_str(":where(")?,
Is(..) => dest.write_str(":is(")?, Is(..) => dest.write_str(":is(")?,
Negation(..) => dest.write_str(":not(")?,
_ => unreachable!(), _ => unreachable!(),
} }
serialize_selector_list(list.iter(), dest)?; serialize_selector_list(list.iter(), dest)?;
@ -1566,13 +1558,13 @@ impl<Impl: SelectorImpl> ToCss for AttrSelectorWithOptionalNamespace<Impl> {
dest.write_char('[')?; dest.write_char('[')?;
match self.namespace { match self.namespace {
Some(NamespaceConstraint::Specific((ref prefix, _))) => { Some(NamespaceConstraint::Specific((ref prefix, _))) => {
display_to_css_identifier(prefix, dest)?; prefix.to_css(dest)?;
dest.write_char('|')? dest.write_char('|')?
}, },
Some(NamespaceConstraint::Any) => dest.write_str("*|")?, Some(NamespaceConstraint::Any) => dest.write_str("*|")?,
None => {}, None => {},
} }
display_to_css_identifier(&self.local_name, dest)?; self.local_name.to_css(dest)?;
match self.operation { match self.operation {
ParsedAttrSelectorOperation::Exists => {}, ParsedAttrSelectorOperation::Exists => {},
ParsedAttrSelectorOperation::WithValue { ParsedAttrSelectorOperation::WithValue {
@ -1582,7 +1574,7 @@ impl<Impl: SelectorImpl> ToCss for AttrSelectorWithOptionalNamespace<Impl> {
} => { } => {
operator.to_css(dest)?; operator.to_css(dest)?;
dest.write_char('"')?; dest.write_char('"')?;
write!(CssStringWriter::new(dest), "{}", expected_value)?; expected_value.to_css(dest)?;
dest.write_char('"')?; dest.write_char('"')?;
match case_sensitivity { match case_sensitivity {
ParsedCaseSensitivity::CaseSensitive | ParsedCaseSensitivity::CaseSensitive |
@ -1601,29 +1593,10 @@ impl<Impl: SelectorImpl> ToCss for LocalName<Impl> {
where where
W: fmt::Write, W: fmt::Write,
{ {
display_to_css_identifier(&self.name, dest) self.name.to_css(dest)
} }
} }
/// Serialize the output of Display as a CSS identifier
fn display_to_css_identifier<T: Display, W: fmt::Write>(x: &T, dest: &mut W) -> fmt::Result {
// FIXME(SimonSapin): it is possible to avoid this heap allocation
// by creating a stream adapter like cssparser::CssStringWriter
// that holds and writes to `&mut W` and itself implements `fmt::Write`.
//
// I havent done this yet because it would require somewhat complex and fragile state machine
// to support in `fmt::Write::write_char` cases that,
// in `serialize_identifier` (which has the full value as a `&str` slice),
// can be expressed as
// `string.starts_with("--")`, `string == "-"`, `string.starts_with("-")`, etc.
//
// And I dont even know if this would be a performance win: jemalloc is good at what it does
// and the state machine might be slower than `serialize_identifier` as currently written.
let string = x.to_string();
serialize_identifier(&string, dest)
}
/// Build up a Selector. /// Build up a Selector.
/// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ; /// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ;
/// ///
@ -1794,7 +1767,7 @@ enum SimpleSelectorParseResult<Impl: SelectorImpl> {
SimpleSelector(Component<Impl>), SimpleSelector(Component<Impl>),
PseudoElement(Impl::PseudoElement), PseudoElement(Impl::PseudoElement),
SlottedPseudo(Selector<Impl>), SlottedPseudo(Selector<Impl>),
PartPseudo(Box<[Impl::PartName]>), PartPseudo(Box<[Impl::Identifier]>),
} }
#[derive(Debug)] #[derive(Debug)]
@ -1959,16 +1932,16 @@ where
return Ok(Component::AttributeOther(Box::new( return Ok(Component::AttributeOther(Box::new(
AttrSelectorWithOptionalNamespace { AttrSelectorWithOptionalNamespace {
namespace: Some(namespace), namespace: Some(namespace),
local_name: local_name, local_name,
local_name_lower: local_name_lower, local_name_lower,
operation: ParsedAttrSelectorOperation::Exists, operation: ParsedAttrSelectorOperation::Exists,
never_matches: false, never_matches: false,
}, },
))); )));
} else { } else {
return Ok(Component::AttributeInNoNamespaceExists { return Ok(Component::AttributeInNoNamespaceExists {
local_name: local_name, local_name,
local_name_lower: local_name_lower, local_name_lower,
}); });
} }
}, },
@ -2032,19 +2005,19 @@ where
local_name_lower, local_name_lower,
never_matches, never_matches,
operation: ParsedAttrSelectorOperation::WithValue { operation: ParsedAttrSelectorOperation::WithValue {
operator: operator, operator,
case_sensitivity: case_sensitivity, case_sensitivity,
expected_value: value, expected_value: value,
}, },
}, },
))) )))
} else { } else {
Ok(Component::AttributeInNoNamespace { Ok(Component::AttributeInNoNamespace {
local_name: local_name, local_name,
operator: operator, operator,
value: value, value,
case_sensitivity: case_sensitivity, case_sensitivity,
never_matches: never_matches, never_matches,
}) })
} }
} }
@ -2118,44 +2091,16 @@ where
P: Parser<'i, Impl = Impl>, P: Parser<'i, Impl = Impl>,
Impl: SelectorImpl, Impl: SelectorImpl,
{ {
let state = state | SelectorParsingState::INSIDE_NEGATION; let list = SelectorList::parse_with_state(
parser,
input,
state |
SelectorParsingState::SKIP_DEFAULT_NAMESPACE |
SelectorParsingState::DISALLOW_PSEUDOS,
ParseErrorRecovery::DiscardList,
)?;
// We use a sequence because a type selector may be represented as two Components. Ok(Component::Negation(list.0.into_vec().into_boxed_slice()))
let mut sequence = SmallVec::<[Component<Impl>; 2]>::new();
input.skip_whitespace();
// Get exactly one simple selector. The parse logic in the caller will verify
// that there are no trailing tokens after we're done.
let is_type_sel = match parse_type_selector(parser, input, state, &mut sequence) {
Ok(result) => result,
Err(ParseError {
kind: ParseErrorKind::Basic(BasicParseErrorKind::EndOfInput),
..
}) => return Err(input.new_custom_error(SelectorParseErrorKind::EmptyNegation)),
Err(e) => return Err(e.into()),
};
if !is_type_sel {
match parse_one_simple_selector(parser, input, state)? {
Some(SimpleSelectorParseResult::SimpleSelector(s)) => {
sequence.push(s);
},
None => {
return Err(input.new_custom_error(SelectorParseErrorKind::EmptyNegation));
},
Some(SimpleSelectorParseResult::PseudoElement(_)) |
Some(SimpleSelectorParseResult::PartPseudo(_)) |
Some(SimpleSelectorParseResult::SlottedPseudo(_)) => {
let e = SelectorParseErrorKind::NonSimpleSelectorInNegation;
return Err(input.new_custom_error(e));
},
}
}
// Success.
Ok(Component::Negation(
sequence.into_vec().into_boxed_slice().into(),
))
} }
/// simple_selector_sequence /// simple_selector_sequence
@ -2191,7 +2136,8 @@ where
if let Some(url) = parser.default_namespace() { if let Some(url) = parser.default_namespace() {
// If there was no explicit type selector, but there is a // If there was no explicit type selector, but there is a
// default namespace, there is an implicit "<defaultns>|*" type // default namespace, there is an implicit "<defaultns>|*" type
// selector. Except for :host, where we ignore it. // selector. Except for :host() or :not() / :is() / :where(),
// where we ignore it.
// //
// https://drafts.csswg.org/css-scoping/#host-element-in-tree: // https://drafts.csswg.org/css-scoping/#host-element-in-tree:
// //
@ -2206,10 +2152,22 @@ where
// given selector is allowed to match a featureless element, // given selector is allowed to match a featureless element,
// it must do so while ignoring the default namespace. // it must do so while ignoring the default namespace.
// //
if !matches!( // https://drafts.csswg.org/selectors-4/#matches
result, //
SimpleSelectorParseResult::SimpleSelector(Component::Host(..)) // Default namespace declarations do not affect the compound
) { // selector representing the subject of any selector within
// a :is() pseudo-class, unless that compound selector
// contains an explicit universal selector or type selector.
//
// (Similar quotes for :where() / :not())
//
let ignore_default_ns = state
.intersects(SelectorParsingState::SKIP_DEFAULT_NAMESPACE) ||
matches!(
result,
SimpleSelectorParseResult::SimpleSelector(Component::Host(..))
);
if !ignore_default_ns {
builder.push_simple_selector(Component::DefaultNamespace(url)); builder.push_simple_selector(Component::DefaultNamespace(url));
} }
} }
@ -2263,7 +2221,10 @@ where
let inner = SelectorList::parse_with_state( let inner = SelectorList::parse_with_state(
parser, parser,
input, input,
state | SelectorParsingState::DISALLOW_PSEUDOS, state |
SelectorParsingState::SKIP_DEFAULT_NAMESPACE |
SelectorParsingState::DISALLOW_PSEUDOS,
parser.is_and_where_error_recovery(),
)?; )?;
Ok(component(inner.0.into_vec().into_boxed_slice())) Ok(component(inner.0.into_vec().into_boxed_slice()))
} }
@ -2292,16 +2253,15 @@ where
return Ok(Component::Host(Some(parse_inner_compound_selector(parser, input, state)?))); return Ok(Component::Host(Some(parse_inner_compound_selector(parser, input, state)?)));
}, },
"not" => { "not" => {
if state.intersects(SelectorParsingState::INSIDE_NEGATION) {
return Err(input.new_custom_error(
SelectorParseErrorKind::UnexpectedIdent("not".into())
));
}
return parse_negation(parser, input, state) return parse_negation(parser, input, state)
}, },
_ => {} _ => {}
} }
if parser.parse_is_and_where() && parser.is_is_alias(&name) {
return parse_is_or_where(parser, input, state, Component::Is);
}
if !state.allows_custom_functional_pseudo_classes() { if !state.allows_custom_functional_pseudo_classes() {
return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState)); return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
} }
@ -2555,11 +2515,6 @@ pub mod tests {
fn is_user_action_state(&self) -> bool { fn is_user_action_state(&self) -> bool {
self.is_active_or_hover() self.is_active_or_hover()
} }
#[inline]
fn has_zero_specificity(&self) -> bool {
false
}
} }
impl ToCss for PseudoClass { impl ToCss for PseudoClass {
@ -2611,10 +2566,8 @@ pub mod tests {
impl SelectorImpl for DummySelectorImpl { impl SelectorImpl for DummySelectorImpl {
type ExtraMatchingData = (); type ExtraMatchingData = ();
type AttrValue = DummyAtom; type AttrValue = DummyAttrValue;
type Identifier = DummyAtom; type Identifier = DummyAtom;
type ClassName = DummyAtom;
type PartName = DummyAtom;
type LocalName = DummyAtom; type LocalName = DummyAtom;
type NamespaceUrl = DummyAtom; type NamespaceUrl = DummyAtom;
type NamespacePrefix = DummyAtom; type NamespacePrefix = DummyAtom;
@ -2624,12 +2577,35 @@ pub mod tests {
type PseudoElement = PseudoElement; type PseudoElement = PseudoElement;
} }
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
pub struct DummyAttrValue(String);
impl ToCss for DummyAttrValue {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
where
W: fmt::Write,
{
use std::fmt::Write;
write!(cssparser::CssStringWriter::new(dest), "{}", &self.0)
}
}
impl<'a> From<&'a str> for DummyAttrValue {
fn from(string: &'a str) -> Self {
Self(string.into())
}
}
#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)]
pub struct DummyAtom(String); pub struct DummyAtom(String);
impl fmt::Display for DummyAtom { impl ToCss for DummyAtom {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fn to_css<W>(&self, dest: &mut W) -> fmt::Result
<String as fmt::Display>::fmt(&self.0, fmt) where
W: fmt::Write,
{
serialize_identifier(&self.0, dest)
} }
} }
@ -2657,6 +2633,10 @@ pub mod tests {
true true
} }
fn is_and_where_error_recovery(&self) -> ParseErrorRecovery {
ParseErrorRecovery::DiscardList
}
fn parse_part(&self) -> bool { fn parse_part(&self) -> bool {
true true
} }
@ -2777,8 +2757,8 @@ pub mod tests {
assert!(list.is_ok()); assert!(list.is_ok());
} }
const MATHML: &'static str = "http://www.w3.org/1998/Math/MathML"; const MATHML: &str = "http://www.w3.org/1998/Math/MathML";
const SVG: &'static str = "http://www.w3.org/2000/svg"; const SVG: &str = "http://www.w3.org/2000/svg";
#[test] #[test]
fn test_parsing() { fn test_parsing() {
@ -3042,9 +3022,12 @@ pub mod tests {
vec![ vec![
Component::DefaultNamespace(MATHML.into()), Component::DefaultNamespace(MATHML.into()),
Component::Negation( Component::Negation(
vec![Component::Class(DummyAtom::from("cl"))] vec![Selector::from_vec(
.into_boxed_slice() vec![Component::Class(DummyAtom::from("cl"))],
.into(), specificity(0, 1, 0),
Default::default(),
)]
.into_boxed_slice()
), ),
], ],
specificity(0, 1, 0), specificity(0, 1, 0),
@ -3057,12 +3040,15 @@ pub mod tests {
vec![ vec![
Component::DefaultNamespace(MATHML.into()), Component::DefaultNamespace(MATHML.into()),
Component::Negation( Component::Negation(
vec![ vec![Selector::from_vec(
Component::DefaultNamespace(MATHML.into()), vec![
Component::ExplicitUniversalType, Component::DefaultNamespace(MATHML.into()),
] Component::ExplicitUniversalType,
.into_boxed_slice() ],
.into(), specificity(0, 0, 0),
Default::default(),
)]
.into_boxed_slice(),
), ),
], ],
specificity(0, 0, 0), specificity(0, 0, 0),
@ -3075,15 +3061,18 @@ pub mod tests {
vec![ vec![
Component::DefaultNamespace(MATHML.into()), Component::DefaultNamespace(MATHML.into()),
Component::Negation( Component::Negation(
vec![ vec![Selector::from_vec(
Component::DefaultNamespace(MATHML.into()), vec![
Component::LocalName(LocalName { Component::DefaultNamespace(MATHML.into()),
name: DummyAtom::from("e"), Component::LocalName(LocalName {
lower_name: DummyAtom::from("e"), name: DummyAtom::from("e"),
}), lower_name: DummyAtom::from("e"),
] }),
],
specificity(0, 0, 1),
Default::default(),
),]
.into_boxed_slice() .into_boxed_slice()
.into(),
), ),
], ],
specificity(0, 0, 1), specificity(0, 0, 1),
@ -3096,7 +3085,7 @@ pub mod tests {
vec![Component::AttributeInNoNamespace { vec![Component::AttributeInNoNamespace {
local_name: DummyAtom::from("attr"), local_name: DummyAtom::from("attr"),
operator: AttrSelectorOperator::DashMatch, operator: AttrSelectorOperator::DashMatch,
value: DummyAtom::from("foo"), value: DummyAttrValue::from("foo"),
never_matches: false, never_matches: false,
case_sensitivity: ParsedCaseSensitivity::CaseSensitive, case_sensitivity: ParsedCaseSensitivity::CaseSensitive,
}], }],
@ -3178,47 +3167,20 @@ pub mod tests {
)])) )]))
); );
parser.default_ns = None; parser.default_ns = None;
assert!(parse(":not(#provel.old)").is_err()); assert!(parse(":not(#provel.old)").is_ok());
assert!(parse(":not(#provel > old)").is_err()); assert!(parse(":not(#provel > old)").is_ok());
assert!(parse("table[rules]:not([rules=\"none\"]):not([rules=\"\"])").is_ok()); assert!(parse("table[rules]:not([rules=\"none\"]):not([rules=\"\"])").is_ok());
assert_eq!(
parse(":not(#provel)"),
Ok(SelectorList::from_vec(vec![Selector::from_vec(
vec![Component::Negation(
vec![Component::ID(DummyAtom::from("provel"))]
.into_boxed_slice()
.into(),
)],
specificity(1, 0, 0),
Default::default(),
)]))
);
assert_eq!(
parse_ns(":not(svg|circle)", &parser),
Ok(SelectorList::from_vec(vec![Selector::from_vec(
vec![Component::Negation(
vec![
Component::Namespace(DummyAtom("svg".into()), SVG.into()),
Component::LocalName(LocalName {
name: DummyAtom::from("circle"),
lower_name: DummyAtom::from("circle"),
}),
]
.into_boxed_slice()
.into(),
)],
specificity(0, 0, 1),
Default::default(),
)]))
);
// https://github.com/servo/servo/issues/16017 // https://github.com/servo/servo/issues/16017
assert_eq!( assert_eq!(
parse_ns(":not(*)", &parser), parse_ns(":not(*)", &parser),
Ok(SelectorList::from_vec(vec![Selector::from_vec( Ok(SelectorList::from_vec(vec![Selector::from_vec(
vec![Component::Negation( vec![Component::Negation(
vec![Component::ExplicitUniversalType] vec![Selector::from_vec(
.into_boxed_slice() vec![Component::ExplicitUniversalType],
.into(), specificity(0, 0, 0),
Default::default(),
)]
.into_boxed_slice()
)], )],
specificity(0, 0, 0), specificity(0, 0, 0),
Default::default(), Default::default(),
@ -3228,12 +3190,15 @@ pub mod tests {
parse_ns(":not(|*)", &parser), parse_ns(":not(|*)", &parser),
Ok(SelectorList::from_vec(vec![Selector::from_vec( Ok(SelectorList::from_vec(vec![Selector::from_vec(
vec![Component::Negation( vec![Component::Negation(
vec![ vec![Selector::from_vec(
Component::ExplicitNoNamespace, vec![
Component::ExplicitUniversalType, Component::ExplicitNoNamespace,
] Component::ExplicitUniversalType,
.into_boxed_slice() ],
.into(), specificity(0, 0, 0),
Default::default(),
)]
.into_boxed_slice(),
)], )],
specificity(0, 0, 0), specificity(0, 0, 0),
Default::default(), Default::default(),
@ -3245,25 +3210,12 @@ pub mod tests {
parse_ns_expected(":not(*|*)", &parser, Some(":not(*)")), parse_ns_expected(":not(*|*)", &parser, Some(":not(*)")),
Ok(SelectorList::from_vec(vec![Selector::from_vec( Ok(SelectorList::from_vec(vec![Selector::from_vec(
vec![Component::Negation( vec![Component::Negation(
vec![Component::ExplicitUniversalType] vec![Selector::from_vec(
.into_boxed_slice() vec![Component::ExplicitUniversalType],
.into(), specificity(0, 0, 0),
)], Default::default()
specificity(0, 0, 0), )]
Default::default(),
)]))
);
assert_eq!(
parse_ns(":not(svg|*)", &parser),
Ok(SelectorList::from_vec(vec![Selector::from_vec(
vec![Component::Negation(
vec![
Component::Namespace(DummyAtom("svg".into()), SVG.into()),
Component::ExplicitUniversalType,
]
.into_boxed_slice() .into_boxed_slice()
.into(),
)], )],
specificity(0, 0, 0), specificity(0, 0, 0),
Default::default(), Default::default(),

View file

@ -113,7 +113,7 @@ pub trait Element: Sized + Clone + Debug {
fn has_class( fn has_class(
&self, &self,
name: &<Self::Impl as SelectorImpl>::ClassName, name: &<Self::Impl as SelectorImpl>::Identifier,
case_sensitivity: CaseSensitivity, case_sensitivity: CaseSensitivity,
) -> bool; ) -> bool;
@ -121,10 +121,10 @@ pub trait Element: Sized + Clone + Debug {
/// direction, that is, in an outer-tree -> inner-tree direction. /// direction, that is, in an outer-tree -> inner-tree direction.
fn imported_part( fn imported_part(
&self, &self,
name: &<Self::Impl as SelectorImpl>::PartName, name: &<Self::Impl as SelectorImpl>::Identifier,
) -> Option<<Self::Impl as SelectorImpl>::PartName>; ) -> Option<<Self::Impl as SelectorImpl>::Identifier>;
fn is_part(&self, name: &<Self::Impl as SelectorImpl>::PartName) -> bool; fn is_part(&self, name: &<Self::Impl as SelectorImpl>::Identifier) -> bool;
/// Returns whether this element matches `:empty`. /// Returns whether this element matches `:empty`.
/// ///

View file

@ -35,7 +35,7 @@ arrayvec = "0.5"
atomic_refcell = "0.1" atomic_refcell = "0.1"
bitflags = "1.0" bitflags = "1.0"
byteorder = "1.0" byteorder = "1.0"
cssparser = "0.27" cssparser = "0.28"
derive_more = "0.99" derive_more = "0.99"
encoding_rs = { version = "0.8", optional = true } encoding_rs = { version = "0.8", optional = true }
euclid = "0.20" euclid = "0.20"

View file

@ -12,6 +12,7 @@ use crate::str::str_join;
use crate::str::{read_exponent, read_fraction, HTML_SPACE_CHARACTERS}; use crate::str::{read_exponent, read_fraction, HTML_SPACE_CHARACTERS};
use crate::str::{read_numbers, split_commas, split_html_space_chars}; use crate::str::{read_numbers, split_commas, split_html_space_chars};
use crate::values::specified::Length; use crate::values::specified::Length;
use crate::values::AtomString;
use crate::{Atom, LocalName, Namespace, Prefix}; use crate::{Atom, LocalName, Namespace, Prefix};
use app_units::Au; use app_units::Au;
use cssparser::{self, Color, RGBA}; use cssparser::{self, Color, RGBA};
@ -354,7 +355,7 @@ impl AttrValue {
} }
} }
pub fn eval_selector(&self, selector: &AttrSelectorOperation<&String>) -> bool { pub fn eval_selector(&self, selector: &AttrSelectorOperation<&AtomString>) -> bool {
// FIXME(SimonSapin) this can be more efficient by matching on `(self, selector)` variants // FIXME(SimonSapin) this can be more efficient by matching on `(self, selector)` variants
// and doing Atom comparisons instead of string comparisons where possible, // and doing Atom comparisons instead of string comparisons where possible,
// with SelectorImpl::AttrValue changed to Atom. // with SelectorImpl::AttrValue changed to Atom.

View file

@ -279,7 +279,7 @@ impl<'a> BuilderWithConfig<'a> {
fn get_builder(self) -> Builder { fn get_builder(self) -> Builder {
for key in self.config.keys() { for key in self.config.keys() {
if !self.used_keys.contains(key.as_str()) { if !self.used_keys.contains(key.as_str()) {
panic!(format!("Unknown key: {}", key)); panic!("Unknown key: {}", key);
} }
} }
self.builder self.builder

View file

@ -232,12 +232,12 @@ macro_rules! counter_style_descriptors {
fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
dest.write_str("@counter-style ")?; dest.write_str("@counter-style ")?;
self.name.to_css(&mut CssWriter::new(dest))?; self.name.to_css(&mut CssWriter::new(dest))?;
dest.write_str(" {\n")?; dest.write_str(" { ")?;
$( $(
if let Some(ref value) = self.$ident { if let Some(ref value) = self.$ident {
dest.write_str(concat!(" ", $name, ": "))?; dest.write_str(concat!($name, ": "))?;
ToCss::to_css(value, &mut CssWriter::new(dest))?; ToCss::to_css(value, &mut CssWriter::new(dest))?;
dest.write_str(";\n")?; dest.write_str("; ")?;
} }
)+ )+
dest.write_str("}") dest.write_str("}")

View file

@ -17,16 +17,18 @@ def main(filename):
or 'data-dfn-for="<counter-style>"' in line or 'data-dfn-for="<counter-style>"' in line
] ]
with open(filename, "wb") as f: with open(filename, "wb") as f:
f.write("""\ f.write(
"""\
/* This Source Code Form is subject to the terms of the Mozilla Public /* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
predefined! { predefined! {
""") """
)
for name in names: for name in names:
f.write(' "%s",\n' % name) f.write(' "%s",\n' % name)
f.write('}\n') f.write("}\n")
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -174,11 +174,11 @@ impl VariableValue {
/// Prevent values from getting terribly big since you can use custom /// Prevent values from getting terribly big since you can use custom
/// properties exponentially. /// properties exponentially.
/// ///
/// This number (1MB) is somewhat arbitrary, but silly enough that no /// This number (2MB) is somewhat arbitrary, but silly enough that no
/// sane page would hit it. We could limit by number of total /// reasonable page should hit it. We could limit by number of total
/// substitutions, but that was very easy to work around in practice /// substitutions, but that was very easy to work around in practice
/// (just choose a larger initial value and boom). /// (just choose a larger initial value and boom).
const MAX_VALUE_LENGTH_IN_BYTES: usize = 1024 * 1024; const MAX_VALUE_LENGTH_IN_BYTES: usize = 2 * 1024 * 1024;
if self.css.len() + css.len() > MAX_VALUE_LENGTH_IN_BYTES { if self.css.len() + css.len() > MAX_VALUE_LENGTH_IN_BYTES {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
@ -247,8 +247,11 @@ impl VariableValue {
.collect::<Vec<_>>() .collect::<Vec<_>>()
.into_boxed_slice(); .into_boxed_slice();
let mut css = css.into_owned();
css.shrink_to_fit();
Ok(Arc::new(VariableValue { Ok(Arc::new(VariableValue {
css: css.into_owned(), css,
first_token_type, first_token_type,
last_token_type, last_token_type,
references: custom_property_references, references: custom_property_references,
@ -268,9 +271,11 @@ impl VariableValue {
unit: CowRcStr::from("px"), unit: CowRcStr::from("px"),
}; };
let token_type = token.serialization_type(); let token_type = token.serialization_type();
let mut css = token.to_css_string();
css.shrink_to_fit();
VariableValue { VariableValue {
css: token.to_css_string(), css,
first_token_type: token_type, first_token_type: token_type,
last_token_type: token_type, last_token_type: token_type,
references: Default::default(), references: Default::default(),
@ -577,7 +582,7 @@ impl<'a> CustomPropertiesBuilder<'a> {
let value = if !has_references && unparsed_value.references_environment { let value = if !has_references && unparsed_value.references_environment {
let result = substitute_references_in_value(unparsed_value, &map, &self.device); let result = substitute_references_in_value(unparsed_value, &map, &self.device);
match result { match result {
Ok(new_value) => Arc::new(new_value), Ok(new_value) => new_value,
Err(..) => { Err(..) => {
// Don't touch the map, this has the same effect as // Don't touch the map, this has the same effect as
// making it compute to the inherited one. // making it compute to the inherited one.
@ -657,6 +662,7 @@ impl<'a> CustomPropertiesBuilder<'a> {
let inherited = self.inherited.as_ref().map(|m| &***m); let inherited = self.inherited.as_ref().map(|m| &***m);
substitute_all(&mut map, inherited, self.device); substitute_all(&mut map, inherited, self.device);
} }
map.shrink_to_fit();
Some(Arc::new(map)) Some(Arc::new(map))
} }
} }
@ -847,7 +853,7 @@ fn substitute_all(
let result = substitute_references_in_value(&value, &context.map, &context.device); let result = substitute_references_in_value(&value, &context.map, &context.device);
match result { match result {
Ok(computed_value) => { Ok(computed_value) => {
context.map.insert(name, Arc::new(computed_value)); context.map.insert(name, computed_value);
}, },
Err(..) => { Err(..) => {
// This is invalid, reset it to the unset (inherited) value. // This is invalid, reset it to the unset (inherited) value.
@ -889,7 +895,7 @@ fn substitute_references_in_value<'i>(
value: &'i VariableValue, value: &'i VariableValue,
custom_properties: &CustomPropertiesMap, custom_properties: &CustomPropertiesMap,
device: &Device, device: &Device,
) -> Result<ComputedValue, ParseError<'i>> { ) -> Result<Arc<ComputedValue>, ParseError<'i>> {
debug_assert!(!value.references.is_empty() || value.references_environment); debug_assert!(!value.references.is_empty() || value.references_environment);
let mut input = ParserInput::new(&value.css); let mut input = ParserInput::new(&value.css);
@ -906,7 +912,8 @@ fn substitute_references_in_value<'i>(
)?; )?;
computed_value.push_from(&input, position, last_token_type)?; computed_value.push_from(&input, position, last_token_type)?;
Ok(computed_value) computed_value.css.shrink_to_fit();
Ok(Arc::new(computed_value))
} }
/// Replace `var()` functions in an arbitrary bit of input. /// Replace `var()` functions in an arbitrary bit of input.

View file

@ -20,7 +20,8 @@ use crate::selector_parser::{AttrValue, Lang, PseudoElement, SelectorImpl};
use crate::shared_lock::{Locked, SharedRwLock}; use crate::shared_lock::{Locked, SharedRwLock};
use crate::stylist::CascadeData; use crate::stylist::CascadeData;
use crate::traversal_flags::TraversalFlags; use crate::traversal_flags::TraversalFlags;
use crate::{Atom, LocalName, Namespace, WeakAtom}; use crate::values::AtomIdent;
use crate::{LocalName, Namespace, WeakAtom};
use atomic_refcell::{AtomicRef, AtomicRefMut}; use atomic_refcell::{AtomicRef, AtomicRefMut};
use selectors::matching::{ElementSelectorFlags, QuirksMode, VisitedHandlingMode}; use selectors::matching::{ElementSelectorFlags, QuirksMode, VisitedHandlingMode};
use selectors::sink::Push; use selectors::sink::Push;
@ -121,7 +122,7 @@ pub trait TDocument: Sized + Copy + Clone {
/// return an empty slice. /// return an empty slice.
fn elements_with_id<'a>( fn elements_with_id<'a>(
&self, &self,
_id: &Atom, _id: &AtomIdent,
) -> Result<&'a [<Self::ConcreteNode as TNode>::ConcreteElement], ()> ) -> Result<&'a [<Self::ConcreteNode as TNode>::ConcreteElement], ()>
where where
Self: 'a, Self: 'a,
@ -187,22 +188,18 @@ pub trait TNode: Sized + Copy + Clone + Debug + NodeInfo + PartialEq {
return Some(c); return Some(c);
} }
if Some(*self) == scoped_to { let mut current = Some(*self);
return None;
}
let mut current = *self;
loop { loop {
if let Some(s) = current.next_sibling() { if current == scoped_to {
return Some(s);
}
let parent = current.parent_node();
if parent == scoped_to {
return None; return None;
} }
current = parent.expect("Not a descendant of the scope?"); debug_assert!(current.is_some(), "not a descendant of the scope?");
if let Some(s) = current?.next_sibling() {
return Some(s);
}
current = current?.parent_node();
} }
} }
@ -344,7 +341,7 @@ pub trait TShadowRoot: Sized + Copy + Clone + Debug + PartialEq {
/// return an empty slice. /// return an empty slice.
fn elements_with_id<'a>( fn elements_with_id<'a>(
&self, &self,
_id: &Atom, _id: &AtomIdent,
) -> Result<&'a [<Self::ConcreteNode as TNode>::ConcreteElement], ()> ) -> Result<&'a [<Self::ConcreteNode as TNode>::ConcreteElement], ()>
where where
Self: 'a, Self: 'a,
@ -520,20 +517,20 @@ pub trait TElement:
/// Internal iterator for the classes of this element. /// Internal iterator for the classes of this element.
fn each_class<F>(&self, callback: F) fn each_class<F>(&self, callback: F)
where where
F: FnMut(&Atom); F: FnMut(&AtomIdent);
/// Internal iterator for the part names of this element. /// Internal iterator for the part names of this element.
fn each_part<F>(&self, _callback: F) fn each_part<F>(&self, _callback: F)
where where
F: FnMut(&Atom), F: FnMut(&AtomIdent),
{ {
} }
/// Internal iterator for the part names that this element exports for a /// Internal iterator for the part names that this element exports for a
/// given part name. /// given part name.
fn each_exported_part<F>(&self, _name: &Atom, _callback: F) fn each_exported_part<F>(&self, _name: &AtomIdent, _callback: F)
where where
F: FnMut(&Atom), F: FnMut(&AtomIdent),
{ {
} }
@ -584,14 +581,35 @@ pub trait TElement:
traversal_flags: TraversalFlags, traversal_flags: TraversalFlags,
) -> bool { ) -> bool {
if traversal_flags.for_animation_only() { if traversal_flags.for_animation_only() {
// In animation-only restyle we never touch snapshots and don't // In animation-only restyle we never touch snapshots and don't care
// care about them. But we can't assert '!self.handled_snapshot()' // about them. But we can't assert '!self.handled_snapshot()'
// here since there are some cases that a second animation-only // here since there are some cases that a second animation-only
// restyle which is a result of normal restyle (e.g. setting // restyle which is a result of normal restyle (e.g. setting
// animation-name in normal restyle and creating a new CSS // animation-name in normal restyle and creating a new CSS
// animation in a SequentialTask) is processed after the normal // animation in a SequentialTask) is processed after the normal
// traversal in that we had elements that handled snapshot. // traversal in that we had elements that handled snapshot.
return data.has_styles() && !data.hint.has_animation_hint_or_recascade(); if !data.has_styles() {
return false;
}
if !data.hint.has_animation_hint_or_recascade() {
return true;
}
// FIXME: This should ideally always return false, but it is a hack
// to work around our weird animation-only traversal
// stuff: If we're display: none and the rules we could match could
// change, we consider our style up-to-date. This is because
// re-cascading with and old style doesn't guarantee returning the
// correct animation style (that's bug 1393323). So if our display
// changed, and it changed from display: none, we would incorrectly
// forget about it and wouldn't be able to correctly style our
// descendants later.
if data.styles.is_display_none() && data.hint.match_self() {
return true;
}
return false;
} }
if self.has_snapshot() && !self.handled_snapshot() { if self.has_snapshot() && !self.handled_snapshot() {

View file

@ -10,13 +10,12 @@ use crate::dom::{TDocument, TElement, TNode, TShadowRoot};
use crate::invalidation::element::invalidation_map::Dependency; use crate::invalidation::element::invalidation_map::Dependency;
use crate::invalidation::element::invalidator::{DescendantInvalidationLists, Invalidation}; use crate::invalidation::element::invalidator::{DescendantInvalidationLists, Invalidation};
use crate::invalidation::element::invalidator::{InvalidationProcessor, InvalidationVector}; use crate::invalidation::element::invalidator::{InvalidationProcessor, InvalidationVector};
use crate::Atom; use crate::values::AtomIdent;
use selectors::attr::CaseSensitivity; use selectors::attr::CaseSensitivity;
use selectors::matching::{self, MatchingContext, MatchingMode}; use selectors::matching::{self, MatchingContext, MatchingMode};
use selectors::parser::{Combinator, Component, LocalName, SelectorImpl}; use selectors::parser::{Combinator, Component, LocalName, SelectorImpl};
use selectors::{Element, NthIndexCache, SelectorList}; use selectors::{Element, NthIndexCache, SelectorList};
use smallvec::SmallVec; use smallvec::SmallVec;
use std::borrow::Borrow;
/// <https://dom.spec.whatwg.org/#dom-element-matches> /// <https://dom.spec.whatwg.org/#dom-element-matches>
pub fn element_matches<E>( pub fn element_matches<E>(
@ -276,7 +275,7 @@ where
/// or shadow root that `root` is connected to. /// or shadow root that `root` is connected to.
fn fast_connected_elements_with_id<'a, N>( fn fast_connected_elements_with_id<'a, N>(
root: N, root: N,
id: &Atom, id: &AtomIdent,
quirks_mode: QuirksMode, quirks_mode: QuirksMode,
) -> Result<&'a [N::ConcreteElement], ()> ) -> Result<&'a [N::ConcreteElement], ()>
where where
@ -305,7 +304,7 @@ where
/// Collects elements with a given id under `root`, that pass `filter`. /// Collects elements with a given id under `root`, that pass `filter`.
fn collect_elements_with_id<E, Q, F>( fn collect_elements_with_id<E, Q, F>(
root: E::ConcreteNode, root: E::ConcreteNode,
id: &Atom, id: &AtomIdent,
results: &mut Q::Output, results: &mut Q::Output,
quirks_mode: QuirksMode, quirks_mode: QuirksMode,
mut filter: F, mut filter: F,
@ -354,11 +353,14 @@ where
ref name, ref name,
ref lower_name, ref lower_name,
} = *local_name; } = *local_name;
if element.is_html_element_in_html_document() {
element.local_name() == lower_name.borrow() let chosen_name = if element.is_html_element_in_html_document() {
lower_name
} else { } else {
element.local_name() == name.borrow() name
} };
element.local_name() == &**chosen_name
} }
/// Fast paths for querySelector with a single simple selector. /// Fast paths for querySelector with a single simple selector.
@ -398,7 +400,7 @@ where
} }
enum SimpleFilter<'a, Impl: SelectorImpl> { enum SimpleFilter<'a, Impl: SelectorImpl> {
Class(&'a Atom), Class(&'a AtomIdent),
LocalName(&'a LocalName<Impl>), LocalName(&'a LocalName<Impl>),
} }

View file

@ -53,18 +53,8 @@ bitflags! {
const IN_MOZ_UI_INVALID_STATE = 1 << 13; const IN_MOZ_UI_INVALID_STATE = 1 << 13;
/// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-broken /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-broken
const IN_BROKEN_STATE = 1 << 14; const IN_BROKEN_STATE = 1 << 14;
/// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-user-disabled
const IN_USER_DISABLED_STATE = 1 << 15;
/// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-suppressed
const IN_SUPPRESSED_STATE = 1 << 16;
/// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-loading /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-loading
const IN_LOADING_STATE = 1 << 17; const IN_LOADING_STATE = 1 << 17;
/// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-handler-blocked
const IN_HANDLER_BLOCKED_STATE = 1 << 18;
/// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-handler-disabled
const IN_HANDLER_DISABLED_STATE = 1 << 19;
/// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-handler-crashed
const IN_HANDLER_CRASHED_STATE = 1 << 20;
/// <https://html.spec.whatwg.org/multipage/#selector-required> /// <https://html.spec.whatwg.org/multipage/#selector-required>
const IN_REQUIRED_STATE = 1 << 21; const IN_REQUIRED_STATE = 1 << 21;
/// <https://html.spec.whatwg.org/multipage/#selector-optional> /// <https://html.spec.whatwg.org/multipage/#selector-optional>
@ -107,13 +97,9 @@ bitflags! {
/// Non-standard & undocumented. /// Non-standard & undocumented.
const IN_INCREMENT_SCRIPT_LEVEL_STATE = 1 << 38; const IN_INCREMENT_SCRIPT_LEVEL_STATE = 1 << 38;
/// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-focusring /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-focusring
///
/// But also https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo
const IN_FOCUSRING_STATE = 1 << 39; const IN_FOCUSRING_STATE = 1 << 39;
/// Non-standard & undocumented.
const IN_HANDLER_CLICK_TO_PLAY_STATE = 1 << 40;
/// Non-standard & undocumented.
const IN_HANDLER_VULNERABLE_UPDATABLE_STATE = 1 << 41;
/// Non-standard & undocumented.
const IN_HANDLER_VULNERABLE_NO_UPDATE_STATE = 1 << 42;
/// <https://drafts.csswg.org/selectors-4/#the-focus-within-pseudo> /// <https://drafts.csswg.org/selectors-4/#the-focus-within-pseudo>
const IN_FOCUS_WITHIN_STATE = 1 << 43; const IN_FOCUS_WITHIN_STATE = 1 << 43;
/// :dir matching; the states are used for dynamic change detection. /// :dir matching; the states are used for dynamic change detection.
@ -137,14 +123,17 @@ bitflags! {
const IN_AUTOFILL_STATE = 1 << 50; const IN_AUTOFILL_STATE = 1 << 50;
/// Non-standard & undocumented. /// Non-standard & undocumented.
const IN_AUTOFILL_PREVIEW_STATE = 1 << 51; const IN_AUTOFILL_PREVIEW_STATE = 1 << 51;
/// :focus-visible
///
/// https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo
const IN_FOCUS_VISIBLE_STATE = 1 << 52;
/// State that dialog element is modal, for centered alignment /// State that dialog element is modal, for centered alignment
/// ///
/// https://html.spec.whatwg.org/multipage/#centered-alignment /// https://html.spec.whatwg.org/multipage/#centered-alignment
const IN_MODAL_DIALOG_STATE = 1 << 53; const IN_MODAL_DIALOG_STATE = 1 << 53;
/// https://html.spec.whatwg.org/multipage/#inert-subtrees
const IN_MOZINERT_STATE = 1 << 54;
/// State for the topmost dialog element in top layer
const IN_TOPMOST_MODAL_DIALOG_STATE = 1 << 55;
/// Non-standard & undocumented.
const IN_HANDLER_NOPLUGINS = 1 << 56;
} }
} }

View file

@ -456,9 +456,9 @@ macro_rules! font_face_descriptors_common {
pub fn decl_to_css(&self, dest: &mut CssStringWriter) -> fmt::Result { pub fn decl_to_css(&self, dest: &mut CssStringWriter) -> fmt::Result {
$( $(
if let Some(ref value) = self.$ident { if let Some(ref value) = self.$ident {
dest.write_str(concat!(" ", $name, ": "))?; dest.write_str(concat!($name, ": "))?;
ToCss::to_css(value, &mut CssWriter::new(dest))?; ToCss::to_css(value, &mut CssWriter::new(dest))?;
dest.write_str(";\n")?; dest.write_str("; ")?;
} }
)* )*
Ok(()) Ok(())
@ -469,8 +469,11 @@ macro_rules! font_face_descriptors_common {
type Declaration = (); type Declaration = ();
type Error = StyleParseErrorKind<'i>; type Error = StyleParseErrorKind<'i>;
fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>) fn parse_value<'t>(
-> Result<(), ParseError<'i>> { &mut self,
name: CowRcStr<'i>,
input: &mut Parser<'i, 't>,
) -> Result<(), ParseError<'i>> {
match_ignore_ascii_case! { &*name, match_ignore_ascii_case! { &*name,
$( $(
$name if is_descriptor_enabled!($name) => { $name if is_descriptor_enabled!($name) => {
@ -493,7 +496,7 @@ macro_rules! font_face_descriptors_common {
impl ToCssWithGuard for FontFaceRuleData { impl ToCssWithGuard for FontFaceRuleData {
// Serialization of FontFaceRule is not specced. // Serialization of FontFaceRule is not specced.
fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result { fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
dest.write_str("@font-face {\n")?; dest.write_str("@font-face { ")?;
self.decl_to_css(dest)?; self.decl_to_css(dest)?;
dest.write_str("}") dest.write_str("}")
} }

View file

@ -10,25 +10,13 @@ use crate::media_queries::media_feature::{AllowsRanges, ParsingRequirements};
use crate::media_queries::media_feature::{Evaluator, MediaFeatureDescription}; use crate::media_queries::media_feature::{Evaluator, MediaFeatureDescription};
use crate::media_queries::media_feature_expression::RangeOrOperator; use crate::media_queries::media_feature_expression::RangeOrOperator;
use crate::media_queries::{Device, MediaType}; use crate::media_queries::{Device, MediaType};
use crate::values::computed::position::Ratio;
use crate::values::computed::CSSPixelLength; use crate::values::computed::CSSPixelLength;
use crate::values::computed::Ratio;
use crate::values::computed::Resolution; use crate::values::computed::Resolution;
use crate::Atom; use crate::Atom;
use app_units::Au; use app_units::Au;
use euclid::default::Size2D; use euclid::default::Size2D;
fn viewport_size(device: &Device) -> Size2D<Au> {
if let Some(pc) = device.pres_context() {
if pc.mIsRootPaginatedDocument() != 0 {
// We want the page size, including unprintable areas and margins.
// FIXME(emilio, bug 1414600): Not quite!
let area = &pc.mPageSize;
return Size2D::new(Au(area.width), Au(area.height));
}
}
device.au_viewport_size()
}
fn device_size(device: &Device) -> Size2D<Au> { fn device_size(device: &Device) -> Size2D<Au> {
let mut width = 0; let mut width = 0;
let mut height = 0; let mut height = 0;
@ -47,7 +35,7 @@ fn eval_width(
RangeOrOperator::evaluate( RangeOrOperator::evaluate(
range_or_operator, range_or_operator,
value.map(Au::from), value.map(Au::from),
viewport_size(device).width, device.au_viewport_size().width,
) )
} }
@ -73,7 +61,7 @@ fn eval_height(
RangeOrOperator::evaluate( RangeOrOperator::evaluate(
range_or_operator, range_or_operator,
value.map(Au::from), value.map(Au::from),
viewport_size(device).height, device.au_viewport_size().height,
) )
} }
@ -99,8 +87,12 @@ fn eval_aspect_ratio_for<F>(
where where
F: FnOnce(&Device) -> Size2D<Au>, F: FnOnce(&Device) -> Size2D<Au>,
{ {
// A ratio of 0/0 behaves as the ratio 1/0, so we need to call used_value()
// to convert it if necessary.
// FIXME: we may need to update here once
// https://github.com/w3c/csswg-drafts/issues/4954 got resolved.
let query_value = match query_value { let query_value = match query_value {
Some(v) => v, Some(v) => v.used_value(),
None => return true, None => return true,
}; };
@ -115,7 +107,12 @@ fn eval_aspect_ratio(
query_value: Option<Ratio>, query_value: Option<Ratio>,
range_or_operator: Option<RangeOrOperator>, range_or_operator: Option<RangeOrOperator>,
) -> bool { ) -> bool {
eval_aspect_ratio_for(device, query_value, range_or_operator, viewport_size) eval_aspect_ratio_for(
device,
query_value,
range_or_operator,
Device::au_viewport_size,
)
} }
/// https://drafts.csswg.org/mediaqueries-4/#device-aspect-ratio /// https://drafts.csswg.org/mediaqueries-4/#device-aspect-ratio
@ -168,7 +165,7 @@ where
/// https://drafts.csswg.org/mediaqueries-4/#orientation /// https://drafts.csswg.org/mediaqueries-4/#orientation
fn eval_orientation(device: &Device, value: Option<Orientation>) -> bool { fn eval_orientation(device: &Device, value: Option<Orientation>) -> bool {
eval_orientation_for(device, value, viewport_size) eval_orientation_for(device, value, Device::au_viewport_size)
} }
/// FIXME: There's no spec for `-moz-device-orientation`. /// FIXME: There's no spec for `-moz-device-orientation`.
@ -247,13 +244,13 @@ fn eval_color_index(
/// https://drafts.csswg.org/mediaqueries-4/#monochrome /// https://drafts.csswg.org/mediaqueries-4/#monochrome
fn eval_monochrome( fn eval_monochrome(
_: &Device, device: &Device,
query_value: Option<u32>, query_value: Option<u32>,
range_or_operator: Option<RangeOrOperator>, range_or_operator: Option<RangeOrOperator>,
) -> bool { ) -> bool {
// For color devices we should return 0. // For color devices we should return 0.
// FIXME: On a monochrome device, return the actual color depth, not 0! let depth =
let depth = 0; unsafe { bindings::Gecko_MediaFeatures_GetMonochromeBitsPerPixel(device.document()) };
RangeOrOperator::evaluate(range_or_operator, query_value, depth) RangeOrOperator::evaluate(range_or_operator, query_value, depth)
} }
@ -308,6 +305,50 @@ fn eval_prefers_reduced_motion(device: &Device, query_value: Option<PrefersReduc
} }
} }
/// Possible values for prefers-contrast media query.
/// https://drafts.csswg.org/mediaqueries-5/#prefers-contrast
#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]
#[repr(u8)]
pub enum PrefersContrast {
/// More contrast is preferred. Corresponds to an accessibility theme
/// being enabled or Firefox forcing high contrast colors.
More,
/// Low contrast is preferred.
Less,
/// The default value if neither high or low contrast is enabled.
NoPreference,
}
/// https://drafts.csswg.org/mediaqueries-5/#prefers-contrast
fn eval_prefers_contrast(device: &Device, query_value: Option<PrefersContrast>) -> bool {
let prefers_contrast =
unsafe { bindings::Gecko_MediaFeatures_PrefersContrast(device.document()) };
match query_value {
Some(v) => v == prefers_contrast,
None => prefers_contrast != PrefersContrast::NoPreference,
}
}
/// Possible values for the forced-colors media query.
/// https://drafts.csswg.org/mediaqueries-5/#forced-colors
#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)]
#[repr(u8)]
pub enum ForcedColors {
/// Page colors are not being forced.
None,
/// Page colors are being forced.
Active,
}
/// https://drafts.csswg.org/mediaqueries-5/#forced-colors
fn eval_forced_colors(device: &Device, query_value: Option<ForcedColors>) -> bool {
let forced = !device.use_document_colors();
match query_value {
Some(query_value) => forced == (query_value == ForcedColors::Active),
None => forced,
}
}
#[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)] #[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)]
#[repr(u8)] #[repr(u8)]
enum OverflowBlock { enum OverflowBlock {
@ -468,6 +509,28 @@ fn eval_moz_is_glyph(
query_value.map_or(is_glyph, |v| v == is_glyph) query_value.map_or(is_glyph, |v| v == is_glyph)
} }
fn eval_moz_print_preview(
device: &Device,
query_value: Option<bool>,
_: Option<RangeOrOperator>,
) -> bool {
let is_print_preview = device.is_print_preview();
if is_print_preview {
debug_assert_eq!(device.media_type(), MediaType::print());
}
query_value.map_or(is_print_preview, |v| v == is_print_preview)
}
fn eval_moz_non_native_content_theme(
device: &Device,
query_value: Option<bool>,
_: Option<RangeOrOperator>,
) -> bool {
let non_native_theme =
unsafe { bindings::Gecko_MediaFeatures_ShouldAvoidNativeTheme(device.document()) };
query_value.map_or(non_native_theme, |v| v == non_native_theme)
}
fn eval_moz_is_resource_document( fn eval_moz_is_resource_document(
device: &Device, device: &Device,
query_value: Option<bool>, query_value: Option<bool>,
@ -494,19 +557,6 @@ fn eval_system_metric(
query_value.map_or(supports_metric, |v| v == supports_metric) query_value.map_or(supports_metric, |v| v == supports_metric)
} }
fn eval_moz_touch_enabled(
device: &Device,
query_value: Option<bool>,
_: Option<RangeOrOperator>,
) -> bool {
eval_system_metric(
device,
query_value,
atom!("-moz-touch-enabled"),
/* accessible_from_content = */ true,
)
}
fn eval_moz_os_version( fn eval_moz_os_version(
device: &Device, device: &Device,
query_value: Option<Atom>, query_value: Option<Atom>,
@ -548,7 +598,7 @@ macro_rules! system_metric_feature {
/// to support new types in these entries and (2) ensuring that either /// to support new types in these entries and (2) ensuring that either
/// nsPresContext::MediaFeatureValuesChanged is called when the value that /// nsPresContext::MediaFeatureValuesChanged is called when the value that
/// would be returned by the evaluator function could change. /// would be returned by the evaluator function could change.
pub static MEDIA_FEATURES: [MediaFeatureDescription; 53] = [ pub static MEDIA_FEATURES: [MediaFeatureDescription; 56] = [
feature!( feature!(
atom!("width"), atom!("width"),
AllowsRanges::Yes, AllowsRanges::Yes,
@ -666,6 +716,23 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 53] = [
keyword_evaluator!(eval_prefers_reduced_motion, PrefersReducedMotion), keyword_evaluator!(eval_prefers_reduced_motion, PrefersReducedMotion),
ParsingRequirements::empty(), ParsingRequirements::empty(),
), ),
feature!(
atom!("prefers-contrast"),
AllowsRanges::No,
keyword_evaluator!(eval_prefers_contrast, PrefersContrast),
// Note: by default this is only enabled in browser chrome and
// ua. It can be enabled on the web via the
// layout.css.prefers-contrast.enabled preference. See
// disabed_by_pref in media_feature_expression.rs for how that
// is done.
ParsingRequirements::empty(),
),
feature!(
atom!("forced-colors"),
AllowsRanges::No,
keyword_evaluator!(eval_forced_colors, ForcedColors),
ParsingRequirements::empty(),
),
feature!( feature!(
atom!("overflow-block"), atom!("overflow-block"),
AllowsRanges::No, AllowsRanges::No,
@ -729,6 +796,18 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 53] = [
Evaluator::Ident(eval_moz_os_version), Evaluator::Ident(eval_moz_os_version),
ParsingRequirements::CHROME_AND_UA_ONLY, ParsingRequirements::CHROME_AND_UA_ONLY,
), ),
feature!(
atom!("-moz-print-preview"),
AllowsRanges::No,
Evaluator::BoolInteger(eval_moz_print_preview),
ParsingRequirements::CHROME_AND_UA_ONLY,
),
feature!(
atom!("-moz-non-native-content-theme"),
AllowsRanges::No,
Evaluator::BoolInteger(eval_moz_non_native_content_theme),
ParsingRequirements::CHROME_AND_UA_ONLY,
),
system_metric_feature!(atom!("-moz-scrollbar-start-backward")), system_metric_feature!(atom!("-moz-scrollbar-start-backward")),
system_metric_feature!(atom!("-moz-scrollbar-start-forward")), system_metric_feature!(atom!("-moz-scrollbar-start-forward")),
system_metric_feature!(atom!("-moz-scrollbar-end-backward")), system_metric_feature!(atom!("-moz-scrollbar-end-backward")),
@ -737,7 +816,7 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 53] = [
system_metric_feature!(atom!("-moz-overlay-scrollbars")), system_metric_feature!(atom!("-moz-overlay-scrollbars")),
system_metric_feature!(atom!("-moz-windows-default-theme")), system_metric_feature!(atom!("-moz-windows-default-theme")),
system_metric_feature!(atom!("-moz-mac-graphite-theme")), system_metric_feature!(atom!("-moz-mac-graphite-theme")),
system_metric_feature!(atom!("-moz-mac-yosemite-theme")), system_metric_feature!(atom!("-moz-mac-big-sur-theme")),
system_metric_feature!(atom!("-moz-windows-accent-color-in-titlebar")), system_metric_feature!(atom!("-moz-windows-accent-color-in-titlebar")),
system_metric_feature!(atom!("-moz-windows-compositor")), system_metric_feature!(atom!("-moz-windows-compositor")),
system_metric_feature!(atom!("-moz-windows-classic")), system_metric_feature!(atom!("-moz-windows-classic")),
@ -752,13 +831,4 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 53] = [
system_metric_feature!(atom!("-moz-gtk-csd-close-button")), system_metric_feature!(atom!("-moz-gtk-csd-close-button")),
system_metric_feature!(atom!("-moz-gtk-csd-reversed-placement")), system_metric_feature!(atom!("-moz-gtk-csd-reversed-placement")),
system_metric_feature!(atom!("-moz-system-dark-theme")), system_metric_feature!(atom!("-moz-system-dark-theme")),
// This is the only system-metric media feature that's accessible to
// content as of today.
// FIXME(emilio): Restrict (or remove?) when bug 1035774 lands.
feature!(
atom!("-moz-touch-enabled"),
AllowsRanges::No,
Evaluator::BoolInteger(eval_moz_touch_enabled),
ParsingRequirements::empty(),
),
]; ];

View file

@ -4,6 +4,7 @@
//! Gecko's media-query device and expression representation. //! Gecko's media-query device and expression representation.
use crate::context::QuirksMode;
use crate::custom_properties::CssEnvironment; use crate::custom_properties::CssEnvironment;
use crate::gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor}; use crate::gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
use crate::gecko_bindings::bindings; use crate::gecko_bindings::bindings;
@ -11,6 +12,7 @@ use crate::gecko_bindings::structs;
use crate::media_queries::MediaType; use crate::media_queries::MediaType;
use crate::properties::ComputedValues; use crate::properties::ComputedValues;
use crate::string_cache::Atom; use crate::string_cache::Atom;
use crate::values::computed::Length;
use crate::values::specified::font::FONT_MEDIUM_PX; use crate::values::specified::font::FONT_MEDIUM_PX;
use crate::values::{CustomIdent, KeyframesName}; use crate::values::{CustomIdent, KeyframesName};
use app_units::{Au, AU_PER_PX}; use app_units::{Au, AU_PER_PX};
@ -18,8 +20,8 @@ use cssparser::RGBA;
use euclid::default::Size2D; use euclid::default::Size2D;
use euclid::{Scale, SideOffsets2D}; use euclid::{Scale, SideOffsets2D};
use servo_arc::Arc; use servo_arc::Arc;
use std::fmt; use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize, Ordering}; use std::{cmp, fmt};
use style_traits::viewport::ViewportConstraints; use style_traits::viewport::ViewportConstraints;
use style_traits::{CSSPixel, DevicePixel}; use style_traits::{CSSPixel, DevicePixel};
@ -30,15 +32,16 @@ pub struct Device {
/// `Device`, so having a raw document pointer here is fine. /// `Device`, so having a raw document pointer here is fine.
document: *const structs::Document, document: *const structs::Document,
default_values: Arc<ComputedValues>, default_values: Arc<ComputedValues>,
/// The font size of the root element /// The font size of the root element.
/// This is set when computing the style of the root
/// element, and used for rem units in other elements.
/// ///
/// When computing the style of the root element, there can't be any /// This is set when computing the style of the root element, and used for
/// other style being computed at the same time, given we need the style of /// rem units in other elements.
/// the parent to compute everything else. So it is correct to just use ///
/// a relaxed atomic here. /// When computing the style of the root element, there can't be any other
root_font_size: AtomicIsize, /// style being computed at the same time, given we need the style of the
/// parent to compute everything else. So it is correct to just use a
/// relaxed atomic here.
root_font_size: AtomicU32,
/// The body text color, stored as an `nscolor`, used for the "tables /// The body text color, stored as an `nscolor`, used for the "tables
/// inherit from body" quirk. /// inherit from body" quirk.
/// ///
@ -85,8 +88,7 @@ impl Device {
Device { Device {
document, document,
default_values: ComputedValues::default_values(doc), default_values: ComputedValues::default_values(doc),
// FIXME(bz): Seems dubious? root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()),
root_font_size: AtomicIsize::new(Au::from_px(FONT_MEDIUM_PX as i32).0 as isize),
body_text_color: AtomicUsize::new(prefs.mDefaultColor as usize), body_text_color: AtomicUsize::new(prefs.mDefaultColor as usize),
used_root_font_size: AtomicBool::new(false), used_root_font_size: AtomicBool::new(false),
used_viewport_size: AtomicBool::new(false), used_viewport_size: AtomicBool::new(false),
@ -131,15 +133,20 @@ impl Device {
} }
/// Get the font size of the root element (for rem) /// Get the font size of the root element (for rem)
pub fn root_font_size(&self) -> Au { pub fn root_font_size(&self) -> Length {
self.used_root_font_size.store(true, Ordering::Relaxed); self.used_root_font_size.store(true, Ordering::Relaxed);
Au::new(self.root_font_size.load(Ordering::Relaxed) as i32) Length::new(f32::from_bits(self.root_font_size.load(Ordering::Relaxed)))
} }
/// Set the font size of the root element (for rem) /// Set the font size of the root element (for rem)
pub fn set_root_font_size(&self, size: Au) { pub fn set_root_font_size(&self, size: Length) {
self.root_font_size self.root_font_size
.store(size.0 as isize, Ordering::Relaxed) .store(size.px().to_bits(), Ordering::Relaxed)
}
/// The quirks mode of the document.
pub fn quirks_mode(&self) -> QuirksMode {
self.document().mCompatMode.into()
} }
/// Sets the body text color for the "inherit color from body" quirk. /// Sets the body text color for the "inherit color from body" quirk.
@ -205,6 +212,15 @@ impl Device {
self.reset_computed_values(); self.reset_computed_values();
} }
/// Returns whether this document is in print preview.
pub fn is_print_preview(&self) -> bool {
let pc = match self.pres_context() {
Some(pc) => pc,
None => return false,
};
pc.mType == structs::nsPresContext_nsPresContextType_eContext_PrintPreview
}
/// Returns the current media type of the device. /// Returns the current media type of the device.
pub fn media_type(&self) -> MediaType { pub fn media_type(&self) -> MediaType {
let pc = match self.pres_context() { let pc = match self.pres_context() {
@ -222,12 +238,29 @@ impl Device {
MediaType(CustomIdent(unsafe { Atom::from_raw(medium_to_use) })) MediaType(CustomIdent(unsafe { Atom::from_raw(medium_to_use) }))
} }
// It may make sense to account for @page rule margins here somehow, however
// it's not clear how that'd work, see:
// https://github.com/w3c/csswg-drafts/issues/5437
fn page_size_minus_default_margin(&self, pc: &structs::nsPresContext) -> Size2D<Au> {
debug_assert!(pc.mIsRootPaginatedDocument() != 0);
let area = &pc.mPageSize;
let margin = &pc.mDefaultPageMargin;
let width = area.width - margin.left - margin.right;
let height = area.height - margin.top - margin.bottom;
Size2D::new(Au(cmp::max(width, 0)), Au(cmp::max(height, 0)))
}
/// Returns the current viewport size in app units. /// Returns the current viewport size in app units.
pub fn au_viewport_size(&self) -> Size2D<Au> { pub fn au_viewport_size(&self) -> Size2D<Au> {
let pc = match self.pres_context() { let pc = match self.pres_context() {
Some(pc) => pc, Some(pc) => pc,
None => return Size2D::new(Au(0), Au(0)), None => return Size2D::new(Au(0), Au(0)),
}; };
if pc.mIsRootPaginatedDocument() != 0 {
return self.page_size_minus_default_margin(pc);
}
let area = &pc.mVisibleArea; let area = &pc.mVisibleArea;
Size2D::new(Au(area.width), Au(area.height)) Size2D::new(Au(area.width), Au(area.height))
} }
@ -236,11 +269,15 @@ impl Device {
/// used for viewport unit resolution. /// used for viewport unit resolution.
pub fn au_viewport_size_for_viewport_unit_resolution(&self) -> Size2D<Au> { pub fn au_viewport_size_for_viewport_unit_resolution(&self) -> Size2D<Au> {
self.used_viewport_size.store(true, Ordering::Relaxed); self.used_viewport_size.store(true, Ordering::Relaxed);
let pc = match self.pres_context() { let pc = match self.pres_context() {
Some(pc) => pc, Some(pc) => pc,
None => return Size2D::new(Au(0), Au(0)), None => return Size2D::new(Au(0), Au(0)),
}; };
if pc.mIsRootPaginatedDocument() != 0 {
return self.page_size_minus_default_margin(pc);
}
let size = &pc.mSizeForViewportUnits; let size = &pc.mSizeForViewportUnits;
Size2D::new(Au(size.width), Au(size.height)) Size2D::new(Au(size.width), Au(size.height))
} }
@ -298,13 +335,13 @@ impl Device {
/// Applies text zoom to a font-size or line-height value (see nsStyleFont::ZoomText). /// Applies text zoom to a font-size or line-height value (see nsStyleFont::ZoomText).
#[inline] #[inline]
pub fn zoom_text(&self, size: Au) -> Au { pub fn zoom_text(&self, size: Length) -> Length {
size.scale_by(self.effective_text_zoom()) size.scale_by(self.effective_text_zoom())
} }
/// Un-apply text zoom. /// Un-apply text zoom.
#[inline] #[inline]
pub fn unzoom_text(&self, size: Au) -> Au { pub fn unzoom_text(&self, size: Length) -> Length {
size.scale_by(1. / self.effective_text_zoom()) size.scale_by(1. / self.effective_text_zoom())
} }

View file

@ -32,44 +32,38 @@ macro_rules! apply_non_ts_list {
[ [
("-moz-table-border-nonzero", MozTableBorderNonzero, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-table-border-nonzero", MozTableBorderNonzero, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("-moz-browser-frame", MozBrowserFrame, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), ("-moz-browser-frame", MozBrowserFrame, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
("-moz-select-list-box", MozSelectListBox, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("link", Link, IN_UNVISITED_STATE, _), ("link", Link, IN_UNVISITED_STATE, _),
("any-link", AnyLink, IN_VISITED_OR_UNVISITED_STATE, _), ("any-link", AnyLink, IN_VISITED_OR_UNVISITED_STATE, _),
("visited", Visited, IN_VISITED_STATE, _), ("visited", Visited, IN_VISITED_STATE, _),
("active", Active, IN_ACTIVE_STATE, _), ("active", Active, IN_ACTIVE_STATE, _),
("autofill", Autofill, IN_AUTOFILL_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
("checked", Checked, IN_CHECKED_STATE, _), ("checked", Checked, IN_CHECKED_STATE, _),
("defined", Defined, IN_DEFINED_STATE, _), ("defined", Defined, IN_DEFINED_STATE, _),
("disabled", Disabled, IN_DISABLED_STATE, _), ("disabled", Disabled, IN_DISABLED_STATE, _),
("enabled", Enabled, IN_ENABLED_STATE, _), ("enabled", Enabled, IN_ENABLED_STATE, _),
("focus", Focus, IN_FOCUS_STATE, _), ("focus", Focus, IN_FOCUS_STATE, _),
("focus-within", FocusWithin, IN_FOCUS_WITHIN_STATE, _), ("focus-within", FocusWithin, IN_FOCUS_WITHIN_STATE, _),
("focus-visible", FocusVisible, IN_FOCUS_VISIBLE_STATE, _), ("focus-visible", FocusVisible, IN_FOCUSRING_STATE, _),
("hover", Hover, IN_HOVER_STATE, _), ("hover", Hover, IN_HOVER_STATE, _),
("-moz-drag-over", MozDragOver, IN_DRAGOVER_STATE, _), ("-moz-drag-over", MozDragOver, IN_DRAGOVER_STATE, _),
("target", Target, IN_TARGET_STATE, _), ("target", Target, IN_TARGET_STATE, _),
("indeterminate", Indeterminate, IN_INDETERMINATE_STATE, _), ("indeterminate", Indeterminate, IN_INDETERMINATE_STATE, _),
("-moz-inert", MozInert, IN_MOZINERT_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("-moz-devtools-highlighted", MozDevtoolsHighlighted, IN_DEVTOOLS_HIGHLIGHTED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-devtools-highlighted", MozDevtoolsHighlighted, IN_DEVTOOLS_HIGHLIGHTED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("-moz-styleeditor-transitioning", MozStyleeditorTransitioning, IN_STYLEEDITOR_TRANSITIONING_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-styleeditor-transitioning", MozStyleeditorTransitioning, IN_STYLEEDITOR_TRANSITIONING_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("fullscreen", Fullscreen, IN_FULLSCREEN_STATE, _), ("fullscreen", Fullscreen, IN_FULLSCREEN_STATE, _),
("-moz-modal-dialog", MozModalDialog, IN_MODAL_DIALOG_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-modal-dialog", MozModalDialog, IN_MODAL_DIALOG_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
// TODO(emilio): This is inconsistently named (the capital R). ("-moz-topmost-modal-dialog", MozTopmostModalDialog, IN_TOPMOST_MODAL_DIALOG_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("-moz-focusring", MozFocusRing, IN_FOCUSRING_STATE, _),
("-moz-broken", MozBroken, IN_BROKEN_STATE, _), ("-moz-broken", MozBroken, IN_BROKEN_STATE, _),
("-moz-loading", MozLoading, IN_LOADING_STATE, _), ("-moz-loading", MozLoading, IN_LOADING_STATE, _),
("-moz-suppressed", MozSuppressed, IN_SUPPRESSED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
("-moz-has-dir-attr", MozHasDirAttr, IN_HAS_DIR_ATTR_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-has-dir-attr", MozHasDirAttr, IN_HAS_DIR_ATTR_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("-moz-dir-attr-ltr", MozDirAttrLTR, IN_HAS_DIR_ATTR_LTR_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-dir-attr-ltr", MozDirAttrLTR, IN_HAS_DIR_ATTR_LTR_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("-moz-dir-attr-rtl", MozDirAttrRTL, IN_HAS_DIR_ATTR_RTL_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-dir-attr-rtl", MozDirAttrRTL, IN_HAS_DIR_ATTR_RTL_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("-moz-dir-attr-like-auto", MozDirAttrLikeAuto, IN_HAS_DIR_ATTR_LIKE_AUTO_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-dir-attr-like-auto", MozDirAttrLikeAuto, IN_HAS_DIR_ATTR_LIKE_AUTO_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("-moz-autofill", MozAutofill, IN_AUTOFILL_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
("-moz-autofill-preview", MozAutofillPreview, IN_AUTOFILL_PREVIEW_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME), ("-moz-autofill-preview", MozAutofillPreview, IN_AUTOFILL_PREVIEW_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
("-moz-handler-clicktoplay", MozHandlerClickToPlay, IN_HANDLER_CLICK_TO_PLAY_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
("-moz-handler-vulnerable-updatable", MozHandlerVulnerableUpdatable, IN_HANDLER_VULNERABLE_UPDATABLE_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
("-moz-handler-vulnerable-no-update", MozHandlerVulnerableNoUpdate, IN_HANDLER_VULNERABLE_NO_UPDATE_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
("-moz-handler-disabled", MozHandlerDisabled, IN_HANDLER_DISABLED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
("-moz-handler-blocked", MozHandlerBlocked, IN_HANDLER_BLOCKED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
("-moz-handler-crashed", MozHandlerCrashed, IN_HANDLER_CRASHED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
("-moz-math-increment-script-level", MozMathIncrementScriptLevel, IN_INCREMENT_SCRIPT_LEVEL_STATE, _), ("-moz-math-increment-script-level", MozMathIncrementScriptLevel, IN_INCREMENT_SCRIPT_LEVEL_STATE, _),
("required", Required, IN_REQUIRED_STATE, _), ("required", Required, IN_REQUIRED_STATE, _),
@ -83,19 +77,16 @@ macro_rules! apply_non_ts_list {
("read-only", ReadOnly, IN_READONLY_STATE, _), ("read-only", ReadOnly, IN_READONLY_STATE, _),
("read-write", ReadWrite, IN_READWRITE_STATE, _), ("read-write", ReadWrite, IN_READWRITE_STATE, _),
("-moz-submit-invalid", MozSubmitInvalid, IN_MOZ_SUBMITINVALID_STATE, _), ("-moz-submit-invalid", MozSubmitInvalid, IN_MOZ_SUBMITINVALID_STATE, _),
("-moz-ui-valid", MozUIValid, IN_MOZ_UI_VALID_STATE, _), ("user-valid", UserValid, IN_MOZ_UI_VALID_STATE, _),
("-moz-ui-invalid", MozUIInvalid, IN_MOZ_UI_INVALID_STATE, _), ("user-invalid", UserInvalid, IN_MOZ_UI_INVALID_STATE, _),
("-moz-meter-optimum", MozMeterOptimum, IN_OPTIMUM_STATE, _), ("-moz-meter-optimum", MozMeterOptimum, IN_OPTIMUM_STATE, _),
("-moz-meter-sub-optimum", MozMeterSubOptimum, IN_SUB_OPTIMUM_STATE, _), ("-moz-meter-sub-optimum", MozMeterSubOptimum, IN_SUB_OPTIMUM_STATE, _),
("-moz-meter-sub-sub-optimum", MozMeterSubSubOptimum, IN_SUB_SUB_OPTIMUM_STATE, _), ("-moz-meter-sub-sub-optimum", MozMeterSubSubOptimum, IN_SUB_SUB_OPTIMUM_STATE, _),
("-moz-user-disabled", MozUserDisabled, IN_USER_DISABLED_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
("-moz-first-node", MozFirstNode, _, _), ("-moz-first-node", MozFirstNode, _, _),
("-moz-last-node", MozLastNode, _, _), ("-moz-last-node", MozLastNode, _, _),
("-moz-only-whitespace", MozOnlyWhitespace, _, _), ("-moz-only-whitespace", MozOnlyWhitespace, _, _),
("-moz-native-anonymous", MozNativeAnonymous, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-native-anonymous", MozNativeAnonymous, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("-moz-native-anonymous-no-specificity", MozNativeAnonymousNoSpecificity, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("-moz-use-shadow-tree-root", MozUseShadowTreeRoot, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS), ("-moz-use-shadow-tree-root", MozUseShadowTreeRoot, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("-moz-is-html", MozIsHTML, _, _), ("-moz-is-html", MozIsHTML, _, _),
("-moz-placeholder", MozPlaceholder, _, _), ("-moz-placeholder", MozPlaceholder, _, _),

View file

@ -38,7 +38,7 @@ impl ::selectors::parser::PseudoElement for PseudoElement {
PseudoElement::After | PseudoElement::After |
PseudoElement::Marker | PseudoElement::Marker |
PseudoElement::Placeholder | PseudoElement::Placeholder |
PseudoElement::FileChooserButton PseudoElement::FileSelectorButton
) )
} }
@ -160,15 +160,7 @@ impl PseudoElement {
/// Whether this pseudo-element is enabled for all content. /// Whether this pseudo-element is enabled for all content.
pub fn enabled_in_content(&self) -> bool { pub fn enabled_in_content(&self) -> bool {
match *self { self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME == 0
PseudoElement::MozFocusOuter => {
static_prefs::pref!("layout.css.moz-focus-outer.enabled")
},
PseudoElement::FileChooserButton => {
static_prefs::pref!("layout.css.file-chooser-button.enabled")
},
_ => (self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME) == 0,
}
} }
/// Whether this pseudo is enabled explicitly in UA sheets. /// Whether this pseudo is enabled explicitly in UA sheets.

View file

@ -112,11 +112,7 @@ impl PseudoElement {
% for pseudo in PSEUDOS: % for pseudo in PSEUDOS:
${pseudo_element_variant(pseudo)} => ${pseudo_element_variant(pseudo)} =>
% if pseudo.is_tree_pseudo_element(): % if pseudo.is_tree_pseudo_element():
if static_prefs::pref!("layout.css.xul-tree-pseudos.content.enabled") { structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME,
0
} else {
structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME
},
% elif pseudo.is_anon_box(): % elif pseudo.is_anon_box():
structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS, structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS,
% else: % else:
@ -209,12 +205,10 @@ impl PseudoElement {
if starts_with_ignore_ascii_case(name, "-moz-tree-") { if starts_with_ignore_ascii_case(name, "-moz-tree-") {
return PseudoElement::tree_pseudo_element(name, Box::new([])) return PseudoElement::tree_pseudo_element(name, Box::new([]))
} }
if static_prefs::pref!("layout.css.unknown-webkit-pseudo-element") { const WEBKIT_PREFIX: &str = "-webkit-";
const WEBKIT_PREFIX: &str = "-webkit-"; if starts_with_ignore_ascii_case(name, WEBKIT_PREFIX) {
if starts_with_ignore_ascii_case(name, WEBKIT_PREFIX) { let part = string_as_ascii_lowercase(&name[WEBKIT_PREFIX.len()..]);
let part = string_as_ascii_lowercase(&name[WEBKIT_PREFIX.len()..]); return Some(PseudoElement::UnknownWebkit(part.into()));
return Some(PseudoElement::UnknownWebkit(part.into()));
}
} }
} }
} }

View file

@ -10,21 +10,33 @@ import sys
from io import BytesIO from io import BytesIO
GECKO_DIR = os.path.dirname(__file__.replace('\\', '/')) GECKO_DIR = os.path.dirname(__file__.replace("\\", "/"))
sys.path.insert(0, os.path.join(os.path.dirname(GECKO_DIR), "properties")) sys.path.insert(0, os.path.join(os.path.dirname(GECKO_DIR), "properties"))
import build import build
# Matches lines like `GK_ATOM(foo, "foo", 0x12345678, true, nsStaticAtom, PseudoElementAtom)`. # Matches lines like `GK_ATOM(foo, "foo", 0x12345678, true, nsStaticAtom, PseudoElementAtom)`.
PATTERN = re.compile('^GK_ATOM\(([^,]*),[^"]*"([^"]*)",\s*(0x[0-9a-f]+),\s*[^,]*,\s*([^,]*),\s*([^)]*)\)', PATTERN = re.compile(
re.MULTILINE) '^GK_ATOM\(([^,]*),[^"]*"([^"]*)",\s*(0x[0-9a-f]+),\s*[^,]*,\s*([^,]*),\s*([^)]*)\)',
re.MULTILINE,
)
FILE = "include/nsGkAtomList.h" FILE = "include/nsGkAtomList.h"
def map_atom(ident): def map_atom(ident):
if ident in {"box", "loop", "match", "mod", "ref", if ident in {
"self", "type", "use", "where", "in"}: "box",
"loop",
"match",
"mod",
"ref",
"self",
"type",
"use",
"where",
"in",
}:
return ident + "_" return ident + "_"
return ident return ident
@ -42,7 +54,11 @@ class Atom:
# or "InheritingAnonBox". # or "InheritingAnonBox".
self.atom_type = atom_type self.atom_type = atom_type
if self.is_pseudo_element() or self.is_anon_box() or self.is_tree_pseudo_element(): if (
self.is_pseudo_element()
or self.is_anon_box()
or self.is_tree_pseudo_element()
):
self.pseudo_ident = (ident.split("_", 1))[1] self.pseudo_ident = (ident.split("_", 1))[1]
if self.is_anon_box(): if self.is_anon_box():
@ -82,34 +98,42 @@ def collect_atoms(objdir):
with open(path) as f: with open(path) as f:
content = f.read() content = f.read()
for result in PATTERN.finditer(content): for result in PATTERN.finditer(content):
atoms.append(Atom(result.group(1), result.group(2), result.group(3), atoms.append(
result.group(4), result.group(5))) Atom(
result.group(1),
result.group(2),
result.group(3),
result.group(4),
result.group(5),
)
)
return atoms return atoms
class FileAvoidWrite(BytesIO): class FileAvoidWrite(BytesIO):
"""File-like object that buffers output and only writes if content changed.""" """File-like object that buffers output and only writes if content changed."""
def __init__(self, filename): def __init__(self, filename):
BytesIO.__init__(self) BytesIO.__init__(self)
self.name = filename self.name = filename
def write(self, buf): def write(self, buf):
if isinstance(buf, str): if isinstance(buf, str):
buf = buf.encode('utf-8') buf = buf.encode("utf-8")
BytesIO.write(self, buf) BytesIO.write(self, buf)
def close(self): def close(self):
buf = self.getvalue() buf = self.getvalue()
BytesIO.close(self) BytesIO.close(self)
try: try:
with open(self.name, 'rb') as f: with open(self.name, "rb") as f:
old_content = f.read() old_content = f.read()
if old_content == buf: if old_content == buf:
print("{} is not changed, skip".format(self.name)) print("{} is not changed, skip".format(self.name))
return return
except IOError: except IOError:
pass pass
with open(self.name, 'wb') as f: with open(self.name, "wb") as f:
f.write(buf) f.write(buf)
def __enter__(self): def __enter__(self):
@ -120,45 +144,57 @@ class FileAvoidWrite(BytesIO):
self.close() self.close()
PRELUDE = ''' PRELUDE = """
/* This Source Code Form is subject to the terms of the Mozilla Public /* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
// Autogenerated file created by components/style/gecko/regen_atoms.py. // Autogenerated file created by components/style/gecko/regen_atoms.py.
// DO NOT EDIT DIRECTLY // DO NOT EDIT DIRECTLY
'''[1:] """[
1:
]
RULE_TEMPLATE = ''' RULE_TEMPLATE = """
("{atom}") => {{{{ ("{atom}") => {{{{
#[allow(unsafe_code)] #[allow(unused_unsafe)] #[allow(unsafe_code)] #[allow(unused_unsafe)]
unsafe {{ $crate::string_cache::Atom::from_index_unchecked({index}) }} unsafe {{ $crate::string_cache::Atom::from_index_unchecked({index}) }}
}}}}; }}}};
'''[1:] """[
1:
]
MACRO_TEMPLATE = ''' MACRO_TEMPLATE = """
/// Returns a static atom by passing the literal string it represents. /// Returns a static atom by passing the literal string it represents.
#[macro_export] #[macro_export]
macro_rules! atom {{ macro_rules! atom {{
{body}\ {body}\
}} }}
''' """
def write_atom_macro(atoms, file_name): def write_atom_macro(atoms, file_name):
with FileAvoidWrite(file_name) as f: with FileAvoidWrite(file_name) as f:
f.write(PRELUDE) f.write(PRELUDE)
macro_rules = [RULE_TEMPLATE.format(atom=atom.value, name=atom.ident, index=i) macro_rules = [
for (i, atom) in enumerate(atoms)] RULE_TEMPLATE.format(atom=atom.value, name=atom.ident, index=i)
f.write(MACRO_TEMPLATE.format(body=''.join(macro_rules))) for (i, atom) in enumerate(atoms)
]
f.write(MACRO_TEMPLATE.format(body="".join(macro_rules)))
def write_pseudo_elements(atoms, target_filename): def write_pseudo_elements(atoms, target_filename):
pseudos = [] pseudos = []
for atom in atoms: for atom in atoms:
if atom.type() == "nsCSSPseudoElementStaticAtom" or atom.type() == "nsCSSAnonBoxPseudoStaticAtom": if (
atom.type() == "nsCSSPseudoElementStaticAtom"
or atom.type() == "nsCSSAnonBoxPseudoStaticAtom"
):
pseudos.append(atom) pseudos.append(atom)
pseudo_definition_template = os.path.join(GECKO_DIR, "pseudo_element_definition.mako.rs") pseudo_definition_template = os.path.join(
GECKO_DIR, "pseudo_element_definition.mako.rs"
)
print("cargo:rerun-if-changed={}".format(pseudo_definition_template)) print("cargo:rerun-if-changed={}".format(pseudo_definition_template))
contents = build.render(pseudo_definition_template, PSEUDOS=pseudos) contents = build.render(pseudo_definition_template, PSEUDOS=pseudos)

View file

@ -57,7 +57,7 @@ impl GeckoRestyleDamage {
&mut reset_only, &mut reset_only,
) )
}; };
if reset_only && old_style.custom_properties() != new_style.custom_properties() { if reset_only && !old_style.custom_properties_equal(new_style) {
// The Gecko_CalcStyleDifference call only checks the non-custom // The Gecko_CalcStyleDifference call only checks the non-custom
// property structs, so we check the custom properties here. Since // property structs, so we check the custom properties here. Since
// they generate no damage themselves, we can skip this check if we // they generate no damage themselves, we can skip this check if we

View file

@ -11,16 +11,13 @@ use crate::invalidation::element::document_state::InvalidationMatchingData;
use crate::selector_parser::{Direction, SelectorParser}; use crate::selector_parser::{Direction, SelectorParser};
use crate::str::starts_with_ignore_ascii_case; use crate::str::starts_with_ignore_ascii_case;
use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};
use crate::values::serialize_atom_identifier; use crate::values::{AtomIdent, AtomString};
use cssparser::{BasicParseError, BasicParseErrorKind, Parser}; use cssparser::{BasicParseError, BasicParseErrorKind, Parser};
use cssparser::{CowRcStr, SourceLocation, ToCss, Token}; use cssparser::{CowRcStr, SourceLocation, ToCss, Token};
use selectors::parser::SelectorParseErrorKind; use selectors::parser::{ParseErrorRecovery, SelectorParseErrorKind};
use selectors::parser::{self as selector_parser, Selector};
use selectors::visitor::SelectorVisitor;
use selectors::SelectorList; use selectors::SelectorList;
use std::fmt; use std::fmt;
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_}; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_};
use thin_slice::ThinBoxedSlice;
pub use crate::gecko::pseudo_element::{ pub use crate::gecko::pseudo_element::{
PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT, PSEUDO_COUNT, PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT, PSEUDO_COUNT,
@ -39,7 +36,7 @@ bitflags! {
} }
/// The type used to store the language argument to the `:lang` pseudo-class. /// The type used to store the language argument to the `:lang` pseudo-class.
pub type Lang = Atom; pub type Lang = AtomIdent;
macro_rules! pseudo_class_name { macro_rules! pseudo_class_name {
([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => { ([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
@ -54,11 +51,6 @@ macro_rules! pseudo_class_name {
Lang(Lang), Lang(Lang),
/// The `:dir` pseudo-class. /// The `:dir` pseudo-class.
Dir(Direction), Dir(Direction),
/// The non-standard `:-moz-any` pseudo-class.
///
/// TODO(emilio): We disallow combinators and pseudos here, so we
/// should use SimpleSelector instead
MozAny(ThinBoxedSlice<Selector<SelectorImpl>>),
/// The non-standard `:-moz-locale-dir` pseudo-class. /// The non-standard `:-moz-locale-dir` pseudo-class.
MozLocaleDir(Direction), MozLocaleDir(Direction),
} }
@ -77,7 +69,7 @@ impl ToCss for NonTSPseudoClass {
$(NonTSPseudoClass::$name => concat!(":", $css),)* $(NonTSPseudoClass::$name => concat!(":", $css),)*
NonTSPseudoClass::Lang(ref s) => { NonTSPseudoClass::Lang(ref s) => {
dest.write_str(":lang(")?; dest.write_str(":lang(")?;
serialize_atom_identifier(s, dest)?; s.to_css(dest)?;
return dest.write_char(')'); return dest.write_char(')');
}, },
NonTSPseudoClass::MozLocaleDir(ref dir) => { NonTSPseudoClass::MozLocaleDir(ref dir) => {
@ -90,17 +82,6 @@ impl ToCss for NonTSPseudoClass {
dir.to_css(&mut CssWriter::new(dest))?; dir.to_css(&mut CssWriter::new(dest))?;
return dest.write_char(')') return dest.write_char(')')
}, },
NonTSPseudoClass::MozAny(ref selectors) => {
dest.write_str(":-moz-any(")?;
let mut iter = selectors.iter();
let first = iter.next().expect(":-moz-any must have at least 1 selector");
first.to_css(dest)?;
for selector in iter {
dest.write_str(", ")?;
selector.to_css(dest)?;
}
return dest.write_char(')')
}
} }
} }
} }
@ -121,6 +102,10 @@ impl NonTSPseudoClass {
"-moz-full-screen" => Some(NonTSPseudoClass::Fullscreen), "-moz-full-screen" => Some(NonTSPseudoClass::Fullscreen),
"-moz-read-only" => Some(NonTSPseudoClass::ReadOnly), "-moz-read-only" => Some(NonTSPseudoClass::ReadOnly),
"-moz-read-write" => Some(NonTSPseudoClass::ReadWrite), "-moz-read-write" => Some(NonTSPseudoClass::ReadWrite),
"-moz-focusring" => Some(NonTSPseudoClass::FocusVisible),
"-moz-ui-valid" => Some(NonTSPseudoClass::UserValid),
"-moz-ui-invalid" => Some(NonTSPseudoClass::UserInvalid),
"-webkit-autofill" => Some(NonTSPseudoClass::Autofill),
_ => None, _ => None,
} }
} }
@ -144,8 +129,7 @@ impl NonTSPseudoClass {
$(NonTSPseudoClass::$name => check_flag!($flags),)* $(NonTSPseudoClass::$name => check_flag!($flags),)*
NonTSPseudoClass::MozLocaleDir(_) | NonTSPseudoClass::MozLocaleDir(_) |
NonTSPseudoClass::Lang(_) | NonTSPseudoClass::Lang(_) |
NonTSPseudoClass::Dir(_) | NonTSPseudoClass::Dir(_) => false,
NonTSPseudoClass::MozAny(_) => false,
} }
} }
} }
@ -155,8 +139,11 @@ impl NonTSPseudoClass {
/// Returns whether the pseudo-class is enabled in content sheets. /// Returns whether the pseudo-class is enabled in content sheets.
#[inline] #[inline]
fn is_enabled_in_content(&self) -> bool { fn is_enabled_in_content(&self) -> bool {
if matches!(*self, NonTSPseudoClass::FocusVisible) { if let NonTSPseudoClass::Autofill = *self {
return static_prefs::pref!("layout.css.focus-visible.enabled"); return static_prefs::pref!("layout.css.autofill.enabled");
}
if let NonTSPseudoClass::MozSubmitInvalid = *self {
return static_prefs::pref!("layout.css.moz-submit-invalid.enabled");
} }
!self.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME) !self.has_any_flag(NonTSPseudoClassFlag::PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME)
} }
@ -177,8 +164,7 @@ impl NonTSPseudoClass {
$(NonTSPseudoClass::$name => flag!($state),)* $(NonTSPseudoClass::$name => flag!($state),)*
NonTSPseudoClass::Dir(..) | NonTSPseudoClass::Dir(..) |
NonTSPseudoClass::MozLocaleDir(..) | NonTSPseudoClass::MozLocaleDir(..) |
NonTSPseudoClass::Lang(..) | NonTSPseudoClass::Lang(..) => ElementState::empty(),
NonTSPseudoClass::MozAny(..) => ElementState::empty(),
} }
} }
} }
@ -200,15 +186,11 @@ impl NonTSPseudoClass {
self.state_flag().is_empty() && self.state_flag().is_empty() &&
!matches!( !matches!(
*self, *self,
// :-moz-any is handled by the revalidation visitor walking // :dir() depends on state only, but doesn't use state_flag
// the things inside it; it does not need to cause // because its semantics don't quite match. Nevertheless, it
// revalidation on its own. // doesn't need cache revalidation, because we already compare
NonTSPseudoClass::MozAny(_) | // states for elements and candidates.
// :dir() depends on state only, but doesn't use state_flag NonTSPseudoClass::Dir(_) |
// because its semantics don't quite match. Nevertheless, it
// doesn't need cache revalidation, because we already compare
// states for elements and candidates.
NonTSPseudoClass::Dir(_) |
// :-moz-is-html only depends on the state of the document and // :-moz-is-html only depends on the state of the document and
// the namespace of the element; the former is invariant // the namespace of the element; the former is invariant
// across all the elements involved and the latter is already // across all the elements involved and the latter is already
@ -216,7 +198,6 @@ impl NonTSPseudoClass {
NonTSPseudoClass::MozIsHTML | NonTSPseudoClass::MozIsHTML |
// We prevent style sharing for NAC. // We prevent style sharing for NAC.
NonTSPseudoClass::MozNativeAnonymous | NonTSPseudoClass::MozNativeAnonymous |
NonTSPseudoClass::MozNativeAnonymousNoSpecificity |
// :-moz-placeholder is parsed but never matches. // :-moz-placeholder is parsed but never matches.
NonTSPseudoClass::MozPlaceholder | NonTSPseudoClass::MozPlaceholder |
// :-moz-locale-dir and :-moz-window-inactive depend only on // :-moz-locale-dir and :-moz-window-inactive depend only on
@ -248,26 +229,6 @@ impl ::selectors::parser::NonTSPseudoClass for NonTSPseudoClass {
NonTSPseudoClass::Hover | NonTSPseudoClass::Active | NonTSPseudoClass::Focus NonTSPseudoClass::Hover | NonTSPseudoClass::Active | NonTSPseudoClass::Focus
) )
} }
#[inline]
fn has_zero_specificity(&self) -> bool {
matches!(*self, NonTSPseudoClass::MozNativeAnonymousNoSpecificity)
}
fn visit<V>(&self, visitor: &mut V) -> bool
where
V: SelectorVisitor<Impl = Self::Impl>,
{
if let NonTSPseudoClass::MozAny(ref selectors) = *self {
for selector in selectors.iter() {
if !selector.visit(visitor) {
return false;
}
}
}
true
}
} }
/// The dummy struct we use to implement our selector parsing. /// The dummy struct we use to implement our selector parsing.
@ -276,12 +237,10 @@ pub struct SelectorImpl;
impl ::selectors::SelectorImpl for SelectorImpl { impl ::selectors::SelectorImpl for SelectorImpl {
type ExtraMatchingData = InvalidationMatchingData; type ExtraMatchingData = InvalidationMatchingData;
type AttrValue = Atom; type AttrValue = AtomString;
type Identifier = Atom; type Identifier = AtomIdent;
type ClassName = Atom; type LocalName = AtomIdent;
type PartName = Atom; type NamespacePrefix = AtomIdent;
type LocalName = Atom;
type NamespacePrefix = Atom;
type NamespaceUrl = Namespace; type NamespaceUrl = Namespace;
type BorrowedNamespaceUrl = WeakNamespace; type BorrowedNamespaceUrl = WeakNamespace;
type BorrowedLocalName = WeakAtom; type BorrowedLocalName = WeakAtom;
@ -344,13 +303,26 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
#[inline] #[inline]
fn parse_is_and_where(&self) -> bool { fn parse_is_and_where(&self) -> bool {
self.in_user_agent_stylesheet() || true
static_prefs::pref!("layout.css.is-where-selectors.enabled") }
#[inline]
fn is_and_where_error_recovery(&self) -> ParseErrorRecovery {
if static_prefs::pref!("layout.css.is-and-where-better-error-recovery.enabled") {
ParseErrorRecovery::IgnoreInvalidSelector
} else {
ParseErrorRecovery::DiscardList
}
} }
#[inline] #[inline]
fn parse_part(&self) -> bool { fn parse_part(&self) -> bool {
self.chrome_rules_enabled() || static_prefs::pref!("layout.css.shadow-parts.enabled") true
}
#[inline]
fn is_is_alias(&self, function: &str) -> bool {
function.eq_ignore_ascii_case("-moz-any")
} }
fn parse_non_ts_pseudo_class( fn parse_non_ts_pseudo_class(
@ -378,7 +350,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
let pseudo_class = match_ignore_ascii_case! { &name, let pseudo_class = match_ignore_ascii_case! { &name,
"lang" => { "lang" => {
let name = parser.expect_ident_or_string()?; let name = parser.expect_ident_or_string()?;
NonTSPseudoClass::Lang(Atom::from(name.as_ref())) NonTSPseudoClass::Lang(Lang::from(name.as_ref()))
}, },
"-moz-locale-dir" => { "-moz-locale-dir" => {
NonTSPseudoClass::MozLocaleDir(Direction::parse(parser)?) NonTSPseudoClass::MozLocaleDir(Direction::parse(parser)?)
@ -386,14 +358,6 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
"dir" => { "dir" => {
NonTSPseudoClass::Dir(Direction::parse(parser)?) NonTSPseudoClass::Dir(Direction::parse(parser)?)
}, },
"-moz-any" => {
NonTSPseudoClass::MozAny(
selector_parser::parse_compound_selector_list(
self,
parser,
)?.into()
)
},
_ => return Err(parser.new_custom_error( _ => return Err(parser.new_custom_error(
SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone()) SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone())
)) ))
@ -464,10 +428,10 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
} }
fn default_namespace(&self) -> Option<Namespace> { fn default_namespace(&self) -> Option<Namespace> {
self.namespaces.default.as_ref().map(|ns| ns.clone()) self.namespaces.default.clone()
} }
fn namespace_for_prefix(&self, prefix: &Atom) -> Option<Namespace> { fn namespace_for_prefix(&self, prefix: &AtomIdent) -> Option<Namespace> {
self.namespaces.prefixes.get(prefix).cloned() self.namespaces.prefixes.get(prefix).cloned()
} }
} }

View file

@ -14,7 +14,10 @@ use crate::gecko_bindings::structs::ServoElementSnapshot;
use crate::gecko_bindings::structs::ServoElementSnapshotFlags as Flags; use crate::gecko_bindings::structs::ServoElementSnapshotFlags as Flags;
use crate::gecko_bindings::structs::ServoElementSnapshotTable; use crate::gecko_bindings::structs::ServoElementSnapshotTable;
use crate::invalidation::element::element_wrapper::ElementSnapshot; use crate::invalidation::element::element_wrapper::ElementSnapshot;
use crate::selector_parser::AttrValue;
use crate::string_cache::{Atom, Namespace}; use crate::string_cache::{Atom, Namespace};
use crate::values::{AtomIdent, AtomString};
use crate::LocalName;
use crate::WeakAtom; use crate::WeakAtom;
use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator}; use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator};
use selectors::attr::{CaseSensitivity, NamespaceConstraint}; use selectors::attr::{CaseSensitivity, NamespaceConstraint};
@ -74,10 +77,10 @@ impl GeckoElementSnapshot {
#[inline] #[inline]
pub fn each_attr_changed<F>(&self, mut callback: F) pub fn each_attr_changed<F>(&self, mut callback: F)
where where
F: FnMut(&Atom), F: FnMut(&AtomIdent),
{ {
for attr in self.mChangedAttrNames.iter() { for attr in self.mChangedAttrNames.iter() {
unsafe { Atom::with(attr.mRawPtr, &mut callback) } unsafe { AtomIdent::with(attr.mRawPtr, &mut callback) }
} }
} }
@ -85,8 +88,8 @@ impl GeckoElementSnapshot {
pub fn attr_matches( pub fn attr_matches(
&self, &self,
ns: &NamespaceConstraint<&Namespace>, ns: &NamespaceConstraint<&Namespace>,
local_name: &Atom, local_name: &LocalName,
operation: &AttrSelectorOperation<&Atom>, operation: &AttrSelectorOperation<&AttrValue>,
) -> bool { ) -> bool {
unsafe { unsafe {
match *operation { match *operation {
@ -188,7 +191,7 @@ impl ElementSnapshot for GeckoElementSnapshot {
} }
#[inline] #[inline]
fn is_part(&self, name: &Atom) -> bool { fn is_part(&self, name: &AtomIdent) -> bool {
let attr = match snapshot_helpers::find_attr(&*self.mAttrs, &atom!("part")) { let attr = match snapshot_helpers::find_attr(&*self.mAttrs, &atom!("part")) {
Some(attr) => attr, Some(attr) => attr,
None => return false, None => return false,
@ -198,12 +201,12 @@ impl ElementSnapshot for GeckoElementSnapshot {
} }
#[inline] #[inline]
fn imported_part(&self, name: &Atom) -> Option<Atom> { fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> {
snapshot_helpers::imported_part(&*self.mAttrs, name) snapshot_helpers::imported_part(&*self.mAttrs, name)
} }
#[inline] #[inline]
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
if !self.has_any(Flags::MaybeClass) { if !self.has_any(Flags::MaybeClass) {
return false; return false;
} }
@ -214,7 +217,7 @@ impl ElementSnapshot for GeckoElementSnapshot {
#[inline] #[inline]
fn each_class<F>(&self, callback: F) fn each_class<F>(&self, callback: F)
where where
F: FnMut(&Atom), F: FnMut(&AtomIdent),
{ {
if !self.has_any(Flags::MaybeClass) { if !self.has_any(Flags::MaybeClass) {
return; return;
@ -224,12 +227,12 @@ impl ElementSnapshot for GeckoElementSnapshot {
} }
#[inline] #[inline]
fn lang_attr(&self) -> Option<Atom> { fn lang_attr(&self) -> Option<AtomString> {
let ptr = unsafe { bindings::Gecko_SnapshotLangValue(self) }; let ptr = unsafe { bindings::Gecko_SnapshotLangValue(self) };
if ptr.is_null() { if ptr.is_null() {
None None
} else { } else {
Some(unsafe { Atom::from_addrefed(ptr) }) Some(AtomString(unsafe { Atom::from_addrefed(ptr) }))
} }
} }
} }

View file

@ -6,7 +6,9 @@
use crate::gecko_bindings::bindings; use crate::gecko_bindings::bindings;
use crate::gecko_bindings::structs::{self, nsAtom}; use crate::gecko_bindings::structs::{self, nsAtom};
use crate::string_cache::{Atom, WeakAtom}; use crate::string_cache::WeakAtom;
use crate::values::AtomIdent;
use crate::Atom;
use crate::CaseSensitivityExt; use crate::CaseSensitivityExt;
use selectors::attr::CaseSensitivity; use selectors::attr::CaseSensitivity;
@ -32,27 +34,28 @@ unsafe fn ptr<T>(attr: &structs::nsAttrValue) -> *const T {
unsafe fn get_class_or_part_from_attr(attr: &structs::nsAttrValue) -> Class { unsafe fn get_class_or_part_from_attr(attr: &structs::nsAttrValue) -> Class {
debug_assert!(bindings::Gecko_AssertClassAttrValueIsSane(attr)); debug_assert!(bindings::Gecko_AssertClassAttrValueIsSane(attr));
let base_type = base_type(attr); let base_type = base_type(attr);
if base_type == structs::nsAttrValue_ValueBaseType_eStringBase {
return Class::None;
}
if base_type == structs::nsAttrValue_ValueBaseType_eAtomBase { if base_type == structs::nsAttrValue_ValueBaseType_eAtomBase {
return Class::One(ptr::<nsAtom>(attr)); return Class::One(ptr::<nsAtom>(attr));
} }
debug_assert_eq!(base_type, structs::nsAttrValue_ValueBaseType_eOtherBase); if base_type == structs::nsAttrValue_ValueBaseType_eOtherBase {
let container = ptr::<structs::MiscContainer>(attr);
let container = ptr::<structs::MiscContainer>(attr); debug_assert_eq!(
debug_assert_eq!( (*container).mType,
(*container).mType, structs::nsAttrValue_ValueType_eAtomArray
structs::nsAttrValue_ValueType_eAtomArray );
); // NOTE: Bindgen doesn't deal with AutoTArray, so cast it below.
let array = (*container) let array: *mut u8 = *(*container)
.__bindgen_anon_1 .__bindgen_anon_1
.mValue .mValue
.as_ref() .as_ref()
.__bindgen_anon_1 .__bindgen_anon_1
.mAtomArray .mAtomArray
.as_ref(); .as_ref();
Class::More(&***array) let array = array as *const structs::nsTArray<structs::RefPtr<nsAtom>>;
return Class::More(&**array);
}
debug_assert_eq!(base_type, structs::nsAttrValue_ValueBaseType_eStringBase);
Class::None
} }
#[inline(always)] #[inline(always)]
@ -85,8 +88,8 @@ pub fn get_id(attrs: &[structs::AttrArray_InternalAttr]) -> Option<&WeakAtom> {
#[inline(always)] #[inline(always)]
pub(super) fn each_exported_part( pub(super) fn each_exported_part(
attrs: &[structs::AttrArray_InternalAttr], attrs: &[structs::AttrArray_InternalAttr],
name: &Atom, name: &AtomIdent,
mut callback: impl FnMut(&Atom), mut callback: impl FnMut(&AtomIdent),
) { ) {
let attr = match find_attr(attrs, &atom!("exportparts")) { let attr = match find_attr(attrs, &atom!("exportparts")) {
Some(attr) => attr, Some(attr) => attr,
@ -100,7 +103,7 @@ pub(super) fn each_exported_part(
unsafe { unsafe {
for atom in std::slice::from_raw_parts(atoms, length) { for atom in std::slice::from_raw_parts(atoms, length) {
Atom::with(*atom, &mut callback) AtomIdent::with(*atom, &mut callback)
} }
} }
} }
@ -108,21 +111,21 @@ pub(super) fn each_exported_part(
#[inline(always)] #[inline(always)]
pub(super) fn imported_part( pub(super) fn imported_part(
attrs: &[structs::AttrArray_InternalAttr], attrs: &[structs::AttrArray_InternalAttr],
name: &Atom, name: &AtomIdent,
) -> Option<Atom> { ) -> Option<AtomIdent> {
let attr = find_attr(attrs, &atom!("exportparts"))?; let attr = find_attr(attrs, &atom!("exportparts"))?;
let atom = unsafe { bindings::Gecko_Element_ImportedPart(attr, name.as_ptr()) }; let atom = unsafe { bindings::Gecko_Element_ImportedPart(attr, name.as_ptr()) };
if atom.is_null() { if atom.is_null() {
return None; return None;
} }
Some(unsafe { Atom::from_raw(atom) }) Some(AtomIdent(unsafe { Atom::from_raw(atom) }))
} }
/// Given a class or part name, a case sensitivity, and an array of attributes, /// Given a class or part name, a case sensitivity, and an array of attributes,
/// returns whether the attribute has that name. /// returns whether the attribute has that name.
#[inline(always)] #[inline(always)]
pub fn has_class_or_part( pub fn has_class_or_part(
name: &Atom, name: &AtomIdent,
case_sensitivity: CaseSensitivity, case_sensitivity: CaseSensitivity,
attr: &structs::nsAttrValue, attr: &structs::nsAttrValue,
) -> bool { ) -> bool {
@ -131,7 +134,8 @@ pub fn has_class_or_part(
Class::One(atom) => unsafe { case_sensitivity.eq_atom(name, WeakAtom::new(atom)) }, Class::One(atom) => unsafe { case_sensitivity.eq_atom(name, WeakAtom::new(atom)) },
Class::More(atoms) => match case_sensitivity { Class::More(atoms) => match case_sensitivity {
CaseSensitivity::CaseSensitive => { CaseSensitivity::CaseSensitive => {
atoms.iter().any(|atom| atom.mRawPtr == name.as_ptr()) let name_ptr = name.as_ptr();
atoms.iter().any(|atom| atom.mRawPtr == name_ptr)
}, },
CaseSensitivity::AsciiCaseInsensitive => unsafe { CaseSensitivity::AsciiCaseInsensitive => unsafe {
atoms atoms
@ -147,15 +151,15 @@ pub fn has_class_or_part(
#[inline(always)] #[inline(always)]
pub fn each_class_or_part<F>(attr: &structs::nsAttrValue, mut callback: F) pub fn each_class_or_part<F>(attr: &structs::nsAttrValue, mut callback: F)
where where
F: FnMut(&Atom), F: FnMut(&AtomIdent),
{ {
unsafe { unsafe {
match get_class_or_part_from_attr(attr) { match get_class_or_part_from_attr(attr) {
Class::None => {}, Class::None => {},
Class::One(atom) => Atom::with(atom, callback), Class::One(atom) => AtomIdent::with(atom, callback),
Class::More(atoms) => { Class::More(atoms) => {
for atom in atoms { for atom in atoms {
Atom::with(atom.mRawPtr, &mut callback) AtomIdent::with(atom.mRawPtr, &mut callback)
} }
}, },
} }

View file

@ -109,7 +109,11 @@ impl CssUrlData {
/// Returns true if this URL looks like a fragment. /// Returns true if this URL looks like a fragment.
/// See https://drafts.csswg.org/css-values/#local-urls /// See https://drafts.csswg.org/css-values/#local-urls
pub fn is_fragment(&self) -> bool { pub fn is_fragment(&self) -> bool {
self.as_str().chars().next().map_or(false, |c| c == '#') self.as_str()
.as_bytes()
.iter()
.next()
.map_or(false, |b| *b == b'#')
} }
/// Return the unresolved url as string, or the empty string if it's /// Return the unresolved url as string, or the empty string if it's
@ -285,14 +289,13 @@ impl SpecifiedImageUrl {
/// Provides an alternate method for parsing that associates the URL /// Provides an alternate method for parsing that associates the URL
/// with anonymous CORS headers. /// with anonymous CORS headers.
pub fn parse_with_cors_anonymous<'i, 't>( pub fn parse_with_cors_mode<'i, 't>(
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
cors_mode: CorsMode,
) -> Result<Self, ParseError<'i>> { ) -> Result<Self, ParseError<'i>> {
Ok(SpecifiedImageUrl(SpecifiedUrl::parse_with_cors_mode( Ok(SpecifiedImageUrl(SpecifiedUrl::parse_with_cors_mode(
context, context, input, cors_mode,
input,
CorsMode::Anonymous,
)?)) )?))
} }
} }

View file

@ -70,8 +70,9 @@ use crate::stylist::CascadeData;
use crate::values::computed::font::GenericFontFamily; use crate::values::computed::font::GenericFontFamily;
use crate::values::computed::Length; use crate::values::computed::Length;
use crate::values::specified::length::FontBaseSize; use crate::values::specified::length::FontBaseSize;
use crate::values::{AtomIdent, AtomString};
use crate::CaseSensitivityExt; use crate::CaseSensitivityExt;
use app_units::Au; use crate::LocalName;
use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut};
use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator}; use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator};
use selectors::attr::{CaseSensitivity, NamespaceConstraint}; use selectors::attr::{CaseSensitivity, NamespaceConstraint};
@ -131,7 +132,7 @@ impl<'ld> TDocument for GeckoDocument<'ld> {
} }
#[inline] #[inline]
fn elements_with_id<'a>(&self, id: &Atom) -> Result<&'a [GeckoElement<'ld>], ()> fn elements_with_id<'a>(&self, id: &AtomIdent) -> Result<&'a [GeckoElement<'ld>], ()>
where where
Self: 'a, Self: 'a,
{ {
@ -187,7 +188,7 @@ impl<'lr> TShadowRoot for GeckoShadowRoot<'lr> {
} }
#[inline] #[inline]
fn elements_with_id<'a>(&self, id: &Atom) -> Result<&'a [GeckoElement<'lr>], ()> fn elements_with_id<'a>(&self, id: &AtomIdent) -> Result<&'a [GeckoElement<'lr>], ()>
where where
Self: 'a, Self: 'a,
{ {
@ -802,7 +803,7 @@ impl<'le> GeckoElement<'le> {
return false; return false;
} }
let host = self.containing_shadow_host().unwrap(); let host = self.containing_shadow_host().unwrap();
host.is_svg_element() && host.local_name() == &*local_name!("use") host.is_svg_element() && host.local_name() == &**local_name!("use")
} }
fn css_transitions_info(&self) -> FxHashMap<LonghandId, Arc<AnimationValue>> { fn css_transitions_info(&self) -> FxHashMap<LonghandId, Arc<AnimationValue>> {
@ -922,14 +923,12 @@ fn get_animation_rule(
#[derive(Debug)] #[derive(Debug)]
/// Gecko font metrics provider /// Gecko font metrics provider
pub struct GeckoFontMetricsProvider { pub struct GeckoFontMetricsProvider {
/// Cache of base font sizes for each language /// Cache of base font sizes for each language. Usually will have 1 element.
/// ///
/// Usually will have 1 element. /// This may be slow on pages using more languages, might be worth
/// /// optimizing by caching lang->group mapping separately and/or using a
// This may be slow on pages using more languages, might be worth optimizing /// hashmap on larger loads.
// by caching lang->group mapping separately and/or using a hashmap on larger pub font_size_cache: RefCell<Vec<(Atom, DefaultFontSizes)>>,
// loads.
pub font_size_cache: RefCell<Vec<(Atom, crate::gecko_bindings::structs::FontSizePrefs)>>,
} }
impl GeckoFontMetricsProvider { impl GeckoFontMetricsProvider {
@ -952,8 +951,9 @@ impl FontMetricsProvider for GeckoFontMetricsProvider {
return sizes.1.size_for_generic(font_family); return sizes.1.size_for_generic(font_family);
} }
let sizes = unsafe { bindings::Gecko_GetBaseSize(font_name.as_ptr()) }; let sizes = unsafe { bindings::Gecko_GetBaseSize(font_name.as_ptr()) };
let size = sizes.size_for_generic(font_family);
cache.push((font_name.clone(), sizes)); cache.push((font_name.clone(), sizes));
sizes.size_for_generic(font_family) size
} }
fn query( fn query(
@ -967,7 +967,7 @@ impl FontMetricsProvider for GeckoFontMetricsProvider {
None => return Default::default(), None => return Default::default(),
}; };
let size = Au::from(base_size.resolve(context)); let size = base_size.resolve(context);
let style = context.style(); let style = context.style();
let (wm, font) = match base_size { let (wm, font) = match base_size {
@ -987,15 +987,15 @@ impl FontMetricsProvider for GeckoFontMetricsProvider {
pc, pc,
vertical_metrics, vertical_metrics,
font.gecko(), font.gecko(),
size.0, size,
// we don't use the user font set in a media query // we don't use the user font set in a media query
!context.in_media_query, !context.in_media_query,
) )
}; };
FontMetrics { FontMetrics {
x_height: Some(Au(gecko_metrics.mXSize).into()), x_height: Some(gecko_metrics.mXSize),
zero_advance_measure: if gecko_metrics.mChSize >= 0 { zero_advance_measure: if gecko_metrics.mChSize.px() >= 0. {
Some(Au(gecko_metrics.mChSize).into()) Some(gecko_metrics.mChSize)
} else { } else {
None None
}, },
@ -1003,20 +1003,31 @@ impl FontMetricsProvider for GeckoFontMetricsProvider {
} }
} }
impl structs::FontSizePrefs { /// The default font sizes for generic families for a given language group.
#[derive(Debug)]
#[repr(C)]
pub struct DefaultFontSizes {
variable: Length,
serif: Length,
sans_serif: Length,
monospace: Length,
cursive: Length,
fantasy: Length,
}
impl DefaultFontSizes {
fn size_for_generic(&self, font_family: GenericFontFamily) -> Length { fn size_for_generic(&self, font_family: GenericFontFamily) -> Length {
Au(match font_family { match font_family {
GenericFontFamily::None => self.mDefaultVariableSize, GenericFontFamily::None => self.variable,
GenericFontFamily::Serif => self.mDefaultSerifSize, GenericFontFamily::Serif => self.serif,
GenericFontFamily::SansSerif => self.mDefaultSansSerifSize, GenericFontFamily::SansSerif => self.sans_serif,
GenericFontFamily::Monospace => self.mDefaultMonospaceSize, GenericFontFamily::Monospace => self.monospace,
GenericFontFamily::Cursive => self.mDefaultCursiveSize, GenericFontFamily::Cursive => self.cursive,
GenericFontFamily::Fantasy => self.mDefaultFantasySize, GenericFontFamily::Fantasy => self.fantasy,
GenericFontFamily::MozEmoji => unreachable!( GenericFontFamily::MozEmoji => unreachable!(
"Should never get here, since this doesn't (yet) appear on font family" "Should never get here, since this doesn't (yet) appear on font family"
), ),
}) }
.into()
} }
} }
@ -1261,7 +1272,7 @@ impl<'le> TElement for GeckoElement<'le> {
} }
#[inline] #[inline]
fn has_attr(&self, namespace: &Namespace, attr: &Atom) -> bool { fn has_attr(&self, namespace: &Namespace, attr: &AtomIdent) -> bool {
unsafe { bindings::Gecko_HasAttr(self.0, namespace.0.as_ptr(), attr.as_ptr()) } unsafe { bindings::Gecko_HasAttr(self.0, namespace.0.as_ptr(), attr.as_ptr()) }
} }
@ -1288,7 +1299,7 @@ impl<'le> TElement for GeckoElement<'le> {
fn each_class<F>(&self, callback: F) fn each_class<F>(&self, callback: F)
where where
F: FnMut(&Atom), F: FnMut(&AtomIdent),
{ {
let attr = match self.get_class_attr() { let attr = match self.get_class_attr() {
Some(c) => c, Some(c) => c,
@ -1299,16 +1310,16 @@ impl<'le> TElement for GeckoElement<'le> {
} }
#[inline] #[inline]
fn each_exported_part<F>(&self, name: &Atom, callback: F) fn each_exported_part<F>(&self, name: &AtomIdent, callback: F)
where where
F: FnMut(&Atom), F: FnMut(&AtomIdent),
{ {
snapshot_helpers::each_exported_part(self.attrs(), name, callback) snapshot_helpers::each_exported_part(self.attrs(), name, callback)
} }
fn each_part<F>(&self, callback: F) fn each_part<F>(&self, callback: F)
where where
F: FnMut(&Atom), F: FnMut(&AtomIdent),
{ {
let attr = match self.get_part_attr() { let attr = match self.get_part_attr() {
Some(c) => c, Some(c) => c,
@ -1606,7 +1617,7 @@ impl<'le> TElement for GeckoElement<'le> {
if ptr.is_null() { if ptr.is_null() {
None None
} else { } else {
Some(unsafe { Atom::from_addrefed(ptr) }) Some(AtomString(unsafe { Atom::from_addrefed(ptr) }))
} }
} }
@ -1614,8 +1625,8 @@ impl<'le> TElement for GeckoElement<'le> {
// Gecko supports :lang() from CSS Selectors 3, which only accepts a // Gecko supports :lang() from CSS Selectors 3, which only accepts a
// single language tag, and which performs simple dash-prefix matching // single language tag, and which performs simple dash-prefix matching
// on it. // on it.
let override_lang_ptr = match &override_lang { let override_lang_ptr = match override_lang {
&Some(Some(ref atom)) => atom.as_ptr(), Some(Some(ref atom)) => atom.as_ptr(),
_ => ptr::null_mut(), _ => ptr::null_mut(),
}; };
unsafe { unsafe {
@ -1629,7 +1640,7 @@ impl<'le> TElement for GeckoElement<'le> {
} }
fn is_html_document_body_element(&self) -> bool { fn is_html_document_body_element(&self) -> bool {
if self.local_name() != &*local_name!("body") { if self.local_name() != &**local_name!("body") {
return false; return false;
} }
@ -1886,8 +1897,8 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
fn attr_matches( fn attr_matches(
&self, &self,
ns: &NamespaceConstraint<&Namespace>, ns: &NamespaceConstraint<&Namespace>,
local_name: &Atom, local_name: &LocalName,
operation: &AttrSelectorOperation<&Atom>, operation: &AttrSelectorOperation<&AttrValue>,
) -> bool { ) -> bool {
unsafe { unsafe {
match *operation { match *operation {
@ -2006,6 +2017,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
{ {
use selectors::matching::*; use selectors::matching::*;
match *pseudo_class { match *pseudo_class {
NonTSPseudoClass::Autofill |
NonTSPseudoClass::Defined | NonTSPseudoClass::Defined |
NonTSPseudoClass::Focus | NonTSPseudoClass::Focus |
NonTSPseudoClass::Enabled | NonTSPseudoClass::Enabled |
@ -2013,18 +2025,13 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
NonTSPseudoClass::Checked | NonTSPseudoClass::Checked |
NonTSPseudoClass::Fullscreen | NonTSPseudoClass::Fullscreen |
NonTSPseudoClass::Indeterminate | NonTSPseudoClass::Indeterminate |
NonTSPseudoClass::MozInert |
NonTSPseudoClass::PlaceholderShown | NonTSPseudoClass::PlaceholderShown |
NonTSPseudoClass::Target | NonTSPseudoClass::Target |
NonTSPseudoClass::Valid | NonTSPseudoClass::Valid |
NonTSPseudoClass::Invalid | NonTSPseudoClass::Invalid |
NonTSPseudoClass::MozUIValid |
NonTSPseudoClass::MozBroken | NonTSPseudoClass::MozBroken |
NonTSPseudoClass::MozUserDisabled |
NonTSPseudoClass::MozSuppressed |
NonTSPseudoClass::MozLoading | NonTSPseudoClass::MozLoading |
NonTSPseudoClass::MozHandlerBlocked |
NonTSPseudoClass::MozHandlerDisabled |
NonTSPseudoClass::MozHandlerCrashed |
NonTSPseudoClass::Required | NonTSPseudoClass::Required |
NonTSPseudoClass::Optional | NonTSPseudoClass::Optional |
NonTSPseudoClass::ReadOnly | NonTSPseudoClass::ReadOnly |
@ -2034,16 +2041,13 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
NonTSPseudoClass::MozDragOver | NonTSPseudoClass::MozDragOver |
NonTSPseudoClass::MozDevtoolsHighlighted | NonTSPseudoClass::MozDevtoolsHighlighted |
NonTSPseudoClass::MozStyleeditorTransitioning | NonTSPseudoClass::MozStyleeditorTransitioning |
NonTSPseudoClass::MozFocusRing |
NonTSPseudoClass::MozHandlerClickToPlay |
NonTSPseudoClass::MozHandlerVulnerableUpdatable |
NonTSPseudoClass::MozHandlerVulnerableNoUpdate |
NonTSPseudoClass::MozMathIncrementScriptLevel | NonTSPseudoClass::MozMathIncrementScriptLevel |
NonTSPseudoClass::InRange | NonTSPseudoClass::InRange |
NonTSPseudoClass::OutOfRange | NonTSPseudoClass::OutOfRange |
NonTSPseudoClass::Default | NonTSPseudoClass::Default |
NonTSPseudoClass::MozSubmitInvalid | NonTSPseudoClass::MozSubmitInvalid |
NonTSPseudoClass::MozUIInvalid | NonTSPseudoClass::UserValid |
NonTSPseudoClass::UserInvalid |
NonTSPseudoClass::MozMeterOptimum | NonTSPseudoClass::MozMeterOptimum |
NonTSPseudoClass::MozMeterSubOptimum | NonTSPseudoClass::MozMeterSubOptimum |
NonTSPseudoClass::MozMeterSubSubOptimum | NonTSPseudoClass::MozMeterSubSubOptimum |
@ -2051,8 +2055,8 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
NonTSPseudoClass::MozDirAttrLTR | NonTSPseudoClass::MozDirAttrLTR |
NonTSPseudoClass::MozDirAttrRTL | NonTSPseudoClass::MozDirAttrRTL |
NonTSPseudoClass::MozDirAttrLikeAuto | NonTSPseudoClass::MozDirAttrLikeAuto |
NonTSPseudoClass::MozAutofill |
NonTSPseudoClass::MozModalDialog | NonTSPseudoClass::MozModalDialog |
NonTSPseudoClass::MozTopmostModalDialog |
NonTSPseudoClass::Active | NonTSPseudoClass::Active |
NonTSPseudoClass::Hover | NonTSPseudoClass::Hover |
NonTSPseudoClass::MozAutofillPreview => { NonTSPseudoClass::MozAutofillPreview => {
@ -2098,15 +2102,15 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
} }
true true
}, },
NonTSPseudoClass::MozNativeAnonymous | NonTSPseudoClass::MozNativeAnonymous => self.is_in_native_anonymous_subtree(),
NonTSPseudoClass::MozNativeAnonymousNoSpecificity => {
self.is_in_native_anonymous_subtree()
},
NonTSPseudoClass::MozUseShadowTreeRoot => self.is_root_of_use_element_shadow_tree(), NonTSPseudoClass::MozUseShadowTreeRoot => self.is_root_of_use_element_shadow_tree(),
NonTSPseudoClass::MozTableBorderNonzero => unsafe { NonTSPseudoClass::MozTableBorderNonzero => unsafe {
bindings::Gecko_IsTableBorderNonzero(self.0) bindings::Gecko_IsTableBorderNonzero(self.0)
}, },
NonTSPseudoClass::MozBrowserFrame => unsafe { bindings::Gecko_IsBrowserFrame(self.0) }, NonTSPseudoClass::MozBrowserFrame => unsafe { bindings::Gecko_IsBrowserFrame(self.0) },
NonTSPseudoClass::MozSelectListBox => unsafe {
bindings::Gecko_IsSelectListBox(self.0)
},
NonTSPseudoClass::MozIsHTML => self.is_html_element_in_html_document(), NonTSPseudoClass::MozIsHTML => self.is_html_element_in_html_document(),
NonTSPseudoClass::MozLWTheme => self.document_theme() != DocumentTheme::Doc_Theme_None, NonTSPseudoClass::MozLWTheme => self.document_theme() != DocumentTheme::Doc_Theme_None,
NonTSPseudoClass::MozLWThemeBrightText => { NonTSPseudoClass::MozLWThemeBrightText => {
@ -2124,10 +2128,6 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
self.document_state().contains(state_bit) self.document_state().contains(state_bit)
}, },
NonTSPseudoClass::MozPlaceholder => false, NonTSPseudoClass::MozPlaceholder => false,
NonTSPseudoClass::MozAny(ref sels) => context.nest(|context| {
sels.iter()
.any(|s| matches_complex_selector(s.iter(), self, context, flags_setter))
}),
NonTSPseudoClass::Lang(ref lang_arg) => self.match_element_lang(None, lang_arg), NonTSPseudoClass::Lang(ref lang_arg) => self.match_element_lang(None, lang_arg),
NonTSPseudoClass::MozLocaleDir(ref dir) => { NonTSPseudoClass::MozLocaleDir(ref dir) => {
let state_bit = DocumentState::NS_DOCUMENT_STATE_RTL_LOCALE; let state_bit = DocumentState::NS_DOCUMENT_STATE_RTL_LOCALE;
@ -2170,7 +2170,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
} }
#[inline] #[inline]
fn has_id(&self, id: &Atom, case_sensitivity: CaseSensitivity) -> bool { fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
if !self.has_id() { if !self.has_id() {
return false; return false;
} }
@ -2184,7 +2184,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
} }
#[inline] #[inline]
fn is_part(&self, name: &Atom) -> bool { fn is_part(&self, name: &AtomIdent) -> bool {
let attr = match self.get_part_attr() { let attr = match self.get_part_attr() {
Some(c) => c, Some(c) => c,
None => return false, None => return false,
@ -2194,12 +2194,12 @@ impl<'le> ::selectors::Element for GeckoElement<'le> {
} }
#[inline] #[inline]
fn imported_part(&self, name: &Atom) -> Option<Atom> { fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> {
snapshot_helpers::imported_part(self.attrs(), name) snapshot_helpers::imported_part(self.attrs(), name)
} }
#[inline(always)] #[inline(always)]
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
let attr = match self.get_class_attr() { let attr = match self.get_class_attr() {
Some(c) => c, Some(c) => c,
None => return false, None => return false,

View file

@ -13,6 +13,7 @@ use std::slice;
impl<T> Deref for nsTArray<T> { impl<T> Deref for nsTArray<T> {
type Target = [T]; type Target = [T];
#[inline]
fn deref<'a>(&'a self) -> &'a [T] { fn deref<'a>(&'a self) -> &'a [T] {
unsafe { slice::from_raw_parts(self.slice_begin(), self.header().mLength as usize) } unsafe { slice::from_raw_parts(self.slice_begin(), self.header().mLength as usize) }
} }

View file

@ -42,12 +42,6 @@ pub mod namespace;
pub use self::namespace::{Namespace, WeakNamespace}; pub use self::namespace::{Namespace, WeakNamespace};
macro_rules! local_name {
($s:tt) => {
atom!($s)
};
}
/// A handle to a Gecko atom. This is a type that can represent either: /// A handle to a Gecko atom. This is a type that can represent either:
/// ///
/// * A strong reference to a dynamic atom (an `nsAtom` pointer), in which case /// * A strong reference to a dynamic atom (an `nsAtom` pointer), in which case
@ -153,6 +147,13 @@ impl PartialEq for WeakAtom {
} }
} }
impl PartialEq<Atom> for WeakAtom {
#[inline]
fn eq(&self, other: &Atom) -> bool {
self == &**other
}
}
unsafe impl Send for Atom {} unsafe impl Send for Atom {}
unsafe impl Sync for Atom {} unsafe impl Sync for Atom {}
unsafe impl Sync for WeakAtom {} unsafe impl Sync for WeakAtom {}

View file

@ -47,18 +47,9 @@ impl PrecomputedHash for Namespace {
} }
/// A Gecko WeakNamespace is a wrapped WeakAtom. /// A Gecko WeakNamespace is a wrapped WeakAtom.
#[derive(Hash)] #[derive(Deref, Hash)]
pub struct WeakNamespace(WeakAtom); pub struct WeakNamespace(WeakAtom);
impl Deref for WeakNamespace {
type Target = WeakAtom;
#[inline]
fn deref(&self) -> &WeakAtom {
&self.0
}
}
impl Deref for Namespace { impl Deref for Namespace {
type Target = WeakNamespace; type Target = WeakNamespace;

View file

@ -9,7 +9,8 @@ use crate::dom::TElement;
use crate::element_state::ElementState; use crate::element_state::ElementState;
use crate::selector_parser::{AttrValue, NonTSPseudoClass, PseudoElement, SelectorImpl}; use crate::selector_parser::{AttrValue, NonTSPseudoClass, PseudoElement, SelectorImpl};
use crate::selector_parser::{Snapshot, SnapshotMap}; use crate::selector_parser::{Snapshot, SnapshotMap};
use crate::{Atom, CaseSensitivityExt, LocalName, Namespace, WeakAtom}; use crate::values::AtomIdent;
use crate::{CaseSensitivityExt, LocalName, Namespace, WeakAtom};
use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint}; use selectors::attr::{AttrSelectorOperation, CaseSensitivity, NamespaceConstraint};
use selectors::matching::{ElementSelectorFlags, MatchingContext}; use selectors::matching::{ElementSelectorFlags, MatchingContext};
use selectors::{Element, OpaqueElement}; use selectors::{Element, OpaqueElement};
@ -56,20 +57,20 @@ pub trait ElementSnapshot: Sized {
/// Whether this snapshot contains the class `name`. Should only be called /// Whether this snapshot contains the class `name`. Should only be called
/// if `has_attrs()` returns true. /// if `has_attrs()` returns true.
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool; fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool;
/// Whether this snapshot represents the part named `name`. Should only be /// Whether this snapshot represents the part named `name`. Should only be
/// called if `has_attrs()` returns true. /// called if `has_attrs()` returns true.
fn is_part(&self, name: &Atom) -> bool; fn is_part(&self, name: &AtomIdent) -> bool;
/// See Element::imported_part. /// See Element::imported_part.
fn imported_part(&self, name: &Atom) -> Option<Atom>; fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent>;
/// A callback that should be called for each class of the snapshot. Should /// A callback that should be called for each class of the snapshot. Should
/// only be called if `has_attrs()` returns true. /// only be called if `has_attrs()` returns true.
fn each_class<F>(&self, _: F) fn each_class<F>(&self, _: F)
where where
F: FnMut(&Atom); F: FnMut(&AtomIdent);
/// The `xml:lang=""` or `lang=""` attribute value per this snapshot. /// The `xml:lang=""` or `lang=""` attribute value per this snapshot.
fn lang_attr(&self) -> Option<AttrValue>; fn lang_attr(&self) -> Option<AttrValue>;
@ -177,16 +178,6 @@ where
// Some pseudo-classes need special handling to evaluate them against // Some pseudo-classes need special handling to evaluate them against
// the snapshot. // the snapshot.
match *pseudo_class { match *pseudo_class {
#[cfg(feature = "gecko")]
NonTSPseudoClass::MozAny(ref selectors) => {
use selectors::matching::matches_complex_selector;
return context.nest(|context| {
selectors
.iter()
.any(|s| matches_complex_selector(s.iter(), self, context, _setter))
});
},
// :dir is implemented in terms of state flags, but which state flag // :dir is implemented in terms of state flags, but which state flag
// it maps to depends on the argument to :dir. That means we can't // it maps to depends on the argument to :dir. That means we can't
// just add its state flags to the NonTSPseudoClass, because if we // just add its state flags to the NonTSPseudoClass, because if we
@ -239,6 +230,15 @@ where
} }
}, },
#[cfg(feature = "gecko")]
NonTSPseudoClass::MozSelectListBox => {
if let Some(snapshot) = self.snapshot() {
if snapshot.has_other_pseudo_class_state() {
return snapshot.mIsSelectListBox();
}
}
},
// :lang() needs to match using the closest ancestor xml:lang="" or // :lang() needs to match using the closest ancestor xml:lang="" or
// lang="" attribtue from snapshots. // lang="" attribtue from snapshots.
NonTSPseudoClass::Lang(ref lang_arg) => { NonTSPseudoClass::Lang(ref lang_arg) => {
@ -352,7 +352,7 @@ where
} }
} }
fn has_id(&self, id: &Atom, case_sensitivity: CaseSensitivity) -> bool { fn has_id(&self, id: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
match self.snapshot() { match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() => snapshot Some(snapshot) if snapshot.has_attrs() => snapshot
.id_attr() .id_attr()
@ -361,21 +361,21 @@ where
} }
} }
fn is_part(&self, name: &Atom) -> bool { fn is_part(&self, name: &AtomIdent) -> bool {
match self.snapshot() { match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() => snapshot.is_part(name), Some(snapshot) if snapshot.has_attrs() => snapshot.is_part(name),
_ => self.element.is_part(name), _ => self.element.is_part(name),
} }
} }
fn imported_part(&self, name: &Atom) -> Option<Atom> { fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> {
match self.snapshot() { match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() => snapshot.imported_part(name), Some(snapshot) if snapshot.has_attrs() => snapshot.imported_part(name),
_ => self.element.imported_part(name), _ => self.element.imported_part(name),
} }
} }
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool { fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
match self.snapshot() { match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() => snapshot.has_class(name, case_sensitivity), Some(snapshot) if snapshot.has_attrs() => snapshot.has_class(name, case_sensitivity),
_ => self.element.has_class(name, case_sensitivity), _ => self.element.has_class(name, case_sensitivity),

View file

@ -478,7 +478,7 @@ impl<'a> SelectorVisitor for SelectorDependencyCollector<'a> {
Component::Class(..) => &mut self.map.class_to_selector, Component::Class(..) => &mut self.map.class_to_selector,
_ => unreachable!(), _ => unreachable!(),
}; };
let entry = match map.try_entry(atom.clone(), self.quirks_mode) { let entry = match map.try_entry(atom.0.clone(), self.quirks_mode) {
Ok(entry) => entry, Ok(entry) => entry,
Err(err) => { Err(err) => {
*self.alloc_error = Some(err); *self.alloc_error = Some(err);
@ -506,6 +506,12 @@ impl<'a> SelectorVisitor for SelectorDependencyCollector<'a> {
NonTSPseudoClass::MozTableBorderNonzero => local_name!("border"), NonTSPseudoClass::MozTableBorderNonzero => local_name!("border"),
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
NonTSPseudoClass::MozBrowserFrame => local_name!("mozbrowser"), NonTSPseudoClass::MozBrowserFrame => local_name!("mozbrowser"),
#[cfg(feature = "gecko")]
NonTSPseudoClass::MozSelectListBox => {
// This depends on two attributes.
return self.add_attr_dependency(local_name!("multiple")) &&
self.add_attr_dependency(local_name!("size"));
},
NonTSPseudoClass::Lang(..) => local_name!("lang"), NonTSPseudoClass::Lang(..) => local_name!("lang"),
_ => return true, _ => return true,
}; };

View file

@ -216,13 +216,13 @@ where
// TODO(emilio): Do this more efficiently! // TODO(emilio): Do this more efficiently!
snapshot.each_class(|c| { snapshot.each_class(|c| {
if !element.has_class(c, CaseSensitivity::CaseSensitive) { if !element.has_class(c, CaseSensitivity::CaseSensitive) {
classes_removed.push(c.clone()) classes_removed.push(c.0.clone())
} }
}); });
element.each_class(|c| { element.each_class(|c| {
if !snapshot.has_class(c, CaseSensitivity::CaseSensitive) { if !snapshot.has_class(c, CaseSensitivity::CaseSensitive) {
classes_added.push(c.clone()) classes_added.push(c.0.clone())
} }
}) })
} }

View file

@ -7,34 +7,46 @@
#![deny(unsafe_code)] #![deny(unsafe_code)]
use crate::context::QuirksMode;
use crate::dom::{TDocument, TElement, TNode}; use crate::dom::{TDocument, TElement, TNode};
use crate::hash::HashSet;
use crate::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper}; use crate::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper};
use crate::invalidation::element::restyle_hints::RestyleHint; use crate::invalidation::element::restyle_hints::RestyleHint;
use crate::media_queries::Device; use crate::media_queries::Device;
use crate::selector_map::{MaybeCaseInsensitiveHashMap, PrecomputedHashMap};
use crate::selector_parser::{SelectorImpl, Snapshot, SnapshotMap}; use crate::selector_parser::{SelectorImpl, Snapshot, SnapshotMap};
use crate::shared_lock::SharedRwLockReadGuard; use crate::shared_lock::SharedRwLockReadGuard;
use crate::stylesheets::{CssRule, StylesheetInDocument}; use crate::stylesheets::{CssRule, StylesheetInDocument};
use crate::stylesheets::{EffectiveRules, EffectiveRulesIterator};
use crate::values::AtomIdent;
use crate::Atom; use crate::Atom;
use crate::CaseSensitivityExt;
use crate::LocalName as SelectorLocalName; use crate::LocalName as SelectorLocalName;
use fxhash::FxHasher;
use selectors::attr::CaseSensitivity;
use selectors::parser::{Component, LocalName, Selector}; use selectors::parser::{Component, LocalName, Selector};
use std::hash::BuildHasherDefault;
type FxHashSet<K> = HashSet<K, BuildHasherDefault<FxHasher>>; /// The kind of change that happened for a given rule.
#[repr(u32)]
#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)]
pub enum RuleChangeKind {
/// The rule was inserted.
Insertion,
/// The rule was removed.
Removal,
/// Some change in the rule which we don't know about, and could have made
/// the rule change in any way.
Generic,
/// A change in the declarations of a style rule.
StyleRuleDeclarations,
}
/// A style sheet invalidation represents a kind of element or subtree that may /// A style sheet invalidation represents a kind of element or subtree that may
/// need to be restyled. Whether it represents a whole subtree or just a single /// need to be restyled. Whether it represents a whole subtree or just a single
/// element is determined by whether the invalidation is stored in the /// element is determined by the given InvalidationKind in
/// StylesheetInvalidationSet's invalid_scopes or invalid_elements table. /// StylesheetInvalidationSet's maps.
#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)] #[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)]
enum Invalidation { enum Invalidation {
/// An element with a given id. /// An element with a given id.
ID(Atom), ID(AtomIdent),
/// An element with a given class name. /// An element with a given class name.
Class(Atom), Class(AtomIdent),
/// An element with a given local name. /// An element with a given local name.
LocalName { LocalName {
name: SelectorLocalName, name: SelectorLocalName,
@ -50,56 +62,35 @@ impl Invalidation {
fn is_id_or_class(&self) -> bool { fn is_id_or_class(&self) -> bool {
matches!(*self, Invalidation::ID(..) | Invalidation::Class(..)) matches!(*self, Invalidation::ID(..) | Invalidation::Class(..))
} }
}
fn matches<E>( /// Whether we should invalidate just the element, or the whole subtree within
&self, /// it.
element: E, #[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd)]
snapshot: Option<&Snapshot>, enum InvalidationKind {
case_sensitivity: CaseSensitivity, None = 0,
) -> bool Element,
where Scope,
E: TElement, }
{
match *self {
Invalidation::Class(ref class) => {
if element.has_class(class, case_sensitivity) {
return true;
}
if let Some(snapshot) = snapshot { impl std::ops::BitOrAssign for InvalidationKind {
if snapshot.has_class(class, case_sensitivity) { #[inline]
return true; fn bitor_assign(&mut self, other: Self) {
} *self = std::cmp::max(*self, other);
} }
}, }
Invalidation::ID(ref id) => {
if let Some(ref element_id) = element.id() {
if case_sensitivity.eq_atom(element_id, id) {
return true;
}
}
if let Some(snapshot) = snapshot { impl InvalidationKind {
if let Some(ref old_id) = snapshot.id_attr() { #[inline]
if case_sensitivity.eq_atom(old_id, id) { fn is_scope(self) -> bool {
return true; matches!(self, Self::Scope)
} }
}
} #[inline]
}, fn add(&mut self, other: Option<&InvalidationKind>) {
Invalidation::LocalName { if let Some(other) = other {
ref name, *self |= *other;
ref lower_name,
} => {
// This could look at the quirks mode of the document, instead
// of testing against both names, but it's probably not worth
// it.
let local_name = element.local_name();
return *local_name == **name || *local_name == **lower_name;
},
} }
false
} }
} }
@ -107,31 +98,24 @@ impl Invalidation {
/// ///
/// TODO(emilio): We might be able to do the same analysis for media query /// TODO(emilio): We might be able to do the same analysis for media query
/// changes too (or even selector changes?). /// changes too (or even selector changes?).
#[derive(MallocSizeOf)] #[derive(Debug, Default, MallocSizeOf)]
pub struct StylesheetInvalidationSet { pub struct StylesheetInvalidationSet {
/// The subtrees we know we have to restyle so far. classes: MaybeCaseInsensitiveHashMap<Atom, InvalidationKind>,
invalid_scopes: FxHashSet<Invalidation>, ids: MaybeCaseInsensitiveHashMap<Atom, InvalidationKind>,
/// The elements we know we have to restyle so far. local_names: PrecomputedHashMap<SelectorLocalName, InvalidationKind>,
invalid_elements: FxHashSet<Invalidation>,
/// Whether the whole document should be restyled.
fully_invalid: bool, fully_invalid: bool,
} }
impl StylesheetInvalidationSet { impl StylesheetInvalidationSet {
/// Create an empty `StylesheetInvalidationSet`. /// Create an empty `StylesheetInvalidationSet`.
pub fn new() -> Self { pub fn new() -> Self {
Self { Default::default()
invalid_scopes: FxHashSet::default(),
invalid_elements: FxHashSet::default(),
fully_invalid: false,
}
} }
/// Mark the DOM tree styles' as fully invalid. /// Mark the DOM tree styles' as fully invalid.
pub fn invalidate_fully(&mut self) { pub fn invalidate_fully(&mut self) {
debug!("StylesheetInvalidationSet::invalidate_fully"); debug!("StylesheetInvalidationSet::invalidate_fully");
self.invalid_scopes.clear(); self.clear();
self.invalid_elements.clear();
self.fully_invalid = true; self.fully_invalid = true;
} }
@ -157,22 +141,19 @@ impl StylesheetInvalidationSet {
return; // Nothing to do here. return; // Nothing to do here.
} }
let quirks_mode = device.quirks_mode();
for rule in stylesheet.effective_rules(device, guard) { for rule in stylesheet.effective_rules(device, guard) {
self.collect_invalidations_for_rule(rule, guard, device); self.collect_invalidations_for_rule(rule, guard, device, quirks_mode);
if self.fully_invalid { if self.fully_invalid {
self.invalid_scopes.clear();
self.invalid_elements.clear();
break; break;
} }
} }
debug!(" > resulting class invalidations: {:?}", self.classes);
debug!(" > resulting id invalidations: {:?}", self.ids);
debug!( debug!(
" > resulting subtree invalidations: {:?}", " > resulting local name invalidations: {:?}",
self.invalid_scopes self.local_names
);
debug!(
" > resulting self invalidations: {:?}",
self.invalid_elements
); );
debug!(" > fully_invalid: {}", self.fully_invalid); debug!(" > fully_invalid: {}", self.fully_invalid);
} }
@ -198,21 +179,84 @@ impl StylesheetInvalidationSet {
have_invalidations have_invalidations
} }
/// Returns whether there's no invalidation to process.
pub fn is_empty(&self) -> bool {
!self.fully_invalid &&
self.classes.is_empty() &&
self.ids.is_empty() &&
self.local_names.is_empty()
}
fn invalidation_kind_for<E>(
&self,
element: E,
snapshot: Option<&Snapshot>,
quirks_mode: QuirksMode,
) -> InvalidationKind
where
E: TElement,
{
debug_assert!(!self.fully_invalid);
let mut kind = InvalidationKind::None;
if !self.classes.is_empty() {
element.each_class(|c| {
kind.add(self.classes.get(c, quirks_mode));
});
if kind.is_scope() {
return kind;
}
if let Some(snapshot) = snapshot {
snapshot.each_class(|c| {
kind.add(self.classes.get(c, quirks_mode));
});
if kind.is_scope() {
return kind;
}
}
}
if !self.ids.is_empty() {
if let Some(ref id) = element.id() {
kind.add(self.ids.get(id, quirks_mode));
if kind.is_scope() {
return kind;
}
}
if let Some(ref old_id) = snapshot.and_then(|s| s.id_attr()) {
kind.add(self.ids.get(old_id, quirks_mode));
if kind.is_scope() {
return kind;
}
}
}
if !self.local_names.is_empty() {
kind.add(self.local_names.get(element.local_name()));
}
kind
}
/// Clears the invalidation set without processing. /// Clears the invalidation set without processing.
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.invalid_scopes.clear(); self.classes.clear();
self.invalid_elements.clear(); self.ids.clear();
self.local_names.clear();
self.fully_invalid = false; self.fully_invalid = false;
debug_assert!(self.is_empty());
} }
fn process_invalidations<E>(&self, element: E, snapshots: Option<&SnapshotMap>) -> bool fn process_invalidations<E>(&self, element: E, snapshots: Option<&SnapshotMap>) -> bool
where where
E: TElement, E: TElement,
{ {
debug!( debug!("Stylist::process_invalidations({:?}, {:?})", element, self);
"Stylist::process_invalidations({:?}, {:?}, {:?})",
element, self.invalid_scopes, self.invalid_elements,
);
{ {
let mut data = match element.mutate_data() { let mut data = match element.mutate_data() {
@ -227,22 +271,18 @@ impl StylesheetInvalidationSet {
} }
} }
if self.invalid_scopes.is_empty() && self.invalid_elements.is_empty() { if self.is_empty() {
debug!("process_invalidations: empty invalidation set"); debug!("process_invalidations: empty invalidation set");
return false; return false;
} }
let case_sensitivity = element let quirks_mode = element.as_node().owner_doc().quirks_mode();
.as_node() self.process_invalidations_in_subtree(element, snapshots, quirks_mode)
.owner_doc()
.quirks_mode()
.classes_and_ids_case_sensitivity();
self.process_invalidations_in_subtree(element, snapshots, case_sensitivity)
} }
/// Process style invalidations in a given subtree. This traverses the /// Process style invalidations in a given subtree. This traverses the
/// subtree looking for elements that match the invalidations in /// subtree looking for elements that match the invalidations in our hash
/// invalid_scopes and invalid_elements. /// map members.
/// ///
/// Returns whether it invalidated at least one element's style. /// Returns whether it invalidated at least one element's style.
#[allow(unsafe_code)] #[allow(unsafe_code)]
@ -250,7 +290,7 @@ impl StylesheetInvalidationSet {
&self, &self,
element: E, element: E,
snapshots: Option<&SnapshotMap>, snapshots: Option<&SnapshotMap>,
case_sensitivity: CaseSensitivity, quirks_mode: QuirksMode,
) -> bool ) -> bool
where where
E: TElement, E: TElement,
@ -275,31 +315,24 @@ impl StylesheetInvalidationSet {
let element_wrapper = snapshots.map(|s| ElementWrapper::new(element, s)); let element_wrapper = snapshots.map(|s| ElementWrapper::new(element, s));
let snapshot = element_wrapper.as_ref().and_then(|e| e.snapshot()); let snapshot = element_wrapper.as_ref().and_then(|e| e.snapshot());
for invalidation in &self.invalid_scopes {
if invalidation.matches(element, snapshot, case_sensitivity) { match self.invalidation_kind_for(element, snapshot, quirks_mode) {
InvalidationKind::None => {},
InvalidationKind::Element => {
debug!( debug!(
"process_invalidations_in_subtree: {:?} matched subtree {:?}", "process_invalidations_in_subtree: {:?} matched self",
element, invalidation element
);
data.hint.insert(RestyleHint::RESTYLE_SELF);
},
InvalidationKind::Scope => {
debug!(
"process_invalidations_in_subtree: {:?} matched subtree",
element
); );
data.hint.insert(RestyleHint::restyle_subtree()); data.hint.insert(RestyleHint::restyle_subtree());
return true; return true;
} },
}
let mut self_invalid = false;
if !data.hint.contains(RestyleHint::RESTYLE_SELF) {
for invalidation in &self.invalid_elements {
if invalidation.matches(element, snapshot, case_sensitivity) {
debug!(
"process_invalidations_in_subtree: {:?} matched self {:?}",
element, invalidation
);
data.hint.insert(RestyleHint::RESTYLE_SELF);
self_invalid = true;
break;
}
}
} }
let mut any_children_invalid = false; let mut any_children_invalid = false;
@ -311,7 +344,7 @@ impl StylesheetInvalidationSet {
}; };
any_children_invalid |= any_children_invalid |=
self.process_invalidations_in_subtree(child, snapshots, case_sensitivity); self.process_invalidations_in_subtree(child, snapshots, quirks_mode);
} }
if any_children_invalid { if any_children_invalid {
@ -322,9 +355,11 @@ impl StylesheetInvalidationSet {
unsafe { element.set_dirty_descendants() } unsafe { element.set_dirty_descendants() }
} }
return self_invalid || any_children_invalid; data.hint.contains(RestyleHint::RESTYLE_SELF) || any_children_invalid
} }
/// TODO(emilio): Reuse the bucket stuff from selectormap? That handles
/// :is() / :where() etc.
fn scan_component( fn scan_component(
component: &Component<SelectorImpl>, component: &Component<SelectorImpl>,
invalidation: &mut Option<Invalidation>, invalidation: &mut Option<Invalidation>,
@ -334,7 +369,7 @@ impl StylesheetInvalidationSet {
ref name, ref name,
ref lower_name, ref lower_name,
}) => { }) => {
if invalidation.as_ref().map_or(true, |s| !s.is_id_or_class()) { if invalidation.is_none() {
*invalidation = Some(Invalidation::LocalName { *invalidation = Some(Invalidation::LocalName {
name: name.clone(), name: name.clone(),
lower_name: lower_name.clone(), lower_name: lower_name.clone(),
@ -342,12 +377,12 @@ impl StylesheetInvalidationSet {
} }
}, },
Component::Class(ref class) => { Component::Class(ref class) => {
if invalidation.as_ref().map_or(true, |s| !s.is_id()) { if invalidation.as_ref().map_or(true, |s| !s.is_id_or_class()) {
*invalidation = Some(Invalidation::Class(class.clone())); *invalidation = Some(Invalidation::Class(class.clone()));
} }
}, },
Component::ID(ref id) => { Component::ID(ref id) => {
if invalidation.is_none() { if invalidation.as_ref().map_or(true, |s| !s.is_id()) {
*invalidation = Some(Invalidation::ID(id.clone())); *invalidation = Some(Invalidation::ID(id.clone()));
} }
}, },
@ -371,7 +406,11 @@ impl StylesheetInvalidationSet {
/// prefer to generate subtree invalidations for the outermost part /// prefer to generate subtree invalidations for the outermost part
/// of the selector, to reduce the amount of traversal we need to do /// of the selector, to reduce the amount of traversal we need to do
/// when flushing invalidations. /// when flushing invalidations.
fn collect_invalidations(&mut self, selector: &Selector<SelectorImpl>) { fn collect_invalidations(
&mut self,
selector: &Selector<SelectorImpl>,
quirks_mode: QuirksMode,
) {
debug!( debug!(
"StylesheetInvalidationSet::collect_invalidations({:?})", "StylesheetInvalidationSet::collect_invalidations({:?})",
selector selector
@ -404,13 +443,14 @@ impl StylesheetInvalidationSet {
if let Some(s) = subtree_invalidation { if let Some(s) = subtree_invalidation {
debug!(" > Found subtree invalidation: {:?}", s); debug!(" > Found subtree invalidation: {:?}", s);
if self.invalid_scopes.try_insert(s).is_ok() { if self.insert_invalidation(s, InvalidationKind::Scope, quirks_mode) {
return; return;
} }
} }
if let Some(s) = element_invalidation { if let Some(s) = element_invalidation {
debug!(" > Found element invalidation: {:?}", s); debug!(" > Found element invalidation: {:?}", s);
if self.invalid_elements.try_insert(s).is_ok() { if self.insert_invalidation(s, InvalidationKind::Element, quirks_mode) {
return; return;
} }
} }
@ -418,7 +458,120 @@ impl StylesheetInvalidationSet {
// The selector was of a form that we can't handle. Any element could // The selector was of a form that we can't handle. Any element could
// match it, so let's just bail out. // match it, so let's just bail out.
debug!(" > Can't handle selector or OOMd, marking fully invalid"); debug!(" > Can't handle selector or OOMd, marking fully invalid");
self.fully_invalid = true; self.invalidate_fully()
}
fn insert_invalidation(
&mut self,
invalidation: Invalidation,
kind: InvalidationKind,
quirks_mode: QuirksMode,
) -> bool {
match invalidation {
Invalidation::Class(c) => {
let entry = match self.classes.try_entry(c.0, quirks_mode) {
Ok(e) => e,
Err(..) => return false,
};
*entry.or_insert(InvalidationKind::None) |= kind;
},
Invalidation::ID(i) => {
let entry = match self.ids.try_entry(i.0, quirks_mode) {
Ok(e) => e,
Err(..) => return false,
};
*entry.or_insert(InvalidationKind::None) |= kind;
},
Invalidation::LocalName { name, lower_name } => {
let insert_lower = name != lower_name;
let entry = match self.local_names.try_entry(name) {
Ok(e) => e,
Err(..) => return false,
};
*entry.or_insert(InvalidationKind::None) |= kind;
if insert_lower {
let entry = match self.local_names.try_entry(lower_name) {
Ok(e) => e,
Err(..) => return false,
};
*entry.or_insert(InvalidationKind::None) |= kind;
}
},
}
true
}
/// Collects invalidations for a given CSS rule, if not fully invalid
/// already.
///
/// TODO(emilio): we can't check whether the rule is inside a non-effective
/// subtree, we potentially could do that.
pub fn rule_changed<S>(
&mut self,
stylesheet: &S,
rule: &CssRule,
guard: &SharedRwLockReadGuard,
device: &Device,
quirks_mode: QuirksMode,
change_kind: RuleChangeKind,
) where
S: StylesheetInDocument,
{
use crate::stylesheets::CssRule::*;
debug!("StylesheetInvalidationSet::rule_changed");
if self.fully_invalid {
return;
}
if !stylesheet.enabled() || !stylesheet.is_effective_for_device(device, guard) {
debug!(" > Stylesheet was not effective");
return; // Nothing to do here.
}
let is_generic_change = change_kind == RuleChangeKind::Generic;
match *rule {
Namespace(..) => {
// It's not clear what handling changes for this correctly would
// look like.
},
CounterStyle(..) |
Page(..) |
Viewport(..) |
FontFeatureValues(..) |
FontFace(..) |
Keyframes(..) |
Style(..) => {
if is_generic_change {
// TODO(emilio): We need to do this for selector / keyframe
// name / font-face changes, because we don't have the old
// selector / name. If we distinguish those changes
// specially, then we can at least use this invalidation for
// style declaration changes.
return self.invalidate_fully();
}
self.collect_invalidations_for_rule(rule, guard, device, quirks_mode)
},
Document(..) | Import(..) | Media(..) | Supports(..) => {
if !is_generic_change &&
!EffectiveRules::is_effective(guard, device, quirks_mode, rule)
{
return;
}
let rules =
EffectiveRulesIterator::effective_children(device, quirks_mode, guard, rule);
for rule in rules {
self.collect_invalidations_for_rule(rule, guard, device, quirks_mode);
if self.fully_invalid {
break;
}
}
},
}
} }
/// Collects invalidations for a given CSS rule. /// Collects invalidations for a given CSS rule.
@ -427,6 +580,7 @@ impl StylesheetInvalidationSet {
rule: &CssRule, rule: &CssRule,
guard: &SharedRwLockReadGuard, guard: &SharedRwLockReadGuard,
device: &Device, device: &Device,
quirks_mode: QuirksMode,
) { ) {
use crate::stylesheets::CssRule::*; use crate::stylesheets::CssRule::*;
debug!("StylesheetInvalidationSet::collect_invalidations_for_rule"); debug!("StylesheetInvalidationSet::collect_invalidations_for_rule");
@ -436,7 +590,7 @@ impl StylesheetInvalidationSet {
Style(ref lock) => { Style(ref lock) => {
let style_rule = lock.read_with(guard); let style_rule = lock.read_with(guard);
for selector in &style_rule.selectors.0 { for selector in &style_rule.selectors.0 {
self.collect_invalidations(selector); self.collect_invalidations(selector, quirks_mode);
if self.fully_invalid { if self.fully_invalid {
return; return;
} }

View file

@ -131,22 +131,26 @@ pub use crate::gecko_string_cache as string_cache;
pub use crate::gecko_string_cache::Atom; pub use crate::gecko_string_cache::Atom;
/// The namespace prefix type for Gecko, which is just an atom. /// The namespace prefix type for Gecko, which is just an atom.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub type Prefix = crate::gecko_string_cache::Atom; pub type Prefix = crate::values::AtomIdent;
/// The local name of an element for Gecko, which is just an atom. /// The local name of an element for Gecko, which is just an atom.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub type LocalName = crate::gecko_string_cache::Atom; pub type LocalName = crate::values::AtomIdent;
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
pub use crate::gecko_string_cache::Namespace; pub use crate::gecko_string_cache::Namespace;
#[cfg(feature = "servo")]
pub use html5ever::LocalName;
#[cfg(feature = "servo")]
pub use html5ever::Namespace;
#[cfg(feature = "servo")]
pub use html5ever::Prefix;
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
pub use servo_atoms::Atom; pub use servo_atoms::Atom;
#[cfg(feature = "servo")]
#[allow(missing_docs)]
pub type LocalName = crate::values::GenericAtomIdent<html5ever::LocalNameStaticSet>;
#[cfg(feature = "servo")]
#[allow(missing_docs)]
pub type Namespace = crate::values::GenericAtomIdent<html5ever::NamespaceStaticSet>;
#[cfg(feature = "servo")]
#[allow(missing_docs)]
pub type Prefix = crate::values::GenericAtomIdent<html5ever::PrefixStaticSet>;
pub use style_traits::arc_slice::ArcSlice; pub use style_traits::arc_slice::ArcSlice;
pub use style_traits::owned_slice::OwnedSlice; pub use style_traits::owned_slice::OwnedSlice;
pub use style_traits::owned_str::OwnedStr; pub use style_traits::owned_str::OwnedStr;

View file

@ -133,3 +133,27 @@ macro_rules! profiler_label {
macro_rules! profiler_label { macro_rules! profiler_label {
($label_type:ident) => {}; ($label_type:ident) => {};
} }
#[cfg(feature = "servo")]
macro_rules! local_name {
($s:tt) => {
$crate::values::GenericAtomIdent(html5ever::local_name!($s))
};
}
#[cfg(feature = "servo")]
macro_rules! ns {
() => {
$crate::values::GenericAtomIdent(html5ever::ns!())
};
($s:tt) => {
$crate::values::GenericAtomIdent(html5ever::ns!($s))
};
}
#[cfg(feature = "gecko")]
macro_rules! local_name {
($s:tt) => {
$crate::values::AtomIdent(atom!($s))
};
}

View file

@ -395,7 +395,7 @@ trait PrivateMatchMethods: TElement {
if context.shared.traversal_flags.for_animation_only() { if context.shared.traversal_flags.for_animation_only() {
self.handle_display_change_for_smil_if_needed( self.handle_display_change_for_smil_if_needed(
context, context,
old_values.as_ref().map(|v| &**v), old_values.as_deref(),
new_values, new_values,
restyle_hint, restyle_hint,
); );
@ -408,7 +408,7 @@ trait PrivateMatchMethods: TElement {
let mut tasks = UpdateAnimationsTasks::empty(); let mut tasks = UpdateAnimationsTasks::empty();
if self.needs_animations_update( if self.needs_animations_update(
context, context,
old_values.as_ref().map(|s| &**s), old_values.as_deref(),
new_values, new_values,
/* pseudo_element = */ None, /* pseudo_element = */ None,
) { ) {
@ -417,7 +417,7 @@ trait PrivateMatchMethods: TElement {
let before_change_style = if self.might_need_transitions_update( let before_change_style = if self.might_need_transitions_update(
context, context,
old_values.as_ref().map(|s| &**s), old_values.as_deref(),
new_values, new_values,
/* pseudo_element = */ None, /* pseudo_element = */ None,
) { ) {
@ -460,7 +460,7 @@ trait PrivateMatchMethods: TElement {
if important_rules_changed { if important_rules_changed {
tasks.insert(UpdateAnimationsTasks::CASCADE_RESULTS); tasks.insert(UpdateAnimationsTasks::CASCADE_RESULTS);
} }
if new_values.is_display_property_changed_from_none(old_values.as_ref().map(|s| &**s)) { if new_values.is_display_property_changed_from_none(old_values.as_deref()) {
tasks.insert(UpdateAnimationsTasks::DISPLAY_CHANGED_FROM_NONE); tasks.insert(UpdateAnimationsTasks::DISPLAY_CHANGED_FROM_NONE);
} }
} }
@ -638,14 +638,14 @@ trait PrivateMatchMethods: TElement {
// map because this call will do a RwLock::read(). // map because this call will do a RwLock::read().
let needs_animations_update = self.needs_animations_update( let needs_animations_update = self.needs_animations_update(
context, context,
old_values.as_ref().map(|s| &**s), old_values.as_deref(),
new_values, new_values,
pseudo_element, pseudo_element,
); );
let might_need_transitions_update = self.might_need_transitions_update( let might_need_transitions_update = self.might_need_transitions_update(
context, context,
old_values.as_ref().map(|s| &**s), old_values.as_deref(),
new_values, new_values,
pseudo_element, pseudo_element,
); );

View file

@ -7,7 +7,7 @@
use super::media_feature_expression::RangeOrOperator; use super::media_feature_expression::RangeOrOperator;
use super::Device; use super::Device;
use crate::parser::ParserContext; use crate::parser::ParserContext;
use crate::values::computed::position::Ratio; use crate::values::computed::Ratio;
use crate::values::computed::{CSSPixelLength, Resolution}; use crate::values::computed::{CSSPixelLength, Resolution};
use crate::Atom; use crate::Atom;
use cssparser::Parser; use cssparser::Parser;

View file

@ -15,8 +15,7 @@ use crate::parser::{Parse, ParserContext};
#[cfg(feature = "servo")] #[cfg(feature = "servo")]
use crate::servo::media_queries::MEDIA_FEATURES; use crate::servo::media_queries::MEDIA_FEATURES;
use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase}; use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};
use crate::values::computed::position::Ratio; use crate::values::computed::{self, Ratio, ToComputedValue};
use crate::values::computed::{self, ToComputedValue};
use crate::values::specified::{Integer, Length, Number, Resolution}; use crate::values::specified::{Integer, Length, Number, Resolution};
use crate::values::{serialize_atom_identifier, CSSFloat}; use crate::values::{serialize_atom_identifier, CSSFloat};
use crate::{Atom, Zero}; use crate::{Atom, Zero};
@ -218,11 +217,17 @@ fn consume_operation_or_colon(input: &mut Parser) -> Result<Option<Operator>, ()
} }
#[allow(unused_variables)] #[allow(unused_variables)]
fn disabled_by_pref(feature: &Atom) -> bool { fn disabled_by_pref(feature: &Atom, context: &ParserContext) -> bool {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
{ {
if *feature == atom!("-moz-touch-enabled") { if *feature == atom!("forced-colors") {
return !static_prefs::pref!("layout.css.moz-touch-enabled.enabled"); return !static_prefs::pref!("layout.css.forced-colors.enabled");
}
// prefers-contrast is always enabled in the ua and chrome. On
// the web it is hidden behind a preference.
if *feature == atom!("prefers-contrast") {
return !context.in_ua_or_chrome_sheet() &&
!static_prefs::pref!("layout.css.prefers-contrast.enabled");
} }
} }
false false
@ -305,7 +310,7 @@ impl MediaFeatureExpression {
}, },
}; };
if disabled_by_pref(&feature.name) || if disabled_by_pref(&feature.name, context) ||
!requirements.contains(feature.requirements) || !requirements.contains(feature.requirements) ||
(range.is_some() && !feature.allows_ranges()) (range.is_some() && !feature.allows_ranges())
{ {
@ -492,15 +497,9 @@ impl MediaExpressionValue {
MediaExpressionValue::Float(number.get()) MediaExpressionValue::Float(number.get())
}, },
Evaluator::NumberRatio(..) => { Evaluator::NumberRatio(..) => {
use crate::values::generics::position::Ratio as GenericRatio; use crate::values::specified::Ratio as SpecifiedRatio;
use crate::values::generics::NonNegative; let ratio = SpecifiedRatio::parse(context, input)?;
use crate::values::specified::position::Ratio; MediaExpressionValue::NumberRatio(Ratio::new(ratio.0.get(), ratio.1.get()))
let ratio = Ratio::parse(context, input)?;
MediaExpressionValue::NumberRatio(GenericRatio(
NonNegative(ratio.0.get()),
NonNegative(ratio.1.get()),
))
}, },
Evaluator::Resolution(..) => { Evaluator::Resolution(..) => {
MediaExpressionValue::Resolution(Resolution::parse(context, input)?) MediaExpressionValue::Resolution(Resolution::parse(context, input)?)

View file

@ -82,27 +82,6 @@ impl<'a> ParserContext<'a> {
} }
} }
/// Create a parser context for on-the-fly parsing in CSSOM
#[inline]
pub fn new_for_cssom(
url_data: &'a UrlExtraData,
rule_type: Option<CssRuleType>,
parsing_mode: ParsingMode,
quirks_mode: QuirksMode,
error_reporter: Option<&'a dyn ParseErrorReporter>,
use_counters: Option<&'a UseCounters>,
) -> Self {
Self::new(
Origin::Author,
url_data,
rule_type,
parsing_mode,
quirks_mode,
error_reporter,
use_counters,
)
}
/// Create a parser context based on a previous context, but with a modified /// Create a parser context based on a previous context, but with a modified
/// rule type. /// rule type.
#[inline] #[inline]
@ -161,7 +140,7 @@ impl<'a> ParserContext<'a> {
/// Returns whether chrome-only rules should be parsed. /// Returns whether chrome-only rules should be parsed.
#[inline] #[inline]
pub fn chrome_rules_enabled(&self) -> bool { pub fn chrome_rules_enabled(&self) -> bool {
self.url_data.is_chrome() || self.stylesheet_origin == Origin::User self.url_data.chrome_rules_enabled() || self.stylesheet_origin == Origin::User
} }
/// Whether we're in a user-agent stylesheet or chrome rules are enabled. /// Whether we're in a user-agent stylesheet or chrome rules are enabled.
@ -207,6 +186,18 @@ where
} }
} }
impl<T> Parse for Box<T>
where
T: Parse,
{
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
T::parse(context, input).map(Box::new)
}
}
impl Parse for crate::OwnedStr { impl Parse for crate::OwnedStr {
fn parse<'i, 't>( fn parse<'i, 't>(
_: &ParserContext, _: &ParserContext,

View file

@ -7,7 +7,7 @@ import os.path
import re import re
import sys import sys
BASE = os.path.dirname(__file__.replace('\\', '/')) BASE = os.path.dirname(__file__.replace("\\", "/"))
sys.path.insert(0, os.path.join(BASE, "Mako-1.1.2-py2.py3-none-any.whl")) sys.path.insert(0, os.path.join(BASE, "Mako-1.1.2-py2.py3-none-any.whl"))
sys.path.insert(0, BASE) # For importing `data.py` sys.path.insert(0, BASE) # For importing `data.py`
@ -17,7 +17,7 @@ from mako.template import Template
import data import data
RE_PYTHON_ADDR = re.compile(r'<.+? object at 0x[0-9a-fA-F]+>') RE_PYTHON_ADDR = re.compile(r"<.+? object at 0x[0-9a-fA-F]+>")
OUT_DIR = os.environ.get("OUT_DIR", "") OUT_DIR = os.environ.get("OUT_DIR", "")
@ -48,15 +48,20 @@ STYLE_STRUCT_LIST = [
def main(): def main():
usage = ("Usage: %s [ servo-2013 | servo-2020 | gecko ] [ style-crate | geckolib <template> | html ]" % usage = (
sys.argv[0]) "Usage: %s [ servo-2013 | servo-2020 | gecko ] [ style-crate | geckolib <template> | html ]"
% sys.argv[0]
)
if len(sys.argv) < 3: if len(sys.argv) < 3:
abort(usage) abort(usage)
engine = sys.argv[1] engine = sys.argv[1]
output = sys.argv[2] output = sys.argv[2]
if engine not in ["servo-2013", "servo-2020", "gecko"] \ if engine not in ["servo-2013", "servo-2020", "gecko"] or output not in [
or output not in ["style-crate", "geckolib", "html"]: "style-crate",
"geckolib",
"html",
]:
abort(usage) abort(usage)
properties = data.PropertiesData(engine=engine) properties = data.PropertiesData(engine=engine)
@ -103,19 +108,19 @@ def main():
pref_attr = "servo_2020_pref" pref_attr = "servo_2020_pref"
properties_dict = { properties_dict = {
kind: { kind: {
p.name: { p.name: {"pref": getattr(p, pref_attr)}
"pref": getattr(p, pref_attr)
}
for prop in properties_list for prop in properties_list
if prop.enabled_in_content() if prop.enabled_in_content()
for p in [prop] + prop.alias for p in [prop] + prop.aliases
} }
for kind, properties_list in [ for kind, properties_list in [
("longhands", properties.longhands), ("longhands", properties.longhands),
("shorthands", properties.shorthands) ("shorthands", properties.shorthands),
] ]
} }
as_html = render(os.path.join(BASE, "properties.html.mako"), properties=properties_dict) as_html = render(
os.path.join(BASE, "properties.html.mako"), properties=properties_dict
)
as_json = json.dumps(properties_dict, indent=4, sort_keys=True) as_json = json.dumps(properties_dict, indent=4, sort_keys=True)
doc_servo = os.path.join(BASE, "..", "..", "..", "target", "doc", "servo") doc_servo = os.path.join(BASE, "..", "..", "..", "target", "doc", "servo")
write(doc_servo, "css-properties.html", as_html) write(doc_servo, "css-properties.html", as_html)
@ -136,14 +141,16 @@ def abort(message):
def render(filename, **context): def render(filename, **context):
try: try:
lookup = TemplateLookup(directories=[BASE], lookup = TemplateLookup(
input_encoding="utf8", directories=[BASE], input_encoding="utf8", strict_undefined=True
strict_undefined=True) )
template = Template(open(filename, "rb").read(), template = Template(
filename=filename, open(filename, "rb").read(),
input_encoding="utf8", filename=filename,
lookup=lookup, input_encoding="utf8",
strict_undefined=True) lookup=lookup,
strict_undefined=True,
)
# Uncomment to debug generated Python code: # Uncomment to debug generated Python code:
# write("/tmp", "mako_%s.py" % os.path.basename(filename), template.code) # write("/tmp", "mako_%s.py" % os.path.basename(filename), template.code)
return template.render(**context) return template.render(**context)
@ -161,7 +168,7 @@ def write(directory, filename, content):
python_addr = RE_PYTHON_ADDR.search(content) python_addr = RE_PYTHON_ADDR.search(content)
if python_addr: if python_addr:
abort("Found \"{}\" in {} ({})".format(python_addr.group(0), filename, full_path)) abort('Found "{}" in {} ({})'.format(python_addr.group(0), filename, full_path))
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -10,21 +10,22 @@ use crate::dom::TElement;
use crate::font_metrics::FontMetricsProvider; use crate::font_metrics::FontMetricsProvider;
use crate::logical_geometry::WritingMode; use crate::logical_geometry::WritingMode;
use crate::media_queries::Device; use crate::media_queries::Device;
use crate::properties::{ComputedValues, StyleBuilder, Importance}; use crate::properties::{
use crate::properties::{LonghandId, LonghandIdSet, CSSWideKeyword, PropertyFlags}; CSSWideKeyword, ComputedValueFlags, ComputedValues, DeclarationImportanceIterator, Importance,
use crate::properties::{PropertyDeclaration, PropertyDeclarationId, DeclarationImportanceIterator}; LonghandId, LonghandIdSet, PropertyDeclaration, PropertyDeclarationId, PropertyFlags,
use crate::properties::{CASCADE_PROPERTY, ComputedValueFlags}; ShorthandsWithPropertyReferencesCache, StyleBuilder, CASCADE_PROPERTY,
};
use crate::rule_cache::{RuleCache, RuleCacheConditions}; use crate::rule_cache::{RuleCache, RuleCacheConditions};
use crate::rule_tree::StrongRuleNode; use crate::rule_tree::StrongRuleNode;
use crate::selector_parser::PseudoElement; use crate::selector_parser::PseudoElement;
use crate::stylesheets::{Origin, PerOrigin};
use servo_arc::Arc;
use crate::shared_lock::StylesheetGuards; use crate::shared_lock::StylesheetGuards;
use crate::style_adjuster::StyleAdjuster;
use crate::stylesheets::{Origin, PerOrigin};
use crate::values::{computed, specified};
use servo_arc::Arc;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::borrow::Cow; use std::borrow::Cow;
use std::cell::RefCell; use std::cell::RefCell;
use crate::style_adjuster::StyleAdjuster;
use crate::values::{computed, specified};
/// We split the cascade in two phases: 'early' properties, and 'late' /// We split the cascade in two phases: 'early' properties, and 'late'
/// properties. /// properties.
@ -121,7 +122,11 @@ struct DeclarationIterator<'a> {
impl<'a> DeclarationIterator<'a> { impl<'a> DeclarationIterator<'a> {
#[inline] #[inline]
fn new(rule_node: &'a StrongRuleNode, guards: &'a StylesheetGuards, pseudo: Option<&PseudoElement>) -> Self { fn new(
rule_node: &'a StrongRuleNode,
guards: &'a StylesheetGuards,
pseudo: Option<&PseudoElement>,
) -> Self {
let restriction = pseudo.and_then(|p| p.property_restriction()); let restriction = pseudo.and_then(|p| p.property_restriction());
let mut iter = Self { let mut iter = Self {
guards, guards,
@ -148,7 +153,6 @@ impl<'a> DeclarationIterator<'a> {
None => DeclarationImportanceIterator::default(), None => DeclarationImportanceIterator::default(),
}; };
} }
} }
impl<'a> Iterator for DeclarationIterator<'a> { impl<'a> Iterator for DeclarationIterator<'a> {
@ -282,10 +286,7 @@ where
let mut declarations = SmallVec::<[(&_, Origin); 32]>::new(); let mut declarations = SmallVec::<[(&_, Origin); 32]>::new();
let custom_properties = { let custom_properties = {
let mut builder = CustomPropertiesBuilder::new( let mut builder = CustomPropertiesBuilder::new(inherited_style.custom_properties(), device);
inherited_style.custom_properties(),
device,
);
for (declaration, origin) in iter { for (declaration, origin) in iter {
declarations.push((declaration, origin)); declarations.push((declaration, origin));
@ -297,9 +298,7 @@ where
builder.build() builder.build()
}; };
let is_root_element = pseudo.is_none() && element.map_or(false, |e| e.is_root());
let is_root_element =
pseudo.is_none() && element.map_or(false, |e| e.is_root());
let mut context = computed::Context { let mut context = computed::Context {
// We'd really like to own the rules here to avoid refcount traffic, but // We'd really like to own the rules here to avoid refcount traffic, but
@ -325,9 +324,13 @@ where
let using_cached_reset_properties = { let using_cached_reset_properties = {
let mut cascade = Cascade::new(&mut context, cascade_mode); let mut cascade = Cascade::new(&mut context, cascade_mode);
let mut shorthand_cache = ShorthandsWithPropertyReferencesCache::default();
cascade cascade.apply_properties::<EarlyProperties, _>(
.apply_properties::<EarlyProperties, _>(ApplyResetProperties::Yes, declarations.iter().cloned()); ApplyResetProperties::Yes,
declarations.iter().cloned(),
&mut shorthand_cache,
);
cascade.compute_visited_style_if_needed( cascade.compute_visited_style_if_needed(
element, element,
@ -346,7 +349,11 @@ where
ApplyResetProperties::Yes ApplyResetProperties::Yes
}; };
cascade.apply_properties::<LateProperties, _>(apply_reset, declarations.iter().cloned()); cascade.apply_properties::<LateProperties, _>(
apply_reset,
declarations.iter().cloned(),
&mut shorthand_cache,
);
using_cached_reset_properties using_cached_reset_properties
}; };
@ -376,7 +383,7 @@ where
/// given initial value if nothing in other origins did override it. /// given initial value if nothing in other origins did override it.
/// ///
/// This is a bit of a clunky way of achieving this. /// This is a bit of a clunky way of achieving this.
type DeclarationsToApplyUnlessOverriden = SmallVec::<[PropertyDeclaration; 2]>; type DeclarationsToApplyUnlessOverriden = SmallVec<[PropertyDeclaration; 2]>;
fn tweak_when_ignoring_colors( fn tweak_when_ignoring_colors(
builder: &StyleBuilder, builder: &StyleBuilder,
@ -385,6 +392,8 @@ fn tweak_when_ignoring_colors(
declaration: &mut Cow<PropertyDeclaration>, declaration: &mut Cow<PropertyDeclaration>,
declarations_to_apply_unless_overriden: &mut DeclarationsToApplyUnlessOverriden, declarations_to_apply_unless_overriden: &mut DeclarationsToApplyUnlessOverriden,
) { ) {
use crate::values::specified::Color;
if !longhand_id.ignored_when_document_colors_disabled() { if !longhand_id.ignored_when_document_colors_disabled() {
return; return;
} }
@ -397,25 +406,58 @@ fn tweak_when_ignoring_colors(
// Don't override background-color on ::-moz-color-swatch. It is set as an // Don't override background-color on ::-moz-color-swatch. It is set as an
// author style (via the style attribute), but it's pretty important for it // author style (via the style attribute), but it's pretty important for it
// to show up for obvious reasons :) // to show up for obvious reasons :)
if builder.pseudo.map_or(false, |p| p.is_color_swatch()) && longhand_id == LonghandId::BackgroundColor { if builder.pseudo.map_or(false, |p| p.is_color_swatch()) &&
longhand_id == LonghandId::BackgroundColor
{
return; return;
} }
fn alpha_channel(color: &Color) -> u8 {
match *color {
// Seems safe enough to assume that the default color and system
// colors are opaque in HCM, though maybe we shouldn't asume the
// later?
#[cfg(feature = "gecko")]
Color::InheritFromBodyQuirk | Color::System(..) => 255,
// We don't have the actual color here, but since except for color:
// transparent we force opaque text colors, it seems sane to do
// this. You can technically fool this bit of code with:
//
// color: transparent; background-color: currentcolor;
//
// but this is best-effort, and that seems unlikely to happen in
// practice.
Color::CurrentColor => 255,
// Complex colors are results of interpolation only and probably
// shouldn't show up around here in HCM, but we've always treated
// them as opaque effectively so keep doing it.
Color::Complex { .. } => 255,
Color::Numeric { ref parsed, .. } => parsed.alpha,
}
}
// A few special-cases ahead. // A few special-cases ahead.
match **declaration { match **declaration {
// We honor color and background-color: transparent, and
// "revert-or-initial" otherwise.
PropertyDeclaration::BackgroundColor(ref color) => { PropertyDeclaration::BackgroundColor(ref color) => {
if !color.is_transparent() { // For background-color, we revert or initial-with-preserved-alpha
let color = builder.device.default_background_color(); // otherwise, this is needed to preserve semi-transparent
declarations_to_apply_unless_overriden.push( // backgrounds.
PropertyDeclaration::BackgroundColor(color.into()) //
) // FIXME(emilio, bug 1666059): We revert for alpha == 0, but maybe
// should consider not doing that even if it causes some issues like
// bug 1625036, or finding a performant way to preserve the original
// widget background color's rgb channels but not alpha...
let alpha = alpha_channel(color);
if alpha != 0 {
let mut color = builder.device.default_background_color();
color.alpha = alpha;
declarations_to_apply_unless_overriden
.push(PropertyDeclaration::BackgroundColor(color.into()))
} }
} },
PropertyDeclaration::Color(ref color) => { PropertyDeclaration::Color(ref color) => {
// otherwise. // We honor color: transparent, and "revert-or-initial" otherwise.
if color.0.is_transparent() { if alpha_channel(&color.0) == 0 {
return; return;
} }
// If the inherited color would be transparent, but we would // If the inherited color would be transparent, but we would
@ -423,9 +465,9 @@ fn tweak_when_ignoring_colors(
// the default color. Otherwise just let it inherit through. // the default color. Otherwise just let it inherit through.
if builder.get_parent_inherited_text().clone_color().alpha == 0 { if builder.get_parent_inherited_text().clone_color().alpha == 0 {
let color = builder.device.default_color(); let color = builder.device.default_color();
declarations_to_apply_unless_overriden.push( declarations_to_apply_unless_overriden.push(PropertyDeclaration::Color(
PropertyDeclaration::Color(specified::ColorPropertyValue(color.into())) specified::ColorPropertyValue(color.into()),
) ))
} }
}, },
// We honor url background-images if backplating. // We honor url background-images if backplating.
@ -441,8 +483,8 @@ fn tweak_when_ignoring_colors(
_ => {}, _ => {},
} }
*declaration.to_mut() = PropertyDeclaration::css_wide_keyword(longhand_id, CSSWideKeyword::Revert); *declaration.to_mut() =
PropertyDeclaration::css_wide_keyword(longhand_id, CSSWideKeyword::Revert);
} }
struct Cascade<'a, 'b: 'a> { struct Cascade<'a, 'b: 'a> {
@ -464,10 +506,14 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
} }
} }
fn substitute_variables_if_needed<'decl>( fn substitute_variables_if_needed<'decl, 'cache>(
&mut self, &mut self,
declaration: &'decl PropertyDeclaration, declaration: &'decl PropertyDeclaration,
) -> Cow<'decl, PropertyDeclaration> { cache: &'cache mut ShorthandsWithPropertyReferencesCache,
) -> Cow<'decl, PropertyDeclaration>
where
'cache: 'decl,
{
let declaration = match *declaration { let declaration = match *declaration {
PropertyDeclaration::WithVariables(ref declaration) => declaration, PropertyDeclaration::WithVariables(ref declaration) => declaration,
ref d => return Cow::Borrowed(d), ref d => return Cow::Borrowed(d),
@ -478,22 +524,39 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
.rule_cache_conditions .rule_cache_conditions
.borrow_mut() .borrow_mut()
.set_uncacheable(); .set_uncacheable();
// NOTE(emilio): We only really need to add the `display` /
// `content` flag if the CSS variable has not been specified on our
// declarations, but we don't have that information at this point,
// and it doesn't seem like an important enough optimization to
// warrant it.
match declaration.id {
LonghandId::Display => {
self.context
.builder
.add_flags(ComputedValueFlags::DISPLAY_DEPENDS_ON_INHERITED_STYLE);
},
LonghandId::Content => {
self.context
.builder
.add_flags(ComputedValueFlags::CONTENT_DEPENDS_ON_INHERITED_STYLE);
},
_ => {},
}
} }
Cow::Owned(declaration.value.substitute_variables( declaration.value.substitute_variables(
declaration.id, declaration.id,
self.context.builder.writing_mode,
self.context.builder.custom_properties.as_ref(), self.context.builder.custom_properties.as_ref(),
self.context.quirks_mode, self.context.quirks_mode,
self.context.device(), self.context.device(),
)) cache,
)
} }
#[inline(always)] #[inline(always)]
fn apply_declaration( fn apply_declaration(&mut self, longhand_id: LonghandId, declaration: &PropertyDeclaration) {
&mut self,
longhand_id: LonghandId,
declaration: &PropertyDeclaration,
) {
// We could (and used to) use a pattern match here, but that bloats this // We could (and used to) use a pattern match here, but that bloats this
// function to over 100K of compiled code! // function to over 100K of compiled code!
// //
@ -507,6 +570,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
&mut self, &mut self,
apply_reset: ApplyResetProperties, apply_reset: ApplyResetProperties,
declarations: I, declarations: I,
mut shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
) where ) where
Phase: CascadePhase, Phase: CascadePhase,
I: Iterator<Item = (&'decls PropertyDeclaration, Origin)>, I: Iterator<Item = (&'decls PropertyDeclaration, Origin)>,
@ -521,8 +585,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
); );
let ignore_colors = !self.context.builder.device.use_document_colors(); let ignore_colors = !self.context.builder.device.use_document_colors();
let mut declarations_to_apply_unless_overriden = let mut declarations_to_apply_unless_overriden = DeclarationsToApplyUnlessOverriden::new();
DeclarationsToApplyUnlessOverriden::new();
for (declaration, origin) in declarations { for (declaration, origin) in declarations {
let declaration_id = declaration.id(); let declaration_id = declaration.id();
@ -551,7 +614,11 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
continue; continue;
} }
if self.reverted.borrow_for_origin(&origin).contains(physical_longhand_id) { if self
.reverted
.borrow_for_origin(&origin)
.contains(physical_longhand_id)
{
continue; continue;
} }
@ -564,7 +631,8 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
continue; continue;
} }
let mut declaration = self.substitute_variables_if_needed(declaration); let mut declaration =
self.substitute_variables_if_needed(declaration, &mut shorthand_cache);
// When document colors are disabled, do special handling of // When document colors are disabled, do special handling of
// properties that are marked as ignored in that mode. // properties that are marked as ignored in that mode.
@ -601,13 +669,11 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
self.author_specified.insert(physical_longhand_id); self.author_specified.insert(physical_longhand_id);
} }
let unset = css_wide_keyword.map_or(false, |css_wide_keyword| { let unset = css_wide_keyword.map_or(false, |css_wide_keyword| match css_wide_keyword {
match css_wide_keyword { CSSWideKeyword::Unset => true,
CSSWideKeyword::Unset => true, CSSWideKeyword::Inherit => inherited,
CSSWideKeyword::Inherit => inherited, CSSWideKeyword::Initial => !inherited,
CSSWideKeyword::Initial => !inherited, CSSWideKeyword::Revert => unreachable!(),
CSSWideKeyword::Revert => unreachable!(),
}
}); });
if unset { if unset {
@ -703,7 +769,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
// styles, we cache the unvisited style instead. We still do // styles, we cache the unvisited style instead. We still do
// need to set the caching dependencies properly if present // need to set the caching dependencies properly if present
// though, so the cache conditions need to match. // though, so the cache conditions need to match.
/* rule_cache = */ None, None, // rule_cache
&mut *self.context.rule_cache_conditions.borrow_mut(), &mut *self.context.rule_cache_conditions.borrow_mut(),
element, element,
); );
@ -722,13 +788,18 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
if let Some(svg) = builder.get_svg_if_mutated() { if let Some(svg) = builder.get_svg_if_mutated() {
svg.fill_arrays(); svg.fill_arrays();
} }
} }
if self.author_specified.contains_any(LonghandIdSet::border_background_properties()) { if self
.author_specified
.contains_any(LonghandIdSet::border_background_properties())
{
builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND); builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND);
} }
if self.author_specified.contains_any(LonghandIdSet::padding_properties()) { if self
.author_specified
.contains_any(LonghandIdSet::padding_properties())
{
builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_PADDING); builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_PADDING);
} }
@ -765,8 +836,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
// //
// Note that all the properties involved are non-inherited, so we don't // Note that all the properties involved are non-inherited, so we don't
// need to do anything else other than just copying the bits over. // need to do anything else other than just copying the bits over.
let reset_props_bits = let reset_props_bits = ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND |
ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND |
ComputedValueFlags::HAS_AUTHOR_SPECIFIED_PADDING; ComputedValueFlags::HAS_AUTHOR_SPECIFIED_PADDING;
builder.add_flags(cached_style.flags & reset_props_bits); builder.add_flags(cached_style.flags & reset_props_bits);
@ -785,8 +855,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
use crate::gecko_bindings::bindings; use crate::gecko_bindings::bindings;
use crate::values::computed::font::GenericFontFamily; use crate::values::computed::font::GenericFontFamily;
if !self.seen.contains(LonghandId::XLang) && if !self.seen.contains(LonghandId::XLang) && !self.seen.contains(LonghandId::FontFamily) {
!self.seen.contains(LonghandId::FontFamily) {
return; return;
} }
@ -798,7 +867,10 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
// System fonts are all right, and should have the default font type // System fonts are all right, and should have the default font type
// set to none already, so bail out early. // set to none already, so bail out early.
if font.mFont.systemFont { if font.mFont.systemFont {
debug_assert_eq!(font.mFont.fontlist.mDefaultFontType, GenericFontFamily::None); debug_assert_eq!(
font.mFont.fontlist.mDefaultFontType,
GenericFontFamily::None
);
return; return;
} }
@ -814,13 +886,12 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
// and we don't have a generic family already (or we're using // and we don't have a generic family already (or we're using
// cursive or fantasy, since they're ignored, see bug 789788), and // cursive or fantasy, since they're ignored, see bug 789788), and
// we have a generic family to actually replace it with. // we have a generic family to actually replace it with.
let prioritize_user_fonts = let prioritize_user_fonts = !use_document_fonts &&
!use_document_fonts &&
matches!( matches!(
font.mGenericID, font.mGenericID,
GenericFontFamily::None | GenericFontFamily::None |
GenericFontFamily::Fantasy | GenericFontFamily::Fantasy |
GenericFontFamily::Cursive GenericFontFamily::Cursive
) && ) &&
default_font_type != GenericFontFamily::None; default_font_type != GenericFontFamily::None;
@ -834,9 +905,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
let font = builder.mutate_font().gecko_mut(); let font = builder.mutate_font().gecko_mut();
font.mFont.fontlist.mDefaultFontType = default_font_type; font.mFont.fontlist.mDefaultFontType = default_font_type;
if prioritize_user_fonts { if prioritize_user_fonts {
unsafe { unsafe { bindings::Gecko_nsStyleFont_PrioritizeUserFonts(font, default_font_type) }
bindings::Gecko_nsStyleFont_PrioritizeUserFonts(font, default_font_type)
}
} }
} }
@ -845,10 +914,8 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
fn recompute_keyword_font_size_if_needed(&mut self) { fn recompute_keyword_font_size_if_needed(&mut self) {
use crate::values::computed::ToComputedValue; use crate::values::computed::ToComputedValue;
use crate::values::specified; use crate::values::specified;
use app_units::Au;
if !self.seen.contains(LonghandId::XLang) && if !self.seen.contains(LonghandId::XLang) && !self.seen.contains(LonghandId::FontFamily) {
!self.seen.contains(LonghandId::FontFamily) {
return; return;
} }
@ -860,10 +927,10 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
_ => { _ => {
self.context.for_non_inherited_property = None; self.context.for_non_inherited_property = None;
specified::FontSize::Keyword(info).to_computed_value(self.context) specified::FontSize::Keyword(info).to_computed_value(self.context)
} },
}; };
if font.gecko().mScriptUnconstrainedSize == Au::from(new_size.size()).0 { if font.gecko().mScriptUnconstrainedSize == new_size.size {
return; return;
} }
@ -878,11 +945,13 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
fn constrain_font_size_if_needed(&mut self) { fn constrain_font_size_if_needed(&mut self) {
use crate::gecko_bindings::bindings; use crate::gecko_bindings::bindings;
use crate::values::generics::NonNegative;
if !self.seen.contains(LonghandId::XLang) && if !self.seen.contains(LonghandId::XLang) &&
!self.seen.contains(LonghandId::FontFamily) && !self.seen.contains(LonghandId::FontFamily) &&
!self.seen.contains(LonghandId::MozMinFontSizeRatio) && !self.seen.contains(LonghandId::MozMinFontSizeRatio) &&
!self.seen.contains(LonghandId::FontSize) { !self.seen.contains(LonghandId::FontSize)
{
return; return;
} }
@ -890,17 +959,14 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
let min_font_size = { let min_font_size = {
let font = builder.get_font().gecko(); let font = builder.get_font().gecko();
let min_font_size = unsafe { let min_font_size = unsafe {
bindings::Gecko_nsStyleFont_ComputeMinSize( bindings::Gecko_nsStyleFont_ComputeMinSize(font, builder.device.document())
font,
builder.device.document(),
)
}; };
if font.mFont.size >= min_font_size { if font.mFont.size.0 >= min_font_size {
return; return;
} }
min_font_size NonNegative(min_font_size)
}; };
builder.mutate_font().gecko_mut().mFont.size = min_font_size; builder.mutate_font().gecko_mut().mFont.size = min_font_size;
@ -942,12 +1008,12 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
/// not clear to me. For now just pretend those don't exist here. /// not clear to me. For now just pretend those don't exist here.
#[cfg(feature = "gecko")] #[cfg(feature = "gecko")]
fn handle_mathml_scriptlevel_if_needed(&mut self) { fn handle_mathml_scriptlevel_if_needed(&mut self) {
use app_units::Au; use crate::values::generics::NonNegative;
use std::cmp;
if !self.seen.contains(LonghandId::MozScriptLevel) && if !self.seen.contains(LonghandId::MathDepth) &&
!self.seen.contains(LonghandId::MozScriptMinSize) && !self.seen.contains(LonghandId::MozScriptMinSize) &&
!self.seen.contains(LonghandId::MozScriptSizeMultiplier) { !self.seen.contains(LonghandId::MozScriptSizeMultiplier)
{
return; return;
} }
@ -961,21 +1027,20 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
let font = builder.get_font().gecko(); let font = builder.get_font().gecko();
let parent_font = builder.get_parent_font().gecko(); let parent_font = builder.get_parent_font().gecko();
let delta = let delta = font.mMathDepth.saturating_sub(parent_font.mMathDepth);
font.mScriptLevel.saturating_sub(parent_font.mScriptLevel);
if delta == 0 { if delta == 0 {
return; return;
} }
let mut min = Au(parent_font.mScriptMinSize); let mut min = parent_font.mScriptMinSize;
if font.mAllowZoomAndMinSize { if font.mAllowZoomAndMinSize {
min = builder.device.zoom_text(min); min = builder.device.zoom_text(min);
} }
let scale = (parent_font.mScriptSizeMultiplier as f32).powi(delta as i32); let scale = (parent_font.mScriptSizeMultiplier as f32).powi(delta as i32);
let parent_size = Au(parent_font.mSize); let parent_size = parent_font.mSize.0;
let parent_unconstrained_size = Au(parent_font.mScriptUnconstrainedSize); let parent_unconstrained_size = parent_font.mScriptUnconstrainedSize.0;
let new_size = parent_size.scale_by(scale); let new_size = parent_size.scale_by(scale);
let new_unconstrained_size = parent_unconstrained_size.scale_by(scale); let new_unconstrained_size = parent_unconstrained_size.scale_by(scale);
@ -987,7 +1052,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
if parent_size <= min { if parent_size <= min {
(parent_size, new_unconstrained_size) (parent_size, new_unconstrained_size)
} else { } else {
(cmp::max(min, new_size), new_unconstrained_size) (min.max(new_size), new_unconstrained_size)
} }
} else { } else {
// If the new unconstrained size is larger than the min size, // If the new unconstrained size is larger than the min size,
@ -996,15 +1061,15 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
// However, if the new size is even larger (perhaps due to usage // However, if the new size is even larger (perhaps due to usage
// of em units), use that instead. // of em units), use that instead.
( (
cmp::min(new_size, cmp::max(new_unconstrained_size, min)), new_size.min(new_unconstrained_size.max(min)),
new_unconstrained_size new_unconstrained_size,
) )
} }
}; };
let font = builder.mutate_font().gecko_mut(); let font = builder.mutate_font().gecko_mut();
font.mFont.size = new_size.0; font.mFont.size = NonNegative(new_size);
font.mSize = new_size.0; font.mSize = NonNegative(new_size);
font.mScriptUnconstrainedSize = new_unconstrained_size.0; font.mScriptUnconstrainedSize = NonNegative(new_unconstrained_size);
} }
/// Various properties affect how font-size and font-family are computed. /// Various properties affect how font-size and font-family are computed.

View file

@ -42,34 +42,39 @@ bitflags! {
/// A flag used to mark styles which are a pseudo-element or under one. /// A flag used to mark styles which are a pseudo-element or under one.
const IS_IN_PSEUDO_ELEMENT_SUBTREE = 1 << 4; const IS_IN_PSEUDO_ELEMENT_SUBTREE = 1 << 4;
/// Whether this style inherits the `display` property. /// Whether this style's `display` property depends on our parent style.
/// ///
/// This is important because it may affect our optimizations to avoid /// This is important because it may affect our optimizations to avoid
/// computing the style of pseudo-elements, given whether the /// computing the style of pseudo-elements, given whether the
/// pseudo-element is generated depends on the `display` value. /// pseudo-element is generated depends on the `display` value.
const INHERITS_DISPLAY = 1 << 6; const DISPLAY_DEPENDS_ON_INHERITED_STYLE = 1 << 6;
/// Whether this style inherits the `content` property. /// Whether this style's `content` depends on our parent style.
/// ///
/// Important because of the same reason. /// Important because of the same reason.
const INHERITS_CONTENT = 1 << 7; const CONTENT_DEPENDS_ON_INHERITED_STYLE = 1 << 7;
/// Whether the child explicitly inherits any reset property. /// Whether the child explicitly inherits any reset property.
const INHERITS_RESET_STYLE = 1 << 8; const INHERITS_RESET_STYLE = 1 << 8;
/// Whether any value on our style is font-metric-dependent. /// Whether any value on our style is font-metric-dependent on our
const DEPENDS_ON_FONT_METRICS = 1 << 9; /// primary font.
const DEPENDS_ON_SELF_FONT_METRICS = 1 << 9;
/// Whether any value on our style is font-metric-dependent on the
/// primary font of our parent.
const DEPENDS_ON_INHERITED_FONT_METRICS = 1 << 10;
/// Whether the style or any of the ancestors has a multicol style. /// Whether the style or any of the ancestors has a multicol style.
/// ///
/// Only used in Servo. /// Only used in Servo.
const CAN_BE_FRAGMENTED = 1 << 10; const CAN_BE_FRAGMENTED = 1 << 11;
/// Whether this style is the style of the document element. /// Whether this style is the style of the document element.
const IS_ROOT_ELEMENT_STYLE = 1 << 11; const IS_ROOT_ELEMENT_STYLE = 1 << 12;
/// Whether this element is inside an `opacity: 0` subtree. /// Whether this element is inside an `opacity: 0` subtree.
const IS_IN_OPACITY_ZERO_SUBTREE = 1 << 12; const IS_IN_OPACITY_ZERO_SUBTREE = 1 << 13;
/// Whether there are author-specified rules for border-* properties /// Whether there are author-specified rules for border-* properties
/// (except border-image-*), background-color, or background-image. /// (except border-image-*), background-color, or background-image.
@ -77,13 +82,13 @@ bitflags! {
/// TODO(emilio): Maybe do include border-image, see: /// TODO(emilio): Maybe do include border-image, see:
/// ///
/// https://github.com/w3c/csswg-drafts/issues/4777#issuecomment-604424845 /// https://github.com/w3c/csswg-drafts/issues/4777#issuecomment-604424845
const HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND = 1 << 13; const HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND = 1 << 14;
/// Whether there are author-specified rules for padding-* properties. /// Whether there are author-specified rules for padding-* properties.
/// ///
/// FIXME(emilio): Try to merge this with BORDER_BACKGROUND, see /// FIXME(emilio): Try to merge this with BORDER_BACKGROUND, see
/// https://github.com/w3c/csswg-drafts/issues/4777 /// https://github.com/w3c/csswg-drafts/issues/4777
const HAS_AUTHOR_SPECIFIED_PADDING = 1 << 14; const HAS_AUTHOR_SPECIFIED_PADDING = 1 << 15;
} }
} }

View file

@ -15,10 +15,18 @@ PHYSICAL_AXES = ["x", "y"]
LOGICAL_AXES = ["inline", "block"] LOGICAL_AXES = ["inline", "block"]
# bool is True when logical # bool is True when logical
ALL_SIDES = [(side, False) for side in PHYSICAL_SIDES] + [(side, True) for side in LOGICAL_SIDES] ALL_SIDES = [(side, False) for side in PHYSICAL_SIDES] + [
ALL_SIZES = [(size, False) for size in PHYSICAL_SIZES] + [(size, True) for size in LOGICAL_SIZES] (side, True) for side in LOGICAL_SIDES
ALL_CORNERS = [(corner, False) for corner in PHYSICAL_CORNERS] + [(corner, True) for corner in LOGICAL_CORNERS] ]
ALL_AXES = [(axis, False) for axis in PHYSICAL_AXES] + [(axis, True) for axis in LOGICAL_AXES] ALL_SIZES = [(size, False) for size in PHYSICAL_SIZES] + [
(size, True) for size in LOGICAL_SIZES
]
ALL_CORNERS = [(corner, False) for corner in PHYSICAL_CORNERS] + [
(corner, True) for corner in LOGICAL_CORNERS
]
ALL_AXES = [(axis, False) for axis in PHYSICAL_AXES] + [
(axis, True) for axis in LOGICAL_AXES
]
SYSTEM_FONT_LONGHANDS = """font_family font_size font_style SYSTEM_FONT_LONGHANDS = """font_family font_size font_style
font_variant_caps font_stretch font_kerning font_variant_caps font_stretch font_kerning
@ -29,6 +37,32 @@ SYSTEM_FONT_LONGHANDS = """font_family font_size font_style
font_feature_settings font_variation_settings font_feature_settings font_variation_settings
font_optical_sizing""".split() font_optical_sizing""".split()
# Bitfield values for all rule types which can have property declarations.
STYLE_RULE = 1 << 0
PAGE_RULE = 1 << 1
KEYFRAME_RULE = 1 << 2
ALL_RULES = STYLE_RULE | PAGE_RULE | KEYFRAME_RULE
DEFAULT_RULES = STYLE_RULE | KEYFRAME_RULE
DEFAULT_RULES_AND_PAGE = DEFAULT_RULES | PAGE_RULE
DEFAULT_RULES_EXCEPT_KEYFRAME = STYLE_RULE
# Rule name to value dict
RULE_VALUES = {
"Style": STYLE_RULE,
"Page": PAGE_RULE,
"Keyframe": KEYFRAME_RULE,
}
def rule_values_from_arg(that):
if isinstance(that, int):
return that
mask = 0
for rule in that.split():
mask |= RULE_VALUES[rule]
return mask
def maybe_moz_logical_alias(engine, side, prop): def maybe_moz_logical_alias(engine, side, prop):
if engine == "gecko" and side[1]: if engine == "gecko" and side[1]:
@ -50,7 +84,9 @@ def to_snake_case(ident):
def to_camel_case(ident): def to_camel_case(ident):
return re.sub("(^|_|-)([a-z0-9])", lambda m: m.group(2).upper(), ident.strip("_").strip("-")) return re.sub(
"(^|_|-)([a-z0-9])", lambda m: m.group(2).upper(), ident.strip("_").strip("-")
)
def to_camel_case_lower(ident): def to_camel_case_lower(ident):
@ -72,23 +108,32 @@ def parse_aliases(value):
class Keyword(object): class Keyword(object):
def __init__(self, name, values, gecko_constant_prefix=None, def __init__(
gecko_enum_prefix=None, custom_consts=None, self,
extra_gecko_values=None, name,
extra_servo_2013_values=None, values,
extra_servo_2020_values=None, gecko_constant_prefix=None,
gecko_aliases=None, gecko_enum_prefix=None,
servo_2013_aliases=None, custom_consts=None,
servo_2020_aliases=None, extra_gecko_values=None,
gecko_strip_moz_prefix=None, extra_servo_2013_values=None,
gecko_inexhaustive=None): extra_servo_2020_values=None,
gecko_aliases=None,
servo_2013_aliases=None,
servo_2020_aliases=None,
gecko_strip_moz_prefix=None,
gecko_inexhaustive=None,
):
self.name = name self.name = name
self.values = values.split() self.values = values.split()
if gecko_constant_prefix and gecko_enum_prefix: if gecko_constant_prefix and gecko_enum_prefix:
raise TypeError("Only one of gecko_constant_prefix and gecko_enum_prefix " raise TypeError(
"can be specified") "Only one of gecko_constant_prefix and gecko_enum_prefix "
self.gecko_constant_prefix = gecko_constant_prefix or \ "can be specified"
"NS_STYLE_" + self.name.upper().replace("-", "_") )
self.gecko_constant_prefix = (
gecko_constant_prefix or "NS_STYLE_" + self.name.upper().replace("-", "_")
)
self.gecko_enum_prefix = gecko_enum_prefix self.gecko_enum_prefix = gecko_enum_prefix
self.extra_gecko_values = (extra_gecko_values or "").split() self.extra_gecko_values = (extra_gecko_values or "").split()
self.extra_servo_2013_values = (extra_servo_2013_values or "").split() self.extra_servo_2013_values = (extra_servo_2013_values or "").split()
@ -97,8 +142,9 @@ class Keyword(object):
self.servo_2013_aliases = parse_aliases(servo_2013_aliases or "") self.servo_2013_aliases = parse_aliases(servo_2013_aliases or "")
self.servo_2020_aliases = parse_aliases(servo_2020_aliases or "") self.servo_2020_aliases = parse_aliases(servo_2020_aliases or "")
self.consts_map = {} if custom_consts is None else custom_consts self.consts_map = {} if custom_consts is None else custom_consts
self.gecko_strip_moz_prefix = True \ self.gecko_strip_moz_prefix = (
if gecko_strip_moz_prefix is None else gecko_strip_moz_prefix True if gecko_strip_moz_prefix is None else gecko_strip_moz_prefix
)
self.gecko_inexhaustive = gecko_inexhaustive or (gecko_enum_prefix is None) self.gecko_inexhaustive = gecko_inexhaustive or (gecko_enum_prefix is None)
def values_for(self, engine): def values_for(self, engine):
@ -122,11 +168,14 @@ class Keyword(object):
raise Exception("Bad engine: " + engine) raise Exception("Bad engine: " + engine)
def gecko_constant(self, value): def gecko_constant(self, value):
moz_stripped = (value.replace("-moz-", '') moz_stripped = (
if self.gecko_strip_moz_prefix else value.replace("-moz-", 'moz-')) value.replace("-moz-", "")
if self.gecko_strip_moz_prefix
else value.replace("-moz-", "moz-")
)
mapped = self.consts_map.get(value) mapped = self.consts_map.get(value)
if self.gecko_enum_prefix: if self.gecko_enum_prefix:
parts = moz_stripped.replace('-', '_').split('_') parts = moz_stripped.replace("-", "_").split("_")
parts = mapped if mapped else [p.title() for p in parts] parts = mapped if mapped else [p.title() for p in parts]
return self.gecko_enum_prefix + "::" + "".join(parts) return self.gecko_enum_prefix + "::" + "".join(parts)
else: else:
@ -146,13 +195,19 @@ class Keyword(object):
if self.gecko_enum_prefix is None: if self.gecko_enum_prefix is None:
return cast_type.upper() + "_" + self.gecko_constant(value) return cast_type.upper() + "_" + self.gecko_constant(value)
else: else:
return cast_type.upper() + "_" + self.gecko_constant(value).upper().replace("::", "_") return (
cast_type.upper()
+ "_"
+ self.gecko_constant(value).upper().replace("::", "_")
)
def arg_to_bool(arg): def arg_to_bool(arg):
if isinstance(arg, bool): if isinstance(arg, bool):
return arg return arg
assert arg in ["True", "False"], "Unexpected value for boolean arguement: " + repr(arg) assert arg in ["True", "False"], "Unexpected value for boolean arguement: " + repr(
arg
)
return arg == "True" return arg == "True"
@ -169,41 +224,30 @@ def to_phys(name, logical, physical):
return name.replace(logical, physical).replace("inset-", "") return name.replace(logical, physical).replace("inset-", "")
class Longhand(object): class Property(object):
def __init__(self, style_struct, name, spec=None, animation_value_type=None, keyword=None, def __init__(
predefined_type=None, self,
servo_2013_pref=None, name,
servo_2020_pref=None, spec,
gecko_pref=None, servo_2013_pref,
enabled_in="content", need_index=False, servo_2020_pref,
gecko_ffi_name=None, gecko_pref,
has_effect_on_gecko_scrollbars=None, enabled_in,
allowed_in_keyframe_block=True, cast_type='u8', rule_types_allowed,
logical=False, logical_group=None, alias=None, extra_prefixes=None, boxed=False, aliases,
flags=None, allowed_in_page_rule=False, allow_quirks="No", extra_prefixes,
ignored_when_colors_disabled=False, flags,
simple_vector_bindings=False, ):
vector=False, servo_restyle_damage="repaint"):
self.name = name self.name = name
if not spec: if not spec:
raise TypeError("Spec should be specified for %s" % name) raise TypeError("Spec should be specified for " + name)
self.spec = spec self.spec = spec
self.keyword = keyword
self.predefined_type = predefined_type
self.ident = to_rust_ident(name) self.ident = to_rust_ident(name)
self.camel_case = to_camel_case(self.ident) self.camel_case = to_camel_case(self.ident)
self.style_struct = style_struct
self.servo_2013_pref = servo_2013_pref self.servo_2013_pref = servo_2013_pref
self.servo_2020_pref = servo_2020_pref self.servo_2020_pref = servo_2020_pref
self.gecko_pref = gecko_pref self.gecko_pref = gecko_pref
self.has_effect_on_gecko_scrollbars = has_effect_on_gecko_scrollbars self.rule_types_allowed = rule_values_from_arg(rule_types_allowed)
assert (
has_effect_on_gecko_scrollbars in [None, False, True]
and not style_struct.inherited
or (gecko_pref is None) == (has_effect_on_gecko_scrollbars is None)), (
"Property " + name + ": has_effect_on_gecko_scrollbars must be "
"specified, and must have a value of True or False, iff a "
"property is inherited and is behind a Gecko pref")
# For enabled_in, the setup is as follows: # For enabled_in, the setup is as follows:
# It needs to be one of the four values: ["", "ua", "chrome", "content"] # It needs to be one of the four values: ["", "ua", "chrome", "content"]
# * "chrome" implies "ua", and implies that they're explicitly # * "chrome" implies "ua", and implies that they're explicitly
@ -211,8 +255,94 @@ class Longhand(object):
# * "" implies the property will never be parsed. # * "" implies the property will never be parsed.
# * "content" implies the property is accessible unconditionally, # * "content" implies the property is accessible unconditionally,
# modulo a pref, set via servo_pref / gecko_pref. # modulo a pref, set via servo_pref / gecko_pref.
assert enabled_in in ["", "ua", "chrome", "content"] assert enabled_in in ("", "ua", "chrome", "content")
self.enabled_in = enabled_in self.enabled_in = enabled_in
self.aliases = parse_property_aliases(aliases)
self.extra_prefixes = parse_property_aliases(extra_prefixes)
self.flags = flags.split() if flags else []
def experimental(self, engine):
if engine == "gecko":
return bool(self.gecko_pref)
elif engine == "servo-2013":
return bool(self.servo_2013_pref)
elif engine == "servo-2020":
return bool(self.servo_2020_pref)
else:
raise Exception("Bad engine: " + engine)
def explicitly_enabled_in_ua_sheets(self):
return self.enabled_in in ("ua", "chrome")
def explicitly_enabled_in_chrome(self):
return self.enabled_in == "chrome"
def enabled_in_content(self):
return self.enabled_in == "content"
def nscsspropertyid(self):
return "nsCSSPropertyID::eCSSProperty_" + self.ident
class Longhand(Property):
def __init__(
self,
style_struct,
name,
spec=None,
animation_value_type=None,
keyword=None,
predefined_type=None,
servo_2013_pref=None,
servo_2020_pref=None,
gecko_pref=None,
enabled_in="content",
need_index=False,
gecko_ffi_name=None,
has_effect_on_gecko_scrollbars=None,
rule_types_allowed=DEFAULT_RULES,
cast_type="u8",
logical=False,
logical_group=None,
aliases=None,
extra_prefixes=None,
boxed=False,
flags=None,
allow_quirks="No",
ignored_when_colors_disabled=False,
simple_vector_bindings=False,
vector=False,
servo_restyle_damage="repaint",
):
Property.__init__(
self,
name=name,
spec=spec,
servo_2013_pref=servo_2013_pref,
servo_2020_pref=servo_2020_pref,
gecko_pref=gecko_pref,
enabled_in=enabled_in,
rule_types_allowed=rule_types_allowed,
aliases=aliases,
extra_prefixes=extra_prefixes,
flags=flags,
)
self.keyword = keyword
self.predefined_type = predefined_type
self.style_struct = style_struct
self.has_effect_on_gecko_scrollbars = has_effect_on_gecko_scrollbars
assert (
has_effect_on_gecko_scrollbars in [None, False, True]
and not style_struct.inherited
or (gecko_pref is None) == (has_effect_on_gecko_scrollbars is None)
), (
"Property "
+ name
+ ": has_effect_on_gecko_scrollbars must be "
+ "specified, and must have a value of True or False, iff a "
+ "property is inherited and is behind a Gecko pref"
)
self.need_index = need_index self.need_index = need_index
self.gecko_ffi_name = gecko_ffi_name or "m" + self.camel_case self.gecko_ffi_name = gecko_ffi_name or "m" + self.camel_case
self.cast_type = cast_type self.cast_type = cast_type
@ -221,34 +351,28 @@ class Longhand(object):
if self.logical: if self.logical:
assert logical_group, "Property " + name + " must have a logical group" assert logical_group, "Property " + name + " must have a logical group"
self.alias = parse_property_aliases(alias)
self.extra_prefixes = parse_property_aliases(extra_prefixes)
self.boxed = arg_to_bool(boxed) self.boxed = arg_to_bool(boxed)
self.flags = flags.split() if flags else []
self.allowed_in_page_rule = arg_to_bool(allowed_in_page_rule)
self.allow_quirks = allow_quirks self.allow_quirks = allow_quirks
self.ignored_when_colors_disabled = ignored_when_colors_disabled self.ignored_when_colors_disabled = ignored_when_colors_disabled
self.is_vector = vector self.is_vector = vector
self.simple_vector_bindings = simple_vector_bindings self.simple_vector_bindings = simple_vector_bindings
# https://drafts.csswg.org/css-animations/#keyframes
# > The <declaration-list> inside of <keyframe-block> accepts any CSS property
# > except those defined in this specification,
# > but does accept the `animation-play-state` property and interprets it specially.
self.allowed_in_keyframe_block = allowed_in_keyframe_block \
and allowed_in_keyframe_block != "False"
# This is done like this since just a plain bool argument seemed like # This is done like this since just a plain bool argument seemed like
# really random. # really random.
if animation_value_type is None: if animation_value_type is None:
raise TypeError("animation_value_type should be specified for (" + name + ")") raise TypeError(
"animation_value_type should be specified for (" + name + ")"
)
self.animation_value_type = animation_value_type self.animation_value_type = animation_value_type
self.animatable = animation_value_type != "none" self.animatable = animation_value_type != "none"
self.transitionable = animation_value_type != "none" \ self.transitionable = (
and animation_value_type != "discrete" animation_value_type != "none" and animation_value_type != "discrete"
self.is_animatable_with_computed_value = animation_value_type == "ComputedValue" \ )
self.is_animatable_with_computed_value = (
animation_value_type == "ComputedValue"
or animation_value_type == "discrete" or animation_value_type == "discrete"
)
# See compute_damage for the various values this can take # See compute_damage for the various values this can take
self.servo_restyle_damage = servo_restyle_damage self.servo_restyle_damage = servo_restyle_damage
@ -263,45 +387,39 @@ class Longhand(object):
if not self.logical: if not self.logical:
return [] return []
candidates = [s for s in LOGICAL_SIDES + LOGICAL_SIZES + LOGICAL_CORNERS candidates = [
if s in self.name] + [s for s in LOGICAL_AXES if self.name.endswith(s)] s for s in LOGICAL_SIDES + LOGICAL_SIZES + LOGICAL_CORNERS if s in self.name
assert(len(candidates) == 1) ] + [s for s in LOGICAL_AXES if self.name.endswith(s)]
assert len(candidates) == 1
logical_side = candidates[0] logical_side = candidates[0]
physical = PHYSICAL_SIDES if logical_side in LOGICAL_SIDES \ physical = (
else PHYSICAL_SIZES if logical_side in LOGICAL_SIZES \ PHYSICAL_SIDES
else PHYSICAL_AXES if logical_side in LOGICAL_AXES \ if logical_side in LOGICAL_SIDES
else PHYSICAL_SIZES
if logical_side in LOGICAL_SIZES
else PHYSICAL_AXES
if logical_side in LOGICAL_AXES
else LOGICAL_CORNERS else LOGICAL_CORNERS
return [data.longhands_by_name[to_phys(self.name, logical_side, physical_side)] )
for physical_side in physical] return [
data.longhands_by_name[to_phys(self.name, logical_side, physical_side)]
def experimental(self, engine): for physical_side in physical
if engine == "gecko": ]
return bool(self.gecko_pref)
elif engine == "servo-2013":
return bool(self.servo_2013_pref)
elif engine == "servo-2020":
return bool(self.servo_2020_pref)
else:
raise Exception("Bad engine: " + engine)
# FIXME(emilio): Shorthand and Longhand should really share a base class.
def explicitly_enabled_in_ua_sheets(self):
return self.enabled_in in ["ua", "chrome"]
def explicitly_enabled_in_chrome(self):
return self.enabled_in == "chrome"
def enabled_in_content(self):
return self.enabled_in == "content"
def may_be_disabled_in(self, shorthand, engine): def may_be_disabled_in(self, shorthand, engine):
if engine == "gecko": if engine == "gecko":
return self.gecko_pref and self.gecko_pref != shorthand.gecko_pref return self.gecko_pref and self.gecko_pref != shorthand.gecko_pref
elif engine == "servo-2013": elif engine == "servo-2013":
return self.servo_2013_pref and self.servo_2013_pref != shorthand.servo_2013_pref return (
self.servo_2013_pref
and self.servo_2013_pref != shorthand.servo_2013_pref
)
elif engine == "servo-2020": elif engine == "servo-2020":
return self.servo_2020_pref and self.servo_2020_pref != shorthand.servo_2020_pref return (
self.servo_2020_pref
and self.servo_2020_pref != shorthand.servo_2020_pref
)
else: else:
raise Exception("Bad engine: " + engine) raise Exception("Bad engine: " + engine)
@ -334,6 +452,7 @@ class Longhand(object):
"BackgroundRepeat", "BackgroundRepeat",
"BorderImageRepeat", "BorderImageRepeat",
"BorderStyle", "BorderStyle",
"table::CaptionSide",
"Clear", "Clear",
"ColumnCount", "ColumnCount",
"Contain", "Contain",
@ -360,7 +479,7 @@ class Longhand(object):
"MasonryAutoFlow", "MasonryAutoFlow",
"MozForceBrokenImageIcon", "MozForceBrokenImageIcon",
"MozListReversed", "MozListReversed",
"MozScriptLevel", "MathDepth",
"MozScriptMinSize", "MozScriptMinSize",
"MozScriptSizeMultiplier", "MozScriptSizeMultiplier",
"TextDecorationSkipInk", "TextDecorationSkipInk",
@ -407,41 +526,36 @@ class Longhand(object):
return computed return computed
return "<{} as ToAnimatedValue>::AnimatedValue".format(computed) return "<{} as ToAnimatedValue>::AnimatedValue".format(computed)
def nscsspropertyid(self):
return "nsCSSPropertyID::eCSSProperty_%s" % self.ident
class Shorthand(Property):
class Shorthand(object): def __init__(
def __init__(self, name, sub_properties, spec=None, self,
servo_2013_pref=None, name,
servo_2020_pref=None, sub_properties,
gecko_pref=None, spec=None,
enabled_in="content", servo_2013_pref=None,
allowed_in_keyframe_block=True, alias=None, extra_prefixes=None, servo_2020_pref=None,
allowed_in_page_rule=False, flags=None): gecko_pref=None,
self.name = name enabled_in="content",
if not spec: rule_types_allowed=DEFAULT_RULES,
raise TypeError("Spec should be specified for %s" % name) aliases=None,
self.spec = spec extra_prefixes=None,
self.ident = to_rust_ident(name) flags=None,
self.camel_case = to_camel_case(self.ident) ):
self.servo_2013_pref = servo_2013_pref Property.__init__(
self.servo_2020_pref = servo_2020_pref self,
self.gecko_pref = gecko_pref name=name,
spec=spec,
servo_2013_pref=servo_2013_pref,
servo_2020_pref=servo_2020_pref,
gecko_pref=gecko_pref,
enabled_in=enabled_in,
rule_types_allowed=rule_types_allowed,
aliases=aliases,
extra_prefixes=extra_prefixes,
flags=flags,
)
self.sub_properties = sub_properties self.sub_properties = sub_properties
assert enabled_in in ["", "ua", "chrome", "content"]
self.enabled_in = enabled_in
self.alias = parse_property_aliases(alias)
self.extra_prefixes = parse_property_aliases(extra_prefixes)
self.allowed_in_page_rule = arg_to_bool(allowed_in_page_rule)
self.flags = flags.split() if flags else []
# https://drafts.csswg.org/css-animations/#keyframes
# > The <declaration-list> inside of <keyframe-block> accepts any CSS property
# > except those defined in this specification,
# > but does accept the `animation-play-state` property and interprets it specially.
self.allowed_in_keyframe_block = allowed_in_keyframe_block \
and allowed_in_keyframe_block != "False"
def get_animatable(self): def get_animatable(self):
for sub in self.sub_properties: for sub in self.sub_properties:
@ -464,29 +578,6 @@ class Shorthand(object):
def type(): def type():
return "shorthand" return "shorthand"
def experimental(self, engine):
if engine == "gecko":
return bool(self.gecko_pref)
elif engine == "servo-2013":
return bool(self.servo_2013_pref)
elif engine == "servo-2020":
return bool(self.servo_2020_pref)
else:
raise Exception("Bad engine: " + engine)
# FIXME(emilio): Shorthand and Longhand should really share a base class.
def explicitly_enabled_in_ua_sheets(self):
return self.enabled_in in ["ua", "chrome"]
def explicitly_enabled_in_chrome(self):
return self.enabled_in == "chrome"
def enabled_in_content(self):
return self.enabled_in == "content"
def nscsspropertyid(self):
return "nsCSSPropertyID::eCSSProperty_%s" % self.ident
class Alias(object): class Alias(object):
def __init__(self, name, original, gecko_pref): def __init__(self, name, original, gecko_pref):
@ -500,8 +591,7 @@ class Alias(object):
self.servo_2020_pref = original.servo_2020_pref self.servo_2020_pref = original.servo_2020_pref
self.gecko_pref = gecko_pref self.gecko_pref = gecko_pref
self.transitionable = original.transitionable self.transitionable = original.transitionable
self.allowed_in_page_rule = original.allowed_in_page_rule self.rule_types_allowed = original.rule_types_allowed
self.allowed_in_keyframe_block = original.allowed_in_keyframe_block
@staticmethod @staticmethod
def type(): def type():
@ -580,7 +670,9 @@ class PropertiesData(object):
self.shorthands = [] self.shorthands = []
self.shorthands_by_name = {} self.shorthands_by_name = {}
self.shorthand_aliases = [] self.shorthand_aliases = []
self.counted_unknown_properties = [CountedUnknownProperty(p) for p in COUNTED_UNKNOWN_PROPERTIES] self.counted_unknown_properties = [
CountedUnknownProperty(p) for p in COUNTED_UNKNOWN_PROPERTIES
]
def new_style_struct(self, *args, **kwargs): def new_style_struct(self, *args, **kwargs):
style_struct = StyleStruct(*args, **kwargs) style_struct = StyleStruct(*args, **kwargs)
@ -595,7 +687,7 @@ class PropertiesData(object):
# See servo/servo#14941. # See servo/servo#14941.
if self.engine == "gecko": if self.engine == "gecko":
for (prefix, pref) in property.extra_prefixes: for (prefix, pref) in property.extra_prefixes:
property.alias.append(('-%s-%s' % (prefix, property.name), pref)) property.aliases.append(("-%s-%s" % (prefix, property.name), pref))
def declare_longhand(self, name, engines=None, **kwargs): def declare_longhand(self, name, engines=None, **kwargs):
engines = engines.split() engines = engines.split()
@ -604,13 +696,15 @@ class PropertiesData(object):
longhand = Longhand(self.current_style_struct, name, **kwargs) longhand = Longhand(self.current_style_struct, name, **kwargs)
self.add_prefixed_aliases(longhand) self.add_prefixed_aliases(longhand)
longhand.alias = [Alias(xp[0], longhand, xp[1]) for xp in longhand.alias] longhand.aliases = [Alias(xp[0], longhand, xp[1]) for xp in longhand.aliases]
self.longhand_aliases += longhand.alias self.longhand_aliases += longhand.aliases
self.current_style_struct.longhands.append(longhand) self.current_style_struct.longhands.append(longhand)
self.longhands.append(longhand) self.longhands.append(longhand)
self.longhands_by_name[name] = longhand self.longhands_by_name[name] = longhand
if longhand.logical_group: if longhand.logical_group:
self.longhands_by_logical_group.setdefault(longhand.logical_group, []).append(longhand) self.longhands_by_logical_group.setdefault(
longhand.logical_group, []
).append(longhand)
return longhand return longhand
@ -622,8 +716,8 @@ class PropertiesData(object):
sub_properties = [self.longhands_by_name[s] for s in sub_properties] sub_properties = [self.longhands_by_name[s] for s in sub_properties]
shorthand = Shorthand(name, sub_properties, *args, **kwargs) shorthand = Shorthand(name, sub_properties, *args, **kwargs)
self.add_prefixed_aliases(shorthand) self.add_prefixed_aliases(shorthand)
shorthand.alias = [Alias(xp[0], shorthand, xp[1]) for xp in shorthand.alias] shorthand.aliases = [Alias(xp[0], shorthand, xp[1]) for xp in shorthand.aliases]
self.shorthand_aliases += shorthand.alias self.shorthand_aliases += shorthand.aliases
self.shorthands.append(shorthand) self.shorthands.append(shorthand)
self.shorthands_by_name[name] = shorthand self.shorthands_by_name[name] = shorthand
return shorthand return shorthand
@ -686,31 +780,31 @@ class PropertyRestrictions:
# https://drafts.csswg.org/css-pseudo/#first-letter-styling # https://drafts.csswg.org/css-pseudo/#first-letter-styling
@staticmethod @staticmethod
def first_letter(data): def first_letter(data):
props = set([ props = set(
"color", [
"opacity", "color",
"float", "opacity",
"initial-letter", "float",
"initial-letter",
# Kinda like css-fonts? # Kinda like css-fonts?
"-moz-osx-font-smoothing", "-moz-osx-font-smoothing",
# Kinda like css-text?
# Kinda like css-text? "-webkit-text-stroke-width",
"-webkit-text-stroke-width", "-webkit-text-fill-color",
"-webkit-text-fill-color", "-webkit-text-stroke-color",
"-webkit-text-stroke-color", "vertical-align",
"vertical-align", "line-height",
"line-height", # Kinda like css-backgrounds?
"background-blend-mode",
# Kinda like css-backgrounds? ]
"background-blend-mode", + PropertyRestrictions.shorthand(data, "padding")
] + PropertyRestrictions.shorthand(data, "padding") + PropertyRestrictions.shorthand(data, "margin")
+ PropertyRestrictions.shorthand(data, "margin") + PropertyRestrictions.spec(data, "css-fonts")
+ PropertyRestrictions.spec(data, "css-fonts") + PropertyRestrictions.spec(data, "css-backgrounds")
+ PropertyRestrictions.spec(data, "css-backgrounds") + PropertyRestrictions.spec(data, "css-text")
+ PropertyRestrictions.spec(data, "css-text") + PropertyRestrictions.spec(data, "css-shapes")
+ PropertyRestrictions.spec(data, "css-shapes") + PropertyRestrictions.spec(data, "css-text-decor")
+ PropertyRestrictions.spec(data, "css-text-decor")) )
_add_logical_props(data, props) _add_logical_props(data, props)
@ -720,27 +814,27 @@ class PropertyRestrictions:
# https://drafts.csswg.org/css-pseudo/#first-line-styling # https://drafts.csswg.org/css-pseudo/#first-line-styling
@staticmethod @staticmethod
def first_line(data): def first_line(data):
props = set([ props = set(
# Per spec. [
"color", # Per spec.
"opacity", "color",
"opacity",
# Kinda like css-fonts? # Kinda like css-fonts?
"-moz-osx-font-smoothing", "-moz-osx-font-smoothing",
# Kinda like css-text?
# Kinda like css-text? "-webkit-text-stroke-width",
"-webkit-text-stroke-width", "-webkit-text-fill-color",
"-webkit-text-fill-color", "-webkit-text-stroke-color",
"-webkit-text-stroke-color", "vertical-align",
"vertical-align", "line-height",
"line-height", # Kinda like css-backgrounds?
"background-blend-mode",
# Kinda like css-backgrounds? ]
"background-blend-mode", + PropertyRestrictions.spec(data, "css-fonts")
] + PropertyRestrictions.spec(data, "css-fonts") + PropertyRestrictions.spec(data, "css-backgrounds")
+ PropertyRestrictions.spec(data, "css-backgrounds") + PropertyRestrictions.spec(data, "css-text")
+ PropertyRestrictions.spec(data, "css-text") + PropertyRestrictions.spec(data, "css-text-decor")
+ PropertyRestrictions.spec(data, "css-text-decor")) )
# These are probably Gecko bugs and should be supported per spec. # These are probably Gecko bugs and should be supported per spec.
for prop in PropertyRestrictions.shorthand(data, "border"): for prop in PropertyRestrictions.shorthand(data, "border"):
@ -770,39 +864,46 @@ class PropertyRestrictions:
# https://drafts.csswg.org/css-pseudo/#marker-pseudo # https://drafts.csswg.org/css-pseudo/#marker-pseudo
@staticmethod @staticmethod
def marker(data): def marker(data):
return set([ return set(
"color", [
"text-combine-upright", "color",
"unicode-bidi", "text-combine-upright",
"direction", "text-transform",
"content", "unicode-bidi",
"-moz-osx-font-smoothing", "direction",
] + PropertyRestrictions.spec(data, "css-fonts")) "content",
"-moz-osx-font-smoothing",
]
+ PropertyRestrictions.spec(data, "css-fonts")
+ PropertyRestrictions.spec(data, "css-animations")
+ PropertyRestrictions.spec(data, "css-transitions")
)
# https://www.w3.org/TR/webvtt1/#the-cue-pseudo-element # https://www.w3.org/TR/webvtt1/#the-cue-pseudo-element
@staticmethod @staticmethod
def cue(data): def cue(data):
return set([ return set(
"color", [
"opacity", "color",
"visibility", "opacity",
"text-shadow", "visibility",
"white-space", "text-shadow",
"text-combine-upright", "white-space",
"ruby-position", "text-combine-upright",
"ruby-position",
# XXX Should these really apply to cue? # XXX Should these really apply to cue?
"font-synthesis", "font-synthesis",
"-moz-osx-font-smoothing", "-moz-osx-font-smoothing",
# FIXME(emilio): background-blend-mode should be part of the
# FIXME(emilio): background-blend-mode should be part of the # background shorthand, and get reset, per
# background shorthand, and get reset, per # https://drafts.fxtf.org/compositing/#background-blend-mode
# https://drafts.fxtf.org/compositing/#background-blend-mode "background-blend-mode",
"background-blend-mode", ]
] + PropertyRestrictions.shorthand(data, "text-decoration") + PropertyRestrictions.shorthand(data, "text-decoration")
+ PropertyRestrictions.shorthand(data, "background") + PropertyRestrictions.shorthand(data, "background")
+ PropertyRestrictions.shorthand(data, "outline") + PropertyRestrictions.shorthand(data, "outline")
+ PropertyRestrictions.shorthand(data, "font")) + PropertyRestrictions.shorthand(data, "font")
)
class CountedUnknownProperty: class CountedUnknownProperty:

View file

@ -14,7 +14,7 @@ use crate::parser::ParserContext;
use crate::properties::animated_properties::{AnimationValue, AnimationValueMap}; use crate::properties::animated_properties::{AnimationValue, AnimationValueMap};
use crate::selector_parser::SelectorImpl; use crate::selector_parser::SelectorImpl;
use crate::shared_lock::Locked; use crate::shared_lock::Locked;
use crate::str::{CssString, CssStringBorrow, CssStringWriter}; use crate::str::{CssString, CssStringWriter};
use crate::stylesheets::{CssRuleType, Origin, UrlExtraData}; use crate::stylesheets::{CssRuleType, Origin, UrlExtraData};
use crate::values::computed::Context; use crate::values::computed::Context;
use cssparser::{parse_important, CowRcStr, DeclarationListParser, ParserInput}; use cssparser::{parse_important, CowRcStr, DeclarationListParser, ParserInput};
@ -348,21 +348,6 @@ impl PropertyDeclarationBlock {
.find(|(declaration, _)| declaration.id() == property) .find(|(declaration, _)| declaration.id() == property)
} }
/// Get a declaration for a given property with the specified importance.
#[inline]
pub fn get_at_importance(
&self,
property: PropertyDeclarationId,
importance: Importance,
) -> Option<&PropertyDeclaration> {
let (declaration, i) = self.get(property)?;
if i == importance {
Some(declaration)
} else {
None
}
}
/// Tries to serialize a given shorthand from the declarations in this /// Tries to serialize a given shorthand from the declarations in this
/// block. /// block.
pub fn shorthand_to_css( pub fn shorthand_to_css(
@ -844,14 +829,16 @@ impl PropertyDeclarationBlock {
// getKeyframes() implementation for CSS animations, if // getKeyframes() implementation for CSS animations, if
// |computed_values| is supplied, we use it to expand such variable // |computed_values| is supplied, we use it to expand such variable
// declarations. This will be fixed properly in Gecko bug 1391537. // declarations. This will be fixed properly in Gecko bug 1391537.
(&PropertyDeclaration::WithVariables(ref declaration), Some(ref _computed_values)) => { (&PropertyDeclaration::WithVariables(ref declaration), Some(ref computed_values)) => {
declaration declaration
.value .value
.substitute_variables( .substitute_variables(
declaration.id, declaration.id,
computed_values.writing_mode,
custom_properties.as_ref(), custom_properties.as_ref(),
QuirksMode::NoQuirks, QuirksMode::NoQuirks,
device, device,
&mut Default::default()
) )
.to_css(dest) .to_css(dest)
}, },
@ -945,7 +932,7 @@ impl PropertyDeclarationBlock {
let mut already_serialized = NonCustomPropertyIdSet::new(); let mut already_serialized = NonCustomPropertyIdSet::new();
// Step 3 // Step 3
for (declaration, importance) in self.declaration_importance_iter() { 'declaration_loop: for (declaration, importance) in self.declaration_importance_iter() {
// Step 3.1 // Step 3.1
let property = declaration.id(); let property = declaration.id();
let longhand_id = match property { let longhand_id = match property {
@ -971,11 +958,7 @@ impl PropertyDeclarationBlock {
continue; continue;
} }
// Step 3.3 // Steps 3.3 & 3.4
// Step 3.3.1 is done by checking already_serialized while
// iterating below.
// Step 3.3.2
for shorthand in longhand_id.shorthands() { for shorthand in longhand_id.shorthands() {
// We already attempted to serialize this shorthand before. // We already attempted to serialize this shorthand before.
if already_serialized.contains(shorthand.into()) { if already_serialized.contains(shorthand.into()) {
@ -987,74 +970,105 @@ impl PropertyDeclarationBlock {
continue; continue;
} }
// Substep 2 & 3 // Step 3.3.1:
let mut current_longhands = SmallVec::<[_; 10]>::new(); // Let longhands be an array consisting of all CSS
let mut important_count = 0; // declarations in declaration blocks declarations that
let mut found_system = None; // that are not in already serialized and have a property
// name that maps to one of the shorthand properties in
let is_system_font = shorthand == ShorthandId::Font && // shorthands.
self.declarations.iter().any(|l| match l.id() { let longhands = {
PropertyDeclarationId::Longhand(id) => { // TODO(emilio): This could just index in an array if we
if already_serialized.contains(id.into()) { // remove pref-controlled longhands.
return false; let mut ids = LonghandIdSet::new();
}
l.get_system().is_some()
},
PropertyDeclarationId::Custom(..) => {
debug_assert!(l.get_system().is_none());
false
},
});
if is_system_font {
for (longhand, importance) in self.declaration_importance_iter() {
if longhand.get_system().is_some() || longhand.is_default_line_height() {
current_longhands.push(longhand);
if found_system.is_none() {
found_system = longhand.get_system();
}
if importance.important() {
important_count += 1;
}
}
}
} else {
let mut contains_all_longhands = true;
for longhand in shorthand.longhands() { for longhand in shorthand.longhands() {
match self.get(PropertyDeclarationId::Longhand(longhand)) { ids.insert(longhand);
Some((declaration, importance)) => {
current_longhands.push(declaration);
if importance.important() {
important_count += 1;
}
},
None => {
contains_all_longhands = false;
break;
},
}
} }
ids
};
// Substep 1: // Step 3.4.2
if !contains_all_longhands { // If all properties that map to shorthand are not present
continue; // in longhands, continue with the steps labeled shorthand
// loop.
if !self.longhands.contains_all(&longhands) {
continue;
}
// Step 3.4.3:
// Let current longhands be an empty array.
let mut current_longhands = SmallVec::<[_; 10]>::new();
let mut logical_groups = LogicalGroupSet::new();
let mut saw_one = false;
let mut logical_mismatch = false;
let mut seen = LonghandIdSet::new();
let mut important_count = 0;
// Step 3.4.4:
// Append all CSS declarations in longhands that have a
// property name that maps to shorthand to current longhands.
for (declaration, importance) in self.declaration_importance_iter() {
let longhand = match declaration.id() {
PropertyDeclarationId::Longhand(id) => id,
PropertyDeclarationId::Custom(..) => continue,
};
if longhands.contains(longhand) {
saw_one = true;
if importance.important() {
important_count += 1;
}
current_longhands.push(declaration);
if shorthand != ShorthandId::All {
// All is special because it contains both physical
// and logical longhands.
if let Some(g) = longhand.logical_group() {
logical_groups.insert(g);
}
seen.insert(longhand);
if seen == longhands {
break;
}
}
} else if saw_one {
if let Some(g) = longhand.logical_group() {
if logical_groups.contains(g) {
logical_mismatch = true;
break;
}
}
} }
} }
// Substep 4 // 3.4.5:
// If there is one or more CSS declarations in current
// longhands have their important flag set and one or more
// with it unset, continue with the steps labeled shorthand
// loop.
let is_important = important_count > 0; let is_important = important_count > 0;
if is_important && important_count != current_longhands.len() { if is_important && important_count != current_longhands.len() {
continue; continue;
} }
// 3.4.6:
// If theres any declaration in declaration block in between
// the first and the last longhand in current longhands which
// belongs to the same logical property group, but has a
// different mapping logic as any of the longhands in current
// longhands, and is not in current longhands, continue with
// the steps labeled shorthand loop.
if logical_mismatch {
continue;
}
let importance = if is_important { let importance = if is_important {
Importance::Important Importance::Important
} else { } else {
Importance::Normal Importance::Normal
}; };
// Substep 5 - Let value be the result of invoking serialize // 3.4.7:
// a CSS value of current longhands. // Let value be the result of invoking serialize a CSS value
// of current longhands.
let appendable_value = match shorthand let appendable_value = match shorthand
.get_shorthand_appendable_value(current_longhands.iter().cloned()) .get_shorthand_appendable_value(current_longhands.iter().cloned())
{ {
@ -1065,44 +1079,41 @@ impl PropertyDeclarationBlock {
// We avoid re-serializing if we're already an // We avoid re-serializing if we're already an
// AppendableValue::Css. // AppendableValue::Css.
let mut v = CssString::new(); let mut v = CssString::new();
let value = match (appendable_value, found_system) { let value = match appendable_value {
( AppendableValue::Css { css, with_variables } => {
AppendableValue::Css {
css,
with_variables,
},
_,
) => {
debug_assert!(!css.is_empty()); debug_assert!(!css.is_empty());
AppendableValue::Css { AppendableValue::Css { css, with_variables }
css: css,
with_variables: with_variables,
}
}, },
#[cfg(feature = "gecko")] other => {
(_, Some(sys)) => {
sys.to_css(&mut CssWriter::new(&mut v))?;
AppendableValue::Css {
css: CssStringBorrow::from(&v),
with_variables: false,
}
},
(other, _) => {
append_declaration_value(&mut v, other)?; append_declaration_value(&mut v, other)?;
// Substep 6 // 3.4.8:
// If value is the empty string, continue with the
// steps labeled shorthand loop.
if v.is_empty() { if v.is_empty() {
continue; continue;
} }
AppendableValue::Css { AppendableValue::Css {
css: CssStringBorrow::from(&v), // Safety: serialization only generates valid utf-8.
#[cfg(feature = "gecko")]
css: unsafe { v.as_str_unchecked() },
#[cfg(feature = "servo")]
css: &v,
with_variables: false, with_variables: false,
} }
}, },
}; };
// Substeps 7 and 8 // 3.4.9:
// Let serialized declaration be the result of invoking
// serialize a CSS declaration with property name shorthand,
// value value, and the important flag set if the CSS
// declarations in current longhands have their important
// flag set.
//
// 3.4.10:
// Append serialized declaration to list.
append_serialization::<Cloned<slice::Iter<_>>, _>( append_serialization::<Cloned<slice::Iter<_>>, _>(
dest, dest,
&shorthand, &shorthand,
@ -1111,6 +1122,9 @@ impl PropertyDeclarationBlock {
&mut is_first_serialization, &mut is_first_serialization,
)?; )?;
// 3.4.11:
// Append the property names of all items of current
// longhands to already serialized.
for current_longhand in &current_longhands { for current_longhand in &current_longhands {
let longhand_id = match current_longhand.id() { let longhand_id = match current_longhand.id() {
PropertyDeclarationId::Longhand(id) => id, PropertyDeclarationId::Longhand(id) => id,
@ -1121,24 +1135,21 @@ impl PropertyDeclarationBlock {
already_serialized.insert(longhand_id.into()); already_serialized.insert(longhand_id.into());
} }
// FIXME(https://github.com/w3c/csswg-drafts/issues/1774) // 3.4.12:
// The specification does not include an instruction to abort // Continue with the steps labeled declaration loop.
// the shorthand loop at this point, but doing so both matches continue 'declaration_loop;
// Gecko and makes sense since shorthands are checked in
// preferred order.
break;
} }
// Step 3.3.4 // Steps 3.5, 3.6 & 3.7:
if already_serialized.contains(longhand_id.into()) { // Let value be the result of invoking serialize a CSS value of
continue; // declaration.
} //
// Let serialized declaration be the result of invoking
// Steps 3.3.5, 3.3.6 & 3.3.7 // serialize a CSS declaration with property name property,
// Need to specify an iterator type here even though its unused to work around // value value, and the important flag set if declaration has
// "error: unable to infer enough type information about `_`; // its important flag set.
// type annotations or generic parameter binding required [E0282]" //
// Use the same type as earlier call to reuse generated code. // Append serialized declaration to list.
append_serialization::<Cloned<slice::Iter<_>>, _>( append_serialization::<Cloned<slice::Iter<_>>, _>(
dest, dest,
&property, &property,
@ -1147,7 +1158,8 @@ impl PropertyDeclarationBlock {
&mut is_first_serialization, &mut is_first_serialization,
)?; )?;
// Step 3.3.8 // Step 3.8:
// Append property to already serialized.
already_serialized.insert(longhand_id.into()); already_serialized.insert(longhand_id.into());
} }
@ -1173,7 +1185,7 @@ where
/// or when storing a serialized shorthand value before appending directly. /// or when storing a serialized shorthand value before appending directly.
Css { Css {
/// The raw CSS string. /// The raw CSS string.
css: CssStringBorrow<'a>, css: &'a str,
/// Whether the original serialization contained variables or not. /// Whether the original serialization contained variables or not.
with_variables: bool, with_variables: bool,
}, },
@ -1201,7 +1213,7 @@ where
I: Iterator<Item = &'a PropertyDeclaration>, I: Iterator<Item = &'a PropertyDeclaration>,
{ {
match appendable_value { match appendable_value {
AppendableValue::Css { css, .. } => css.append_to(dest), AppendableValue::Css { css, .. } => dest.write_str(css),
AppendableValue::Declaration(decl) => decl.to_css(dest), AppendableValue::Declaration(decl) => decl.to_css(dest),
AppendableValue::DeclarationsForShorthand(shorthand, decls) => { AppendableValue::DeclarationsForShorthand(shorthand, decls) => {
shorthand.longhands_to_css(decls, &mut CssWriter::new(dest)) shorthand.longhands_to_css(decls, &mut CssWriter::new(dest))
@ -1263,11 +1275,12 @@ pub fn parse_style_attribute(
url_data: &UrlExtraData, url_data: &UrlExtraData,
error_reporter: Option<&dyn ParseErrorReporter>, error_reporter: Option<&dyn ParseErrorReporter>,
quirks_mode: QuirksMode, quirks_mode: QuirksMode,
rule_type: CssRuleType,
) -> PropertyDeclarationBlock { ) -> PropertyDeclarationBlock {
let context = ParserContext::new( let context = ParserContext::new(
Origin::Author, Origin::Author,
url_data, url_data,
Some(CssRuleType::Style), Some(rule_type),
ParsingMode::DEFAULT, ParsingMode::DEFAULT,
quirks_mode, quirks_mode,
error_reporter, error_reporter,
@ -1287,15 +1300,17 @@ pub fn parse_one_declaration_into(
declarations: &mut SourcePropertyDeclaration, declarations: &mut SourcePropertyDeclaration,
id: PropertyId, id: PropertyId,
input: &str, input: &str,
origin: Origin,
url_data: &UrlExtraData, url_data: &UrlExtraData,
error_reporter: Option<&dyn ParseErrorReporter>, error_reporter: Option<&dyn ParseErrorReporter>,
parsing_mode: ParsingMode, parsing_mode: ParsingMode,
quirks_mode: QuirksMode, quirks_mode: QuirksMode,
rule_type: CssRuleType,
) -> Result<(), ()> { ) -> Result<(), ()> {
let context = ParserContext::new( let context = ParserContext::new(
Origin::Author, origin,
url_data, url_data,
Some(CssRuleType::Style), Some(rule_type),
parsing_mode, parsing_mode,
quirks_mode, quirks_mode,
error_reporter, error_reporter,

View file

@ -376,18 +376,6 @@ def set_gecko_property(ffi_name, expr):
<%call expr="impl_simple_clone(ident, gecko_ffi_name)"></%call> <%call expr="impl_simple_clone(ident, gecko_ffi_name)"></%call>
</%def> </%def>
<%def name="impl_absolute_length(ident, gecko_ffi_name)">
#[allow(non_snake_case)]
pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) {
${set_gecko_property(gecko_ffi_name, "v.to_i32_au()")}
}
<%call expr="impl_simple_copy(ident, gecko_ffi_name)"></%call>
#[allow(non_snake_case)]
pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T {
Au(self.gecko.${gecko_ffi_name}).into()
}
</%def>
<%def name="impl_non_negative_length(ident, gecko_ffi_name, inherit_from=None, <%def name="impl_non_negative_length(ident, gecko_ffi_name, inherit_from=None,
round_to_pixels=False)"> round_to_pixels=False)">
#[allow(non_snake_case)] #[allow(non_snake_case)]
@ -593,11 +581,6 @@ impl Clone for ${style_struct.gecko_struct_name} {
longhands = [x for x in style_struct.longhands longhands = [x for x in style_struct.longhands
if not (skip_longhands == "*" or x.name in skip_longhands.split())] if not (skip_longhands == "*" or x.name in skip_longhands.split())]
# Types used with predefined_type()-defined properties that we can auto-generate.
predefined_types = {
"MozScriptMinSize": impl_absolute_length,
}
def longhand_method(longhand): def longhand_method(longhand):
args = dict(ident=longhand.ident, gecko_ffi_name=longhand.gecko_ffi_name) args = dict(ident=longhand.ident, gecko_ffi_name=longhand.gecko_ffi_name)
@ -610,8 +593,6 @@ impl Clone for ${style_struct.gecko_struct_name} {
args.update(keyword=longhand.keyword) args.update(keyword=longhand.keyword)
if "font" in longhand.ident: if "font" in longhand.ident:
args.update(cast_type=longhand.cast_type) args.update(cast_type=longhand.cast_type)
elif longhand.predefined_type in predefined_types:
method = predefined_types[longhand.predefined_type]
else: else:
method = impl_simple method = impl_simple
@ -920,9 +901,10 @@ fn static_assert() {
} }
pub fn unzoom_fonts(&mut self, device: &Device) { pub fn unzoom_fonts(&mut self, device: &Device) {
self.gecko.mSize = device.unzoom_text(Au(self.gecko.mSize)).0; use crate::values::generics::NonNegative;
self.gecko.mScriptUnconstrainedSize = device.unzoom_text(Au(self.gecko.mScriptUnconstrainedSize)).0; self.gecko.mSize = NonNegative(device.unzoom_text(self.gecko.mSize.0));
self.gecko.mFont.size = device.unzoom_text(Au(self.gecko.mFont.size)).0; self.gecko.mScriptUnconstrainedSize = NonNegative(device.unzoom_text(self.gecko.mScriptUnconstrainedSize.0));
self.gecko.mFont.size = NonNegative(device.unzoom_text(self.gecko.mFont.size.0));
} }
pub fn copy_font_size_from(&mut self, other: &Self) { pub fn copy_font_size_from(&mut self, other: &Self) {
@ -942,25 +924,27 @@ fn static_assert() {
} }
pub fn set_font_size(&mut self, v: FontSize) { pub fn set_font_size(&mut self, v: FontSize) {
let size = Au::from(v.size()); let size = v.size;
self.gecko.mScriptUnconstrainedSize = size.0; self.gecko.mScriptUnconstrainedSize = size;
// These two may be changed from Cascade::fixup_font_stuff. // These two may be changed from Cascade::fixup_font_stuff.
self.gecko.mSize = size.0; self.gecko.mSize = size;
self.gecko.mFont.size = size.0; self.gecko.mFont.size = size;
self.gecko.mFontSizeKeyword = v.keyword_info.kw; self.gecko.mFontSizeKeyword = v.keyword_info.kw;
self.gecko.mFontSizeFactor = v.keyword_info.factor; self.gecko.mFontSizeFactor = v.keyword_info.factor;
self.gecko.mFontSizeOffset = v.keyword_info.offset.to_i32_au(); self.gecko.mFontSizeOffset = v.keyword_info.offset;
} }
pub fn clone_font_size(&self) -> FontSize { pub fn clone_font_size(&self) -> FontSize {
use crate::values::specified::font::KeywordInfo; use crate::values::specified::font::KeywordInfo;
FontSize { FontSize {
size: Au(self.gecko.mSize).into(), size: self.gecko.mSize,
keyword_info: KeywordInfo { keyword_info: KeywordInfo {
kw: self.gecko.mFontSizeKeyword, kw: self.gecko.mFontSizeKeyword,
factor: self.gecko.mFontSizeFactor, factor: self.gecko.mFontSizeFactor,
offset: Au(self.gecko.mFontSizeOffset).into() offset: self.gecko.mFontSizeOffset,
} }
} }
} }
@ -2105,7 +2089,9 @@ pub fn assert_initial_values_match(data: &PerDocumentStyleData) {
let data = data.borrow(); let data = data.borrow();
let cv = data.stylist.device().default_computed_values(); let cv = data.stylist.device().default_computed_values();
<% <%
# Skip properties with initial values that change at computed value time. # Skip properties with initial values that change at computed
# value time, or whose initial value depends on the document
# / other prefs.
SKIPPED = [ SKIPPED = [
"border-top-width", "border-top-width",
"border-bottom-width", "border-bottom-width",
@ -2114,6 +2100,7 @@ pub fn assert_initial_values_match(data: &PerDocumentStyleData) {
"font-family", "font-family",
"font-size", "font-size",
"outline-width", "outline-width",
"color",
] ]
TO_TEST = [p for p in data.longhands if p.enabled_in != "" and not p.logical and not p.name in SKIPPED] TO_TEST = [p for p in data.longhands if p.enabled_in != "" and not p.logical and not p.name in SKIPPED]
%> %>

View file

@ -9,7 +9,7 @@
%> %>
<%def name="predefined_type(name, type, initial_value, parse_method='parse', <%def name="predefined_type(name, type, initial_value, parse_method='parse',
needs_context=True, vector=False, vector=False,
computed_type=None, initial_specified_value=None, computed_type=None, initial_specified_value=None,
allow_quirks='No', allow_empty=False, **kwargs)"> allow_quirks='No', allow_empty=False, **kwargs)">
<%def name="predefined_type_inner(name, type, initial_value, parse_method)"> <%def name="predefined_type_inner(name, type, initial_value, parse_method)">
@ -45,10 +45,10 @@
) -> Result<SpecifiedValue, ParseError<'i>> { ) -> Result<SpecifiedValue, ParseError<'i>> {
% if allow_quirks != "No": % if allow_quirks != "No":
specified::${type}::${parse_method}_quirky(context, input, AllowQuirks::${allow_quirks}) specified::${type}::${parse_method}_quirky(context, input, AllowQuirks::${allow_quirks})
% elif needs_context: % elif parse_method != "parse":
specified::${type}::${parse_method}(context, input) specified::${type}::${parse_method}(context, input)
% else: % else:
specified::${type}::${parse_method}(input) <specified::${type} as crate::parser::Parse>::parse(context, input)
% endif % endif
} }
</%def> </%def>
@ -948,7 +948,8 @@
input.parse_entirely(|input| parse_value(context, input)).map(|longhands| { input.parse_entirely(|input| parse_value(context, input)).map(|longhands| {
% for sub_property in shorthand.sub_properties: % for sub_property in shorthand.sub_properties:
% if sub_property.may_be_disabled_in(shorthand, engine): % if sub_property.may_be_disabled_in(shorthand, engine):
if NonCustomPropertyId::from(LonghandId::${sub_property.camel_case}).allowed_in(context) { if NonCustomPropertyId::from(LonghandId::${sub_property.camel_case})
.allowed_in_ignoring_rule_type(context) {
% endif % endif
declarations.push(PropertyDeclaration::${sub_property.camel_case}( declarations.push(PropertyDeclaration::${sub_property.camel_case}(
longhands.${sub_property.ident} longhands.${sub_property.ident}
@ -971,25 +972,24 @@
name, name,
first_property, first_property,
second_property, second_property,
parser_function, parser_function='crate::parser::Parse::parse',
needs_context=True,
**kwargs **kwargs
)"> )">
<%call expr="self.shorthand(name, sub_properties=' '.join([first_property, second_property]), **kwargs)"> <%call expr="self.shorthand(name, sub_properties=' '.join([first_property, second_property]), **kwargs)">
#[allow(unused_imports)] #[allow(unused_imports)]
use crate::parser::Parse; use crate::parser::Parse;
#[allow(unused_imports)]
use crate::values::specified; use crate::values::specified;
pub fn parse_value<'i, 't>( fn parse_value<'i, 't>(
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> { ) -> Result<Longhands, ParseError<'i>> {
let parse_one = |_c: &ParserContext, input: &mut Parser<'i, 't>| { let parse_one = |c: &ParserContext, input: &mut Parser<'i, 't>| -> Result<
% if needs_context: crate::properties::longhands::${to_rust_ident(first_property)}::SpecifiedValue,
${parser_function}(_c, input) ParseError<'i>
% else: > {
${parser_function}(input) ${parser_function}(c, input)
% endif
}; };
let first = parse_one(context, input)?; let first = parse_one(context, input)?;
@ -1017,26 +1017,29 @@
</%call> </%call>
</%def> </%def>
<%def name="four_sides_shorthand(name, sub_property_pattern, parser_function, <%def name="four_sides_shorthand(name, sub_property_pattern,
needs_context=True, allow_quirks='No', **kwargs)"> parser_function='crate::parser::Parse::parse',
allow_quirks='No', **kwargs)">
<% sub_properties=' '.join(sub_property_pattern % side for side in PHYSICAL_SIDES) %> <% sub_properties=' '.join(sub_property_pattern % side for side in PHYSICAL_SIDES) %>
<%call expr="self.shorthand(name, sub_properties=sub_properties, **kwargs)"> <%call expr="self.shorthand(name, sub_properties=sub_properties, **kwargs)">
#[allow(unused_imports)] #[allow(unused_imports)]
use crate::parser::Parse; use crate::parser::Parse;
use crate::values::generics::rect::Rect; use crate::values::generics::rect::Rect;
#[allow(unused_imports)]
use crate::values::specified; use crate::values::specified;
pub fn parse_value<'i, 't>( fn parse_value<'i, 't>(
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<Longhands, ParseError<'i>> { ) -> Result<Longhands, ParseError<'i>> {
let rect = Rect::parse_with(context, input, |_c, i| { let rect = Rect::parse_with(context, input, |c, i| -> Result<
crate::properties::longhands::${to_rust_ident(sub_property_pattern % "top")}::SpecifiedValue,
ParseError<'i>
> {
% if allow_quirks != "No": % if allow_quirks != "No":
${parser_function}_quirky(_c, i, specified::AllowQuirks::${allow_quirks}) ${parser_function}_quirky(c, i, specified::AllowQuirks::${allow_quirks})
% elif needs_context:
${parser_function}(_c, i)
% else: % else:
${parser_function}(i) ${parser_function}(c, i)
% endif % endif
})?; })?;
Ok(expanded! { Ok(expanded! {

View file

@ -248,7 +248,7 @@ impl AnimationValue {
decl: &PropertyDeclaration, decl: &PropertyDeclaration,
context: &mut Context, context: &mut Context,
extra_custom_properties: Option<<&Arc<crate::custom_properties::CustomPropertiesMap>>, extra_custom_properties: Option<<&Arc<crate::custom_properties::CustomPropertiesMap>>,
initial: &ComputedValues initial: &ComputedValues,
) -> Option<Self> { ) -> Option<Self> {
use super::PropertyDeclarationVariantRepr; use super::PropertyDeclarationVariantRepr;
@ -367,15 +367,18 @@ impl AnimationValue {
} }
}, },
PropertyDeclaration::WithVariables(ref declaration) => { PropertyDeclaration::WithVariables(ref declaration) => {
let mut cache = Default::default();
let substituted = { let substituted = {
let custom_properties = let custom_properties =
extra_custom_properties.or_else(|| context.style().custom_properties()); extra_custom_properties.or_else(|| context.style().custom_properties());
declaration.value.substitute_variables( declaration.value.substitute_variables(
declaration.id, declaration.id,
context.builder.writing_mode,
custom_properties, custom_properties,
context.quirks_mode, context.quirks_mode,
context.device(), context.device(),
&mut cache,
) )
}; };
return AnimationValue::from_declaration( return AnimationValue::from_declaration(
@ -820,7 +823,7 @@ impl<'a> Iterator for TransitionPropertyIterator<'a> {
if let Some(longhand_id) = longhand_iterator.next() { if let Some(longhand_id) = longhand_iterator.next() {
return Some(TransitionPropertyIteration { return Some(TransitionPropertyIteration {
longhand_id, longhand_id,
index: self.index_range.start, index: self.index_range.start - 1,
}); });
} }
self.longhand_iterator = None; self.longhand_iterator = None;

View file

@ -24,7 +24,7 @@
"border-%s-color" % side_name, "Color", "border-%s-color" % side_name, "Color",
"computed_value::T::currentcolor()", "computed_value::T::currentcolor()",
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
alias=maybe_moz_logical_alias(engine, side, "-moz-border-%s-color"), aliases=maybe_moz_logical_alias(engine, side, "-moz-border-%s-color"),
spec=maybe_logical_spec(side, "color"), spec=maybe_logical_spec(side, "color"),
animation_value_type="AnimatedColor", animation_value_type="AnimatedColor",
logical=is_logical, logical=is_logical,
@ -37,12 +37,11 @@
"border-%s-style" % side_name, "BorderStyle", "border-%s-style" % side_name, "BorderStyle",
"specified::BorderStyle::None", "specified::BorderStyle::None",
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
alias=maybe_moz_logical_alias(engine, side, "-moz-border-%s-style"), aliases=maybe_moz_logical_alias(engine, side, "-moz-border-%s-style"),
spec=maybe_logical_spec(side, "style"), spec=maybe_logical_spec(side, "style"),
animation_value_type="discrete" if not is_logical else "none", animation_value_type="discrete" if not is_logical else "none",
logical=is_logical, logical=is_logical,
logical_group="border-style", logical_group="border-style",
needs_context=False,
)} )}
${helpers.predefined_type( ${helpers.predefined_type(
@ -51,7 +50,7 @@
"crate::values::computed::NonNegativeLength::new(3.)", "crate::values::computed::NonNegativeLength::new(3.)",
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
computed_type="crate::values::computed::NonNegativeLength", computed_type="crate::values::computed::NonNegativeLength",
alias=maybe_moz_logical_alias(engine, side, "-moz-border-%s-width"), aliases=maybe_moz_logical_alias(engine, side, "-moz-border-%s-width"),
spec=maybe_logical_spec(side, "width"), spec=maybe_logical_spec(side, "width"),
animation_value_type="NonNegativeLength", animation_value_type="NonNegativeLength",
logical=is_logical, logical=is_logical,

View file

@ -3,7 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
<%namespace name="helpers" file="/helpers.mako.rs" /> <%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import ALL_AXES, Keyword, Method, to_rust_ident, to_camel_case%> <% from data import ALL_AXES, DEFAULT_RULES_EXCEPT_KEYFRAME, Keyword, Method, to_rust_ident, to_camel_case%>
<% data.new_style_struct("Box", <% data.new_style_struct("Box",
inherited=False, inherited=False,
@ -71,7 +71,6 @@ ${helpers.predefined_type(
initial_specified_value="specified::Float::None", initial_specified_value="specified::Float::None",
spec="https://drafts.csswg.org/css-box/#propdef-float", spec="https://drafts.csswg.org/css-box/#propdef-float",
animation_value_type="discrete", animation_value_type="discrete",
needs_context=False,
servo_restyle_damage="rebuild_and_reflow", servo_restyle_damage="rebuild_and_reflow",
gecko_ffi_name="mFloat", gecko_ffi_name="mFloat",
)} )}
@ -82,7 +81,6 @@ ${helpers.predefined_type(
"computed::Clear::None", "computed::Clear::None",
engines="gecko servo-2013", engines="gecko servo-2013",
animation_value_type="discrete", animation_value_type="discrete",
needs_context=False,
gecko_ffi_name="mBreakType", gecko_ffi_name="mBreakType",
spec="https://drafts.csswg.org/css-box/#propdef-clear", spec="https://drafts.csswg.org/css-box/#propdef-clear",
servo_restyle_damage="rebuild_and_reflow", servo_restyle_damage="rebuild_and_reflow",
@ -117,7 +115,6 @@ ${helpers.single_keyword(
"computed::OverflowClipBox::PaddingBox", "computed::OverflowClipBox::PaddingBox",
engines="gecko", engines="gecko",
enabled_in="ua", enabled_in="ua",
needs_context=False,
gecko_pref="layout.css.overflow-clip-box.enabled", gecko_pref="layout.css.overflow-clip-box.enabled",
animation_value_type="discrete", animation_value_type="discrete",
spec="Internal, may be standardized in the future: \ spec="Internal, may be standardized in the future: \
@ -136,7 +133,6 @@ ${helpers.single_keyword(
logical=logical, logical=logical,
animation_value_type="discrete", animation_value_type="discrete",
spec="https://drafts.csswg.org/css-overflow-3/#propdef-{}".format(full_name), spec="https://drafts.csswg.org/css-overflow-3/#propdef-{}".format(full_name),
needs_context=False,
servo_restyle_damage = "reflow", servo_restyle_damage = "reflow",
gecko_pref="layout.css.overflow-logical.enabled" if logical else None, gecko_pref="layout.css.overflow-logical.enabled" if logical else None,
)} )}
@ -148,7 +144,6 @@ ${helpers.predefined_type(
"computed::OverflowAnchor::Auto", "computed::OverflowAnchor::Auto",
engines="gecko", engines="gecko",
initial_specified_value="specified::OverflowAnchor::Auto", initial_specified_value="specified::OverflowAnchor::Auto",
needs_context=False,
gecko_pref="layout.css.scroll-anchoring.enabled", gecko_pref="layout.css.scroll-anchoring.enabled",
spec="https://drafts.csswg.org/css-scroll-anchoring/#exclusion-api", spec="https://drafts.csswg.org/css-scroll-anchoring/#exclusion-api",
animation_value_type="discrete", animation_value_type="discrete",
@ -222,7 +217,7 @@ ${helpers.predefined_type(
need_index=True, need_index=True,
animation_value_type="none", animation_value_type="none",
extra_prefixes=animation_extra_prefixes, extra_prefixes=animation_extra_prefixes,
allowed_in_keyframe_block=False, rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
spec="https://drafts.csswg.org/css-animations/#propdef-animation-name", spec="https://drafts.csswg.org/css-animations/#propdef-animation-name",
)} )}
@ -252,7 +247,6 @@ ${helpers.predefined_type(
need_index=True, need_index=True,
animation_value_type="none", animation_value_type="none",
extra_prefixes=animation_extra_prefixes, extra_prefixes=animation_extra_prefixes,
allowed_in_keyframe_block=True,
spec="https://drafts.csswg.org/css-transitions/#propdef-animation-timing-function", spec="https://drafts.csswg.org/css-transitions/#propdef-animation-timing-function",
)} )}
@ -266,7 +260,7 @@ ${helpers.predefined_type(
need_index=True, need_index=True,
animation_value_type="none", animation_value_type="none",
extra_prefixes=animation_extra_prefixes, extra_prefixes=animation_extra_prefixes,
allowed_in_keyframe_block=False, rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
spec="https://drafts.csswg.org/css-animations/#propdef-animation-iteration-count", spec="https://drafts.csswg.org/css-animations/#propdef-animation-iteration-count",
)} )}
@ -283,7 +277,7 @@ ${helpers.single_keyword(
extra_prefixes=animation_extra_prefixes, extra_prefixes=animation_extra_prefixes,
gecko_inexhaustive=True, gecko_inexhaustive=True,
spec="https://drafts.csswg.org/css-animations/#propdef-animation-direction", spec="https://drafts.csswg.org/css-animations/#propdef-animation-direction",
allowed_in_keyframe_block=False, rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
)} )}
${helpers.single_keyword( ${helpers.single_keyword(
@ -296,7 +290,7 @@ ${helpers.single_keyword(
extra_prefixes=animation_extra_prefixes, extra_prefixes=animation_extra_prefixes,
gecko_enum_prefix="StyleAnimationPlayState", gecko_enum_prefix="StyleAnimationPlayState",
spec="https://drafts.csswg.org/css-animations/#propdef-animation-play-state", spec="https://drafts.csswg.org/css-animations/#propdef-animation-play-state",
allowed_in_keyframe_block=False, rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
)} )}
${helpers.single_keyword( ${helpers.single_keyword(
@ -310,7 +304,7 @@ ${helpers.single_keyword(
extra_prefixes=animation_extra_prefixes, extra_prefixes=animation_extra_prefixes,
gecko_inexhaustive=True, gecko_inexhaustive=True,
spec="https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode", spec="https://drafts.csswg.org/css-animations/#propdef-animation-fill-mode",
allowed_in_keyframe_block=False, rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
)} )}
${helpers.predefined_type( ${helpers.predefined_type(
@ -324,7 +318,7 @@ ${helpers.predefined_type(
animation_value_type="none", animation_value_type="none",
extra_prefixes=animation_extra_prefixes, extra_prefixes=animation_extra_prefixes,
spec="https://drafts.csswg.org/css-animations/#propdef-animation-delay", spec="https://drafts.csswg.org/css-animations/#propdef-animation-delay",
allowed_in_keyframe_block=False, rule_types_allowed=DEFAULT_RULES_EXCEPT_KEYFRAME,
)} )}
<% transform_extra_prefixes = "moz:layout.css.prefixes.transforms webkit" %> <% transform_extra_prefixes = "moz:layout.css.prefixes.transforms webkit" %>
@ -468,7 +462,6 @@ ${helpers.predefined_type(
"OverscrollBehavior", "OverscrollBehavior",
"computed::OverscrollBehavior::Auto", "computed::OverscrollBehavior::Auto",
engines="gecko", engines="gecko",
needs_context=False,
logical_group="overscroll-behavior", logical_group="overscroll-behavior",
logical=logical, logical=logical,
gecko_pref="layout.css.overscroll-behavior.enabled", gecko_pref="layout.css.overscroll-behavior.enabled",
@ -494,7 +487,6 @@ ${helpers.predefined_type(
"BreakBetween", "BreakBetween",
"computed::BreakBetween::Auto", "computed::BreakBetween::Auto",
engines="gecko", engines="gecko",
needs_context=False,
spec="https://drafts.csswg.org/css-break/#propdef-break-after", spec="https://drafts.csswg.org/css-break/#propdef-break-after",
animation_value_type="discrete", animation_value_type="discrete",
)} )}
@ -504,7 +496,6 @@ ${helpers.predefined_type(
"BreakBetween", "BreakBetween",
"computed::BreakBetween::Auto", "computed::BreakBetween::Auto",
engines="gecko", engines="gecko",
needs_context=False,
spec="https://drafts.csswg.org/css-break/#propdef-break-before", spec="https://drafts.csswg.org/css-break/#propdef-break-before",
animation_value_type="discrete", animation_value_type="discrete",
)} )}
@ -514,8 +505,7 @@ ${helpers.predefined_type(
"BreakWithin", "BreakWithin",
"computed::BreakWithin::Auto", "computed::BreakWithin::Auto",
engines="gecko", engines="gecko",
needs_context=False, aliases="page-break-inside",
alias="page-break-inside",
spec="https://drafts.csswg.org/css-break/#propdef-break-inside", spec="https://drafts.csswg.org/css-break/#propdef-break-inside",
animation_value_type="discrete", animation_value_type="discrete",
)} )}
@ -528,7 +518,6 @@ ${helpers.predefined_type(
"computed::Resize::None", "computed::Resize::None",
engines="gecko", engines="gecko",
animation_value_type="discrete", animation_value_type="discrete",
needs_context=False,
gecko_ffi_name="mResize", gecko_ffi_name="mResize",
spec="https://drafts.csswg.org/css-ui/#propdef-resize", spec="https://drafts.csswg.org/css-ui/#propdef-resize",
)} )}
@ -573,7 +562,6 @@ ${helpers.single_keyword(
"border-box fill-box view-box", "border-box fill-box view-box",
engines="gecko", engines="gecko",
gecko_enum_prefix="StyleGeometryBox", gecko_enum_prefix="StyleGeometryBox",
gecko_pref="svg.transform-box.enabled",
spec="https://drafts.csswg.org/css-transforms/#transform-box", spec="https://drafts.csswg.org/css-transforms/#transform-box",
gecko_inexhaustive="True", gecko_inexhaustive="True",
animation_value_type="discrete", animation_value_type="discrete",
@ -585,7 +573,6 @@ ${helpers.predefined_type(
"computed::TransformStyle::Flat", "computed::TransformStyle::Flat",
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
spec="https://drafts.csswg.org/css-transforms-2/#transform-style-property", spec="https://drafts.csswg.org/css-transforms-2/#transform-style-property",
needs_context=False,
extra_prefixes=transform_extra_prefixes, extra_prefixes=transform_extra_prefixes,
flags="CREATES_STACKING_CONTEXT FIXPOS_CB", flags="CREATES_STACKING_CONTEXT FIXPOS_CB",
animation_value_type="discrete", animation_value_type="discrete",
@ -615,22 +602,19 @@ ${helpers.predefined_type(
spec="https://drafts.csswg.org/css-contain/#contain-property", spec="https://drafts.csswg.org/css-contain/#contain-property",
)} )}
// Non-standard
${helpers.predefined_type( ${helpers.predefined_type(
"-moz-appearance", "appearance",
"Appearance", "Appearance",
"computed::Appearance::None", "computed::Appearance::None",
engines="gecko", engines="gecko",
alias="-webkit-appearance", aliases="-moz-appearance -webkit-appearance",
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance)", spec="https://drafts.csswg.org/css-ui-4/#propdef-appearance",
animation_value_type="discrete", animation_value_type="discrete",
gecko_ffi_name="mAppearance", gecko_ffi_name="mAppearance",
)} )}
// A UA-sheet only property that is always set to the same value as // The inherent widget type of an element, selected by specifying
// -moz-appearance. Used to record telemetry for when author sheets // `appearance: auto`.
// override the value of -moz-appearance; see
// nsIFrame::RecordAppearanceTelemetry.
${helpers.predefined_type( ${helpers.predefined_type(
"-moz-default-appearance", "-moz-default-appearance",
"Appearance", "Appearance",
@ -638,7 +622,7 @@ ${helpers.predefined_type(
engines="gecko", engines="gecko",
animation_value_type="none", animation_value_type="none",
spec="Internal (not web-exposed)", spec="Internal (not web-exposed)",
enabled_in="ua", enabled_in="chrome",
gecko_ffi_name="mDefaultAppearance", gecko_ffi_name="mDefaultAppearance",
)} )}

View file

@ -84,7 +84,6 @@ ${helpers.predefined_type(
"BorderStyle", "BorderStyle",
"computed::BorderStyle::None", "computed::BorderStyle::None",
engines="gecko", engines="gecko",
needs_context=False,
initial_specified_value="specified::BorderStyle::None", initial_specified_value="specified::BorderStyle::None",
extra_prefixes="moz:layout.css.prefixes.columns", extra_prefixes="moz:layout.css.prefixes.columns",
animation_value_type="discrete", animation_value_type="discrete",

View file

@ -228,25 +228,27 @@ ${helpers.predefined_type(
)} )}
${helpers.predefined_type( ${helpers.predefined_type(
"-moz-script-level", "math-depth",
"MozScriptLevel", "MathDepth",
"0", "0",
engines="gecko", engines="gecko",
gecko_pref="layout.css.math-depth.enabled",
has_effect_on_gecko_scrollbars=False,
animation_value_type="none", animation_value_type="none",
enabled_in="ua", enabled_in="ua",
gecko_ffi_name="mScriptLevel", spec="https://mathml-refresh.github.io/mathml-core/#the-math-script-level-property",
spec="Internal (not web-exposed)",
)} )}
${helpers.single_keyword( ${helpers.single_keyword(
"-moz-math-display", "math-style",
"inline block", "normal compact",
engines="gecko", engines="gecko",
gecko_constant_prefix="NS_MATHML_DISPLAYSTYLE", gecko_pref="layout.css.math-style.enabled",
gecko_ffi_name="mMathDisplay", spec="https://mathml-refresh.github.io/mathml-core/#the-math-style-property",
enabled_in="ua", has_effect_on_gecko_scrollbars=False,
spec="Internal (not web-exposed)",
animation_value_type="none", animation_value_type="none",
enabled_in="ua",
needs_conversion=True,
)} )}
${helpers.single_keyword( ${helpers.single_keyword(
@ -307,7 +309,6 @@ ${helpers.predefined_type(
//! variable reference. We may want to improve this behavior at some //! variable reference. We may want to improve this behavior at some
//! point. See also https://github.com/w3c/csswg-drafts/issues/1586. //! point. See also https://github.com/w3c/csswg-drafts/issues/1586.
use app_units::Au;
use cssparser::{Parser, ToCss}; use cssparser::{Parser, ToCss};
use crate::values::computed::font::GenericFontFamily; use crate::values::computed::font::GenericFontFamily;
use crate::properties::longhands; use crate::properties::longhands;
@ -397,7 +398,7 @@ ${helpers.predefined_type(
is_system_font: true, is_system_font: true,
}, },
font_size: FontSize { font_size: FontSize {
size: NonNegative(cx.maybe_zoom_text(Au(system.size).into())), size: NonNegative(cx.maybe_zoom_text(system.size.0)),
keyword_info: KeywordInfo::none() keyword_info: KeywordInfo::none()
}, },
font_weight, font_weight,

View file

@ -31,6 +31,7 @@ ${helpers.single_keyword(
servo_2020_pref="layout.writing-mode.enabled", servo_2020_pref="layout.writing-mode.enabled",
animation_value_type="none", animation_value_type="none",
spec="https://drafts.csswg.org/css-writing-modes/#propdef-writing-mode", spec="https://drafts.csswg.org/css-writing-modes/#propdef-writing-mode",
gecko_enum_prefix="StyleWritingModeProperty",
servo_restyle_damage="rebuild_and_reflow", servo_restyle_damage="rebuild_and_reflow",
)} )}

View file

@ -72,7 +72,6 @@ ${helpers.predefined_type(
"FillRule", "FillRule",
"Default::default()", "Default::default()",
engines="gecko", engines="gecko",
needs_context=False,
animation_value_type="discrete", animation_value_type="discrete",
spec="https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty", spec="https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty",
)} )}
@ -165,7 +164,6 @@ ${helpers.predefined_type(
"FillRule", "FillRule",
"Default::default()", "Default::default()",
engines="gecko", engines="gecko",
needs_context=False,
animation_value_type="discrete", animation_value_type="discrete",
spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty", spec="https://www.w3.org/TR/SVG11/masking.html#ClipRuleProperty",
)} )}

View file

@ -26,12 +26,11 @@ ${helpers.single_keyword(
servo_restyle_damage="rebuild_and_reflow", servo_restyle_damage="rebuild_and_reflow",
)} )}
${helpers.single_keyword( ${helpers.predefined_type(
"caption-side", "caption-side",
"top bottom", "table::CaptionSide",
"computed::table::CaptionSide::Top",
engines="gecko servo-2013", engines="gecko servo-2013",
extra_gecko_values="right left top-outside bottom-outside",
needs_conversion="True",
animation_value_type="discrete", animation_value_type="discrete",
spec="https://drafts.csswg.org/css-tables/#propdef-caption-side", spec="https://drafts.csswg.org/css-tables/#propdef-caption-side",
servo_restyle_damage="rebuild_and_reflow", servo_restyle_damage="rebuild_and_reflow",
@ -46,5 +45,5 @@ ${helpers.predefined_type(
animation_value_type="BorderSpacing", animation_value_type="BorderSpacing",
boxed=True, boxed=True,
spec="https://drafts.csswg.org/css-tables/#propdef-border-spacing", spec="https://drafts.csswg.org/css-tables/#propdef-border-spacing",
servo_restyle_damage = "reflow", servo_restyle_damage="reflow",
)} )}

View file

@ -57,7 +57,7 @@ ${helpers.single_keyword(
gecko_ffi_name="mTextSizeAdjust", gecko_ffi_name="mTextSizeAdjust",
animation_value_type="discrete", animation_value_type="discrete",
spec="https://drafts.csswg.org/css-size-adjust/#adjustment-control", spec="https://drafts.csswg.org/css-size-adjust/#adjustment-control",
alias="-webkit-text-size-adjust", aliases="-webkit-text-size-adjust",
)} )}
${helpers.predefined_type( ${helpers.predefined_type(
@ -82,8 +82,7 @@ ${helpers.predefined_type(
servo_2020_pref="layout.2020.unimplemented", servo_2020_pref="layout.2020.unimplemented",
animation_value_type="discrete", animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text/#propdef-overflow-wrap", spec="https://drafts.csswg.org/css-text/#propdef-overflow-wrap",
alias="word-wrap", aliases="word-wrap",
needs_context=False,
servo_restyle_damage="rebuild_and_reflow", servo_restyle_damage="rebuild_and_reflow",
)} )}
@ -95,7 +94,6 @@ ${helpers.predefined_type(
servo_2020_pref="layout.2020.unimplemented", servo_2020_pref="layout.2020.unimplemented",
animation_value_type="discrete", animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text/#propdef-word-break", spec="https://drafts.csswg.org/css-text/#propdef-word-break",
needs_context=False,
servo_restyle_damage="rebuild_and_reflow", servo_restyle_damage="rebuild_and_reflow",
)} )}
@ -109,8 +107,6 @@ ${helpers.predefined_type(
extra_specified="${'distribute' if engine == 'gecko' else ''}" extra_specified="${'distribute' if engine == 'gecko' else ''}"
gecko_enum_prefix="StyleTextJustify" gecko_enum_prefix="StyleTextJustify"
animation_value_type="discrete" animation_value_type="discrete"
gecko_pref="layout.css.text-justify.enabled"
has_effect_on_gecko_scrollbars="False"
spec="https://drafts.csswg.org/css-text/#propdef-text-justify" spec="https://drafts.csswg.org/css-text/#propdef-text-justify"
servo_restyle_damage="rebuild_and_reflow" servo_restyle_damage="rebuild_and_reflow"
> >
@ -145,7 +141,6 @@ ${helpers.predefined_type(
"text-align-last", "text-align-last",
"TextAlignLast", "TextAlignLast",
"computed::text::TextAlignLast::Auto", "computed::text::TextAlignLast::Auto",
needs_context=False,
engines="gecko", engines="gecko",
animation_value_type="discrete", animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text/#propdef-text-align-last", spec="https://drafts.csswg.org/css-text/#propdef-text-align-last",
@ -288,7 +283,6 @@ ${helpers.predefined_type(
engines="gecko", engines="gecko",
animation_value_type="discrete", animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text-3/#line-break-property", spec="https://drafts.csswg.org/css-text-3/#line-break-property",
needs_context=False,
)} )}
// CSS Compatibility // CSS Compatibility
@ -352,7 +346,7 @@ ${helpers.single_keyword(
"text-combine-upright", "text-combine-upright",
"none all", "none all",
engines="gecko", engines="gecko",
animation_value_type="discrete", animation_value_type="none",
spec="https://drafts.csswg.org/css-writing-modes-3/#text-combine-upright", spec="https://drafts.csswg.org/css-writing-modes-3/#text-combine-upright",
)} )}
@ -385,8 +379,6 @@ ${helpers.predefined_type(
"computed::LengthPercentageOrAuto::auto()", "computed::LengthPercentageOrAuto::auto()",
engines="gecko", engines="gecko",
animation_value_type="ComputedValue", animation_value_type="ComputedValue",
gecko_pref="layout.css.text-underline-offset.enabled",
has_effect_on_gecko_scrollbars=False,
spec="https://drafts.csswg.org/css-text-decor-4/#underline-offset", spec="https://drafts.csswg.org/css-text-decor-4/#underline-offset",
)} )}
@ -397,8 +389,6 @@ ${helpers.predefined_type(
"computed::TextUnderlinePosition::AUTO", "computed::TextUnderlinePosition::AUTO",
engines="gecko", engines="gecko",
animation_value_type="discrete", animation_value_type="discrete",
gecko_pref="layout.css.text-underline-position.enabled",
has_effect_on_gecko_scrollbars=False,
spec="https://drafts.csswg.org/css-text-decor-3/#text-underline-position-property", spec="https://drafts.csswg.org/css-text-decor-3/#text-underline-position-property",
)} )}
@ -408,9 +398,6 @@ ${helpers.predefined_type(
"TextDecorationSkipInk", "TextDecorationSkipInk",
"computed::TextDecorationSkipInk::Auto", "computed::TextDecorationSkipInk::Auto",
engines="gecko", engines="gecko",
needs_context=False,
animation_value_type="discrete", animation_value_type="discrete",
gecko_pref="layout.css.text-decoration-skip-ink.enabled",
has_effect_on_gecko_scrollbars=False,
spec="https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-ink-property", spec="https://drafts.csswg.org/css-text-decor-4/#text-decoration-skip-ink-property",
)} )}

View file

@ -29,6 +29,17 @@ ${helpers.single_keyword(
gecko_enum_prefix="StylePointerEvents", gecko_enum_prefix="StylePointerEvents",
)} )}
${helpers.single_keyword(
"-moz-inert",
"none inert",
engines="gecko",
gecko_ffi_name="mInert",
gecko_enum_prefix="StyleInert",
animation_value_type="discrete",
enabled_in="ua",
spec="Nonstandard (https://html.spec.whatwg.org/multipage/#inert-subtrees)",
)}
${helpers.single_keyword( ${helpers.single_keyword(
"-moz-user-input", "-moz-user-input",
"auto none", "auto none",

View file

@ -54,12 +54,13 @@ ${helpers.single_keyword(
${helpers.predefined_type( ${helpers.predefined_type(
"list-style-image", "list-style-image",
"url::ImageUrlOrNone", "Image",
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
initial_value="computed::url::ImageUrlOrNone::none()", initial_value="computed::Image::None",
initial_specified_value="specified::url::ImageUrlOrNone::none()", initial_specified_value="specified::Image::None",
animation_value_type="discrete", animation_value_type="discrete",
spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image", spec="https://drafts.csswg.org/css-lists/#propdef-list-style-image",
boxed=engine == "servo-2013",
servo_restyle_damage="rebuild_and_reflow", servo_restyle_damage="rebuild_and_reflow",
)} )}
@ -91,7 +92,6 @@ ${helpers.predefined_type(
engines="gecko", engines="gecko",
animation_value_type="discrete", animation_value_type="discrete",
enabled_in="ua", enabled_in="ua",
needs_context=False,
spec="Internal implementation detail for <ol reversed>", spec="Internal implementation detail for <ol reversed>",
servo_restyle_damage="rebuild_and_reflow", servo_restyle_damage="rebuild_and_reflow",
)} )}

View file

@ -3,7 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
<%namespace name="helpers" file="/helpers.mako.rs" /> <%namespace name="helpers" file="/helpers.mako.rs" />
<% from data import ALL_SIDES, maybe_moz_logical_alias %> <% from data import ALL_SIDES, DEFAULT_RULES_AND_PAGE, maybe_moz_logical_alias %>
<% data.new_style_struct("Margin", inherited=False) %> <% data.new_style_struct("Margin", inherited=False) %>
% for side in ALL_SIDES: % for side in ALL_SIDES:
@ -17,13 +17,13 @@
"LengthPercentageOrAuto", "LengthPercentageOrAuto",
"computed::LengthPercentageOrAuto::zero()", "computed::LengthPercentageOrAuto::zero()",
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
alias=maybe_moz_logical_alias(engine, side, "-moz-margin-%s"), aliases=maybe_moz_logical_alias(engine, side, "-moz-margin-%s"),
allow_quirks="No" if side[1] else "Yes", allow_quirks="No" if side[1] else "Yes",
animation_value_type="ComputedValue", animation_value_type="ComputedValue",
logical=side[1], logical=side[1],
logical_group="margin", logical_group="margin",
spec=spec, spec=spec,
allowed_in_page_rule=True, rule_types_allowed=DEFAULT_RULES_AND_PAGE,
servo_restyle_damage="reflow" servo_restyle_damage="reflow"
)} )}
% endfor % endfor

View file

@ -17,7 +17,7 @@
"NonNegativeLengthPercentage", "NonNegativeLengthPercentage",
"computed::NonNegativeLengthPercentage::zero()", "computed::NonNegativeLengthPercentage::zero()",
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
alias=maybe_moz_logical_alias(engine, side, "-moz-padding-%s"), aliases=maybe_moz_logical_alias(engine, side, "-moz-padding-%s"),
animation_value_type="NonNegativeLengthPercentage", animation_value_type="NonNegativeLengthPercentage",
logical=side[1], logical=side[1],
logical_group="padding", logical_group="padding",

View file

@ -30,7 +30,6 @@
"computed::LengthPercentageOrAuto::auto()", "computed::LengthPercentageOrAuto::auto()",
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
spec="https://drafts.csswg.org/css-logical-props/#propdef-inset-%s" % side, spec="https://drafts.csswg.org/css-logical-props/#propdef-inset-%s" % side,
alias="offset-%s:layout.css.offset-logical-properties.enabled" % side,
animation_value_type="ComputedValue", animation_value_type="ComputedValue",
logical=True, logical=True,
logical_group="inset", logical_group="inset",
@ -433,7 +432,7 @@ ${helpers.predefined_type(
"length::NonNegativeLengthPercentageOrNormal", "length::NonNegativeLengthPercentageOrNormal",
"computed::length::NonNegativeLengthPercentageOrNormal::normal()", "computed::length::NonNegativeLengthPercentageOrNormal::normal()",
engines="gecko servo-2013", engines="gecko servo-2013",
alias="grid-column-gap" if engine == "gecko" else "", aliases="grid-column-gap" if engine == "gecko" else "",
extra_prefixes="moz:layout.css.prefixes.columns", extra_prefixes="moz:layout.css.prefixes.columns",
servo_2013_pref="layout.columns.enabled", servo_2013_pref="layout.columns.enabled",
spec="https://drafts.csswg.org/css-align-3/#propdef-column-gap", spec="https://drafts.csswg.org/css-align-3/#propdef-column-gap",
@ -447,7 +446,7 @@ ${helpers.predefined_type(
"length::NonNegativeLengthPercentageOrNormal", "length::NonNegativeLengthPercentageOrNormal",
"computed::length::NonNegativeLengthPercentageOrNormal::normal()", "computed::length::NonNegativeLengthPercentageOrNormal::normal()",
engines="gecko", engines="gecko",
alias="grid-row-gap", aliases="grid-row-gap",
spec="https://drafts.csswg.org/css-align-3/#propdef-row-gap", spec="https://drafts.csswg.org/css-align-3/#propdef-row-gap",
animation_value_type="NonNegativeLengthPercentageOrNormal", animation_value_type="NonNegativeLengthPercentageOrNormal",
servo_restyle_damage="reflow", servo_restyle_damage="reflow",
@ -458,7 +457,7 @@ ${helpers.predefined_type(
"AspectRatio", "AspectRatio",
"computed::AspectRatio::auto()", "computed::AspectRatio::auto()",
engines="gecko servo-2013", engines="gecko servo-2013",
animation_value_type="discrete", animation_value_type="ComputedValue",
spec="https://drafts.csswg.org/css-sizing-4/#aspect-ratio", spec="https://drafts.csswg.org/css-sizing-4/#aspect-ratio",
gecko_pref="layout.css.aspect-ratio.enabled", gecko_pref="layout.css.aspect-ratio.enabled",
servo_restyle_damage="reflow", servo_restyle_damage="reflow",

View file

@ -75,6 +75,5 @@ ${helpers.predefined_type(
engines="gecko", engines="gecko",
initial_specified_value="generics::text::GenericTextDecorationLength::Auto", initial_specified_value="generics::text::GenericTextDecorationLength::Auto",
animation_value_type="ComputedValue", animation_value_type="ComputedValue",
gecko_pref="layout.css.text-decoration-thickness.enabled",
spec="https://drafts.csswg.org/css-text-decor-4/#text-decoration-width-property" spec="https://drafts.csswg.org/css-text-decor-4/#text-decoration-width-property"
)} )}

View file

@ -37,7 +37,6 @@ ${helpers.predefined_type(
engines="gecko", engines="gecko",
extra_prefixes="moz webkit", extra_prefixes="moz webkit",
animation_value_type="discrete", animation_value_type="discrete",
needs_context=False,
spec="https://drafts.csswg.org/css-ui-4/#propdef-user-select", spec="https://drafts.csswg.org/css-ui-4/#propdef-user-select",
)} )}

View file

@ -15,7 +15,7 @@ ${helpers.single_keyword(
gecko_ffi_name="mBoxAlign", gecko_ffi_name="mBoxAlign",
gecko_enum_prefix="StyleBoxAlign", gecko_enum_prefix="StyleBoxAlign",
animation_value_type="discrete", animation_value_type="discrete",
alias="-webkit-box-align", aliases="-webkit-box-align",
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-align)", spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-align)",
)} )}
@ -26,7 +26,7 @@ ${helpers.single_keyword(
gecko_ffi_name="mBoxDirection", gecko_ffi_name="mBoxDirection",
gecko_enum_prefix="StyleBoxDirection", gecko_enum_prefix="StyleBoxDirection",
animation_value_type="discrete", animation_value_type="discrete",
alias="-webkit-box-direction", aliases="-webkit-box-direction",
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-direction)", spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-direction)",
)} )}
@ -37,7 +37,7 @@ ${helpers.predefined_type(
engines="gecko", engines="gecko",
gecko_ffi_name="mBoxFlex", gecko_ffi_name="mBoxFlex",
animation_value_type="NonNegativeNumber", animation_value_type="NonNegativeNumber",
alias="-webkit-box-flex", aliases="-webkit-box-flex",
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-flex)", spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-flex)",
)} )}
@ -49,7 +49,7 @@ ${helpers.single_keyword(
gecko_aliases="inline-axis=horizontal block-axis=vertical", gecko_aliases="inline-axis=horizontal block-axis=vertical",
gecko_enum_prefix="StyleBoxOrient", gecko_enum_prefix="StyleBoxOrient",
animation_value_type="discrete", animation_value_type="discrete",
alias="-webkit-box-orient", aliases="-webkit-box-orient",
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-orient)", spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-orient)",
)} )}
@ -60,7 +60,7 @@ ${helpers.single_keyword(
gecko_ffi_name="mBoxPack", gecko_ffi_name="mBoxPack",
gecko_enum_prefix="StyleBoxPack", gecko_enum_prefix="StyleBoxPack",
animation_value_type="discrete", animation_value_type="discrete",
alias="-webkit-box-pack", aliases="-webkit-box-pack",
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-pack)", spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/box-pack)",
)} )}
@ -72,7 +72,7 @@ ${helpers.predefined_type(
"1", "1",
engines="gecko", engines="gecko",
parse_method="parse_non_negative", parse_method="parse_non_negative",
alias="-webkit-box-ordinal-group", aliases="-webkit-box-ordinal-group",
gecko_ffi_name="mBoxOrdinal", gecko_ffi_name="mBoxOrdinal",
animation_value_type="discrete", animation_value_type="discrete",
spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-box-ordinal-group)", spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-box-ordinal-group)",

View file

@ -29,11 +29,11 @@ use crate::context::QuirksMode;
use crate::logical_geometry::WritingMode; use crate::logical_geometry::WritingMode;
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use crate::computed_value_flags::*; use crate::computed_value_flags::*;
use crate::hash::FxHashMap;
use crate::media_queries::Device; use crate::media_queries::Device;
use crate::parser::ParserContext; use crate::parser::ParserContext;
use crate::properties::longhands::system_font::SystemFont; use crate::properties::longhands::system_font::SystemFont;
use crate::selector_parser::PseudoElement; use crate::selector_parser::PseudoElement;
use selectors::parser::SelectorParseErrorKind;
#[cfg(feature = "servo")] use servo_config::prefs; #[cfg(feature = "servo")] use servo_config::prefs;
use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode}; use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode};
use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss}; use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
@ -46,7 +46,7 @@ use crate::values::computed::NonNegativeLength;
use crate::values::serialize_atom_name; use crate::values::serialize_atom_name;
use crate::rule_tree::StrongRuleNode; use crate::rule_tree::StrongRuleNode;
use crate::Zero; use crate::Zero;
use crate::str::{CssString, CssStringBorrow, CssStringWriter}; use crate::str::{CssString, CssStringWriter};
use std::cell::Cell; use std::cell::Cell;
pub use self::declaration_block::*; pub use self::declaration_block::*;
@ -54,7 +54,8 @@ pub use self::cascade::*;
<%! <%!
from collections import defaultdict from collections import defaultdict
from data import Method, PropertyRestrictions, Keyword, to_rust_ident, to_camel_case, SYSTEM_FONT_LONGHANDS from data import Method, PropertyRestrictions, Keyword, to_rust_ident, \
to_camel_case, RULE_VALUES, SYSTEM_FONT_LONGHANDS
import os.path import os.path
%> %>
@ -542,31 +543,33 @@ impl NonCustomPropertyId {
false false
} }
fn allowed_in(self, context: &ParserContext) -> bool { /// Returns whether a given rule allows a given property.
#[inline]
pub fn allowed_in_rule(self, rule_type: CssRuleType) -> bool {
debug_assert!( debug_assert!(
matches!( matches!(
context.rule_type(), rule_type,
CssRuleType::Keyframe | CssRuleType::Page | CssRuleType::Style CssRuleType::Keyframe | CssRuleType::Page | CssRuleType::Style
), ),
"Declarations are only expected inside a keyframe, page, or style rule." "Declarations are only expected inside a keyframe, page, or style rule."
); );
${static_non_custom_property_id_set( static MAP: [u8; NON_CUSTOM_PROPERTY_ID_COUNT] = [
"DISALLOWED_IN_KEYFRAME_BLOCK", % for property in data.longhands + data.shorthands + data.all_aliases():
lambda p: not p.allowed_in_keyframe_block ${property.rule_types_allowed},
)} % endfor
${static_non_custom_property_id_set( ];
"DISALLOWED_IN_PAGE_RULE", match rule_type {
lambda p: not p.allowed_in_page_rule % for name in RULE_VALUES:
)} CssRuleType::${name} => MAP[self.0] & ${RULE_VALUES[name]} != 0,
match context.rule_type() { % endfor
CssRuleType::Keyframe if DISALLOWED_IN_KEYFRAME_BLOCK.contains(self) => { _ => true
return false; }
} }
CssRuleType::Page if DISALLOWED_IN_PAGE_RULE.contains(self) => {
return false; fn allowed_in(self, context: &ParserContext) -> bool {
} if !self.allowed_in_rule(context.rule_type()) {
_ => {} return false;
} }
self.allowed_in_ignoring_rule_type(context) self.allowed_in_ignoring_rule_type(context)
@ -788,14 +791,44 @@ static ${name}: LonghandIdSet = LonghandIdSet {
/// A group for properties which may override each other /// A group for properties which may override each other
/// via logical resolution. /// via logical resolution.
#[derive(Clone, Copy, Eq, Hash, PartialEq)] #[derive(Clone, Copy, Eq, Hash, PartialEq)]
#[repr(u8)]
pub enum LogicalGroup { pub enum LogicalGroup {
% for group in sorted(logical_groups.keys()): % for i, group in enumerate(logical_groups.keys()):
/// ${group} /// ${group}
${to_camel_case(group)}, ${to_camel_case(group)} = ${i},
% endfor % endfor
} }
/// A set of logical groups.
#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
pub struct LogicalGroupSet {
storage: [u32; (${len(logical_groups)} - 1 + 32) / 32]
}
impl LogicalGroupSet {
/// Creates an empty `NonCustomPropertyIdSet`.
pub fn new() -> Self {
Self {
storage: Default::default(),
}
}
/// Return whether the given group is in the set
#[inline]
pub fn contains(&self, g: LogicalGroup) -> bool {
let bit = g as usize;
(self.storage[bit / 32] & (1 << (bit % 32))) != 0
}
/// Insert a group the set.
#[inline]
pub fn insert(&mut self, g: LogicalGroup) {
let bit = g as usize;
self.storage[bit / 32] |= 1 << (bit % 32);
}
}
/// A set of longhand properties /// A set of longhand properties
#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)] #[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
pub struct LonghandIdSet { pub struct LonghandIdSet {
@ -1162,20 +1195,21 @@ impl LonghandId {
} }
} }
// TODO(emilio): Should we use a function table like CASCADE_PROPERTY does
// to avoid blowing up code-size here?
fn parse_value<'i, 't>( fn parse_value<'i, 't>(
&self, &self,
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<PropertyDeclaration, ParseError<'i>> { ) -> Result<PropertyDeclaration, ParseError<'i>> {
match *self { type ParsePropertyFn = for<'i, 't> fn(
% for property in data.longhands: context: &ParserContext,
LonghandId::${property.camel_case} => { input: &mut Parser<'i, 't>,
longhands::${property.ident}::parse_declared(context, input) ) -> Result<PropertyDeclaration, ParseError<'i>>;
} static PARSE_PROPERTY: [ParsePropertyFn; ${len(data.longhands)}] = [
% endfor % for property in data.longhands:
} longhands::${property.ident}::parse_declared,
% endfor
];
(PARSE_PROPERTY[*self as usize])(context, input)
} }
/// Returns whether this property is animatable. /// Returns whether this property is animatable.
@ -1338,8 +1372,8 @@ impl LonghandId {
// preferences properly, see bug 1165538. // preferences properly, see bug 1165538.
LonghandId::MozMinFontSizeRatio | LonghandId::MozMinFontSizeRatio |
// Needed to do font-size for MathML. :( // font-size depends on math-depth's computed value.
LonghandId::MozScriptLevel | LonghandId::MathDepth |
% endif % endif
// Needed to compute the first available font, in order to // Needed to compute the first available font, in order to
@ -1495,10 +1529,7 @@ impl ShorthandId {
// https://drafts.csswg.org/css-variables/#variables-in-shorthands // https://drafts.csswg.org/css-variables/#variables-in-shorthands
if let Some(css) = first_declaration.with_variables_from_shorthand(self) { if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
if declarations2.all(|d| d.with_variables_from_shorthand(self) == Some(css)) { if declarations2.all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
return Some(AppendableValue::Css { return Some(AppendableValue::Css { css, with_variables: true });
css: CssStringBorrow::from(css),
with_variables: true,
});
} }
return None; return None;
} }
@ -1507,7 +1538,7 @@ impl ShorthandId {
if let Some(keyword) = first_declaration.get_css_wide_keyword() { if let Some(keyword) = first_declaration.get_css_wide_keyword() {
if declarations2.all(|d| d.get_css_wide_keyword() == Some(keyword)) { if declarations2.all(|d| d.get_css_wide_keyword() == Some(keyword)) {
return Some(AppendableValue::Css { return Some(AppendableValue::Css {
css: CssStringBorrow::from(keyword.to_str()), css: keyword.to_str(),
with_variables: false, with_variables: false,
}); });
} }
@ -1567,15 +1598,36 @@ impl ShorthandId {
context: &ParserContext, context: &ParserContext,
input: &mut Parser<'i, 't>, input: &mut Parser<'i, 't>,
) -> Result<(), ParseError<'i>> { ) -> Result<(), ParseError<'i>> {
match *self { type ParseIntoFn = for<'i, 't> fn(
% for shorthand in data.shorthands_except_all(): declarations: &mut SourcePropertyDeclaration,
ShorthandId::${shorthand.camel_case} => { context: &ParserContext,
shorthands::${shorthand.ident}::parse_into(declarations, context, input) input: &mut Parser<'i, 't>,
} ) -> Result<(), ParseError<'i>>;
% endfor
// 'all' accepts no value other than CSS-wide keywords fn unreachable<'i, 't>(
ShorthandId::All => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) _: &mut SourcePropertyDeclaration,
_: &ParserContext,
_: &mut Parser<'i, 't>
) -> Result<(), ParseError<'i>> {
unreachable!()
} }
// 'all' accepts no value other than CSS-wide keywords
if *self == ShorthandId::All {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
}
static PARSE_INTO: [ParseIntoFn; ${len(data.shorthands)}] = [
% for shorthand in data.shorthands:
% if shorthand.ident == "all":
unreachable,
% else:
shorthands::${shorthand.ident}::parse_into,
% endif
% endfor
];
(PARSE_INTO[*self as usize])(declarations, context, input)
} }
} }
@ -1605,23 +1657,45 @@ impl ToCss for UnparsedValue {
} }
} }
/// A simple cache for properties that come from a shorthand and have variable
/// references.
///
/// This cache works because of the fact that you can't have competing values
/// for a given longhand coming from the same shorthand (but note that this is
/// why the shorthand needs to be part of the cache key).
pub type ShorthandsWithPropertyReferencesCache =
FxHashMap<(ShorthandId, LonghandId), PropertyDeclaration>;
impl UnparsedValue { impl UnparsedValue {
fn substitute_variables( fn substitute_variables<'cache>(
&self, &self,
longhand_id: LonghandId, longhand_id: LonghandId,
writing_mode: WritingMode,
custom_properties: Option<<&Arc<crate::custom_properties::CustomPropertiesMap>>, custom_properties: Option<<&Arc<crate::custom_properties::CustomPropertiesMap>>,
quirks_mode: QuirksMode, quirks_mode: QuirksMode,
device: &Device, device: &Device,
) -> PropertyDeclaration { shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
) -> Cow<'cache, PropertyDeclaration> {
let invalid_at_computed_value_time = || { let invalid_at_computed_value_time = || {
let keyword = if longhand_id.inherited() { let keyword = if longhand_id.inherited() {
CSSWideKeyword::Inherit CSSWideKeyword::Inherit
} else { } else {
CSSWideKeyword::Initial CSSWideKeyword::Initial
}; };
PropertyDeclaration::css_wide_keyword(longhand_id, keyword) Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword))
}; };
if let Some(shorthand_id) = self.from_shorthand {
let key = (shorthand_id, longhand_id);
if shorthand_cache.contains_key(&key) {
// FIXME: This double lookup should be avoidable, but rustc
// doesn't like that, see:
//
// https://github.com/rust-lang/rust/issues/82146
return Cow::Borrowed(&shorthand_cache[&key]);
}
}
let css = match crate::custom_properties::substitute( let css = match crate::custom_properties::substitute(
&self.css, &self.css,
self.first_token_type, self.first_token_type,
@ -1656,53 +1730,34 @@ impl UnparsedValue {
let mut input = Parser::new(&mut input); let mut input = Parser::new(&mut input);
input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less. input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) { if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) {
return PropertyDeclaration::css_wide_keyword(longhand_id, keyword); return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword));
} }
let declaration = input.parse_entirely(|input| { let shorthand = match self.from_shorthand {
match self.from_shorthand { None => {
None => longhand_id.parse_value(&context, input), return match input.parse_entirely(|input| longhand_id.parse_value(&context, input)) {
Some(ShorthandId::All) => { Ok(decl) => Cow::Owned(decl),
// No need to parse the 'all' shorthand as anything other Err(..) => invalid_at_computed_value_time(),
// than a CSS-wide keyword, after variable substitution.
Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent("all".into())))
} }
% for shorthand in data.shorthands_except_all(): },
Some(ShorthandId::${shorthand.camel_case}) => { Some(shorthand) => shorthand,
shorthands::${shorthand.ident}::parse_value(&context, input) };
.map(|longhands| {
match longhand_id { let mut decls = SourcePropertyDeclaration::new();
<% seen = set() %> // parse_into takes care of doing `parse_entirely` for us.
% for property in shorthand.sub_properties: if shorthand.parse_into(&mut decls, &context, &mut input).is_err() {
// When animating logical properties, we end up return invalid_at_computed_value_time();
// physicalizing the value during the animation, but }
// the value still comes from the logical shorthand.
// for declaration in decls.declarations.drain(..) {
// So we need to handle the physical properties too. let longhand = declaration.id().as_longhand().unwrap();
% for prop in [property] + property.all_physical_mapped_properties(data): if longhand.is_logical() {
% if prop.camel_case not in seen: shorthand_cache.insert((shorthand, longhand.to_physical(writing_mode)), declaration.clone());
LonghandId::${prop.camel_case} => {
PropertyDeclaration::${prop.camel_case}(
longhands.${property.ident}
)
}
<% seen.add(prop.camel_case) %>
% endif
% endfor
% endfor
<% del seen %>
_ => unreachable!()
}
})
}
% endfor
} }
}); shorthand_cache.insert((shorthand, longhand), declaration);
match declaration {
Ok(decl) => decl,
Err(..) => invalid_at_computed_value_time(),
} }
Cow::Borrowed(&shorthand_cache[&(shorthand, longhand_id)])
} }
} }
@ -1897,7 +1952,7 @@ impl PropertyId {
% for (kind, properties) in [("Longhand", data.longhands), ("Shorthand", data.shorthands)]: % for (kind, properties) in [("Longhand", data.longhands), ("Shorthand", data.shorthands)]:
% for property in properties: % for property in properties:
"${property.name}" => StaticId::${kind}(${kind}Id::${property.camel_case}), "${property.name}" => StaticId::${kind}(${kind}Id::${property.camel_case}),
% for alias in property.alias: % for alias in property.aliases:
"${alias.name}" => { "${alias.name}" => {
StaticId::${kind}Alias( StaticId::${kind}Alias(
${kind}Id::${property.camel_case}, ${kind}Id::${property.camel_case},
@ -2413,7 +2468,7 @@ impl PropertyDeclaration {
id, id,
value: Arc::new(UnparsedValue { value: Arc::new(UnparsedValue {
css: css.into_owned(), css: css.into_owned(),
first_token_type: first_token_type, first_token_type,
url_data: context.url_data.clone(), url_data: context.url_data.clone(),
from_shorthand: None, from_shorthand: None,
}), }),
@ -2450,7 +2505,7 @@ impl PropertyDeclaration {
crate::custom_properties::parse_non_custom_with_var(input)?; crate::custom_properties::parse_non_custom_with_var(input)?;
let unparsed = Arc::new(UnparsedValue { let unparsed = Arc::new(UnparsedValue {
css: css.into_owned(), css: css.into_owned(),
first_token_type: first_token_type, first_token_type,
url_data: context.url_data.clone(), url_data: context.url_data.clone(),
from_shorthand: Some(id), from_shorthand: Some(id),
}); });
@ -2962,7 +3017,7 @@ impl ComputedValues {
/// Returns the visited style, if any. /// Returns the visited style, if any.
pub fn visited_style(&self) -> Option<<&ComputedValues> { pub fn visited_style(&self) -> Option<<&ComputedValues> {
self.visited_style.as_ref().map(|s| &**s) self.visited_style.as_deref()
} }
/// Returns the visited rules, if applicable. /// Returns the visited rules, if applicable.
@ -2975,6 +3030,27 @@ impl ComputedValues {
self.custom_properties.as_ref() self.custom_properties.as_ref()
} }
/// Returns whether we have the same custom properties as another style.
///
/// This should effectively be just:
///
/// self.custom_properties() == other.custom_properties()
///
/// But that's not really the case because IndexMap equality doesn't
/// consider ordering, which we have to account for. Also, for the same
/// reason, IndexMap equality comparisons are slower than needed.
///
/// See https://github.com/bluss/indexmap/issues/153
pub fn custom_properties_equal(&self, other: &Self) -> bool {
match (self.custom_properties(), other.custom_properties()) {
(Some(l), Some(r)) => {
l.len() == r.len() && l.iter().zip(r.iter()).all(|((k1, v1), (k2, v2))| k1 == k2 && v1 == v2)
},
(None, None) => true,
_ => false,
}
}
% for prop in data.longhands: % for prop in data.longhands:
/// Gets the computed value of a given property. /// Gets the computed value of a given property.
#[inline(always)] #[inline(always)]
@ -3621,11 +3697,11 @@ impl<'a> StyleBuilder<'a> {
self.add_flags(ComputedValueFlags::INHERITS_RESET_STYLE); self.add_flags(ComputedValueFlags::INHERITS_RESET_STYLE);
% if property.ident == "content": % if property.ident == "content":
self.add_flags(ComputedValueFlags::INHERITS_CONTENT); self.add_flags(ComputedValueFlags::CONTENT_DEPENDS_ON_INHERITED_STYLE);
% endif % endif
% if property.ident == "display": % if property.ident == "display":
self.add_flags(ComputedValueFlags::INHERITS_DISPLAY); self.add_flags(ComputedValueFlags::DISPLAY_DEPENDS_ON_INHERITED_STYLE);
% endif % endif
if self.${property.style_struct.ident}.ptr_eq(inherited_struct) { if self.${property.style_struct.ident}.ptr_eq(inherited_struct) {
@ -4017,7 +4093,7 @@ macro_rules! css_properties_accessors {
% for kind, props in [("Longhand", data.longhands), ("Shorthand", data.shorthands)]: % for kind, props in [("Longhand", data.longhands), ("Shorthand", data.shorthands)]:
% for property in props: % for property in props:
% if property.enabled_in_content(): % if property.enabled_in_content():
% for prop in [property] + property.alias: % for prop in [property] + property.aliases:
% if '-' in prop.name: % if '-' in prop.name:
[${prop.ident.capitalize()}, Set${prop.ident.capitalize()}, [${prop.ident.capitalize()}, Set${prop.ident.capitalize()},
PropertyId::${kind}(${kind}Id::${property.camel_case})], PropertyId::${kind}(${kind}Id::${property.camel_case})],

View file

@ -17,9 +17,7 @@ ${helpers.four_sides_shorthand(
${helpers.four_sides_shorthand( ${helpers.four_sides_shorthand(
"border-style", "border-style",
"border-%s-style", "border-%s-style",
"specified::BorderStyle::parse",
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
needs_context=False,
spec="https://drafts.csswg.org/css-backgrounds/#border-style", spec="https://drafts.csswg.org/css-backgrounds/#border-style",
)} )}
@ -114,7 +112,7 @@ pub fn parse_border<'i, 't>(
'border-%s-%s' % (side, prop) 'border-%s-%s' % (side, prop)
for prop in ['color', 'style', 'width'] for prop in ['color', 'style', 'width']
)}" )}"
alias="${maybe_moz_logical_alias(engine, (side, logical), '-moz-border-%s')}" aliases="${maybe_moz_logical_alias(engine, (side, logical), '-moz-border-%s')}"
spec="${spec}"> spec="${spec}">
pub fn parse_value<'i, 't>( pub fn parse_value<'i, 't>(

View file

@ -8,10 +8,8 @@ ${helpers.two_properties_shorthand(
"overflow", "overflow",
"overflow-x", "overflow-x",
"overflow-y", "overflow-y",
"specified::Overflow::parse",
engines="gecko servo-2013 servo-2020", engines="gecko servo-2013 servo-2020",
flags="SHORTHAND_IN_GETCS", flags="SHORTHAND_IN_GETCS",
needs_context=False,
spec="https://drafts.csswg.org/css-overflow/#propdef-overflow", spec="https://drafts.csswg.org/css-overflow/#propdef-overflow",
)} )}
@ -19,10 +17,8 @@ ${helpers.two_properties_shorthand(
"overflow-clip-box", "overflow-clip-box",
"overflow-clip-box-block", "overflow-clip-box-block",
"overflow-clip-box-inline", "overflow-clip-box-inline",
"specified::OverflowClipBox::parse",
engines="gecko", engines="gecko",
enabled_in="ua", enabled_in="ua",
needs_context=False,
gecko_pref="layout.css.overflow-clip-box.enabled", gecko_pref="layout.css.overflow-clip-box.enabled",
spec="Internal, may be standardized in the future " spec="Internal, may be standardized in the future "
"(https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box)", "(https://developer.mozilla.org/en-US/docs/Web/CSS/overflow-clip-box)",
@ -194,7 +190,7 @@ macro_rules! try_parse_one {
animation-timing-function animation-delay animation-timing-function animation-delay
animation-iteration-count animation-direction animation-iteration-count animation-direction
animation-fill-mode animation-play-state" animation-fill-mode animation-play-state"
allowed_in_keyframe_block="False" rule_types_allowed="Style"
spec="https://drafts.csswg.org/css-animations/#propdef-animation"> spec="https://drafts.csswg.org/css-animations/#propdef-animation">
<% <%
props = "name duration timing_function delay iteration_count \ props = "name duration timing_function delay iteration_count \
@ -310,9 +306,7 @@ ${helpers.two_properties_shorthand(
"overscroll-behavior", "overscroll-behavior",
"overscroll-behavior-x", "overscroll-behavior-x",
"overscroll-behavior-y", "overscroll-behavior-y",
"specified::OverscrollBehavior::parse",
engines="gecko", engines="gecko",
needs_context=False,
gecko_pref="layout.css.overscroll-behavior.enabled", gecko_pref="layout.css.overscroll-behavior.enabled",
spec="https://wicg.github.io/overscroll-behavior/#overscroll-behavior-properties", spec="https://wicg.github.io/overscroll-behavior/#overscroll-behavior-properties",
)} )}

Some files were not shown because too many files have changed in this diff Show more