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

View file

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

View file

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

View file

@ -154,19 +154,19 @@ pub fn fmap_trait_output(input: &DeriveInput, trait_path: &Path, trait_output: &
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
F: FnMut(&Ident) -> Type,
{
match *ty {
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()
}),
Type::Array(ref inner) => {
//ref ty, ref expr) => {
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()
})
},
@ -175,7 +175,7 @@ where
elems: inner
.elems
.iter()
.map(|ty| map_type_params(&ty, params, f))
.map(|ty| map_type_params(&ty, params, self_type, f))
.collect(),
..inner.clone()
}),
@ -187,10 +187,16 @@ where
if params.iter().any(|ref param| &param.ident == ident) {
return f(ident);
}
if ident == "Self" {
return Type::from(TypePath {
qself: None,
path: self_type.clone(),
});
}
}
Type::from(TypePath {
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 {
@ -198,25 +204,30 @@ where
ref path,
}) => Type::from(TypePath {
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,
..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 {
elem: Box::new(map_type_params(&inner.elem, params, f)),
elem: Box::new(map_type_params(&inner.elem, params, self_type, f)),
..inner.clone()
}),
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()
}),
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
F: FnMut(&Ident) -> Type,
{
@ -235,12 +246,12 @@ where
.iter()
.map(|arg| match arg {
ty @ &GenericArgument::Lifetime(_) => ty.clone(),
&GenericArgument::Type(ref data) => {
GenericArgument::Type(map_type_params(data, params, f))
},
&GenericArgument::Type(ref data) => GenericArgument::Type(
map_type_params(data, params, self_type, f),
),
&GenericArgument::Binding(ref data) => {
GenericArgument::Binding(Binding {
ty: map_type_params(&data.ty, params, f),
ty: map_type_params(&data.ty, params, self_type, f),
..data.clone()
})
},

View file

@ -71,8 +71,8 @@ use style::logical_geometry::Direction;
use style::properties::ComputedValues;
use style::selector_parser::{PseudoElement, RestyleDamage};
use style::servo::restyle_damage::ServoRestyleDamage;
use style::values::computed::Image;
use style::values::generics::counters::ContentItem;
use style::values::generics::url::UrlOrNone as ImageUrlOrNone;
/// The results of flow construction for a DOM node.
#[derive(Clone)]
@ -1506,9 +1506,9 @@ where
) -> ConstructionResult {
let flotation = FloatKind::from_property(flotation);
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(
url_value.url().map(|u| u.clone()),
url_value.url().cloned(),
None,
node,
&self.layout_context,
@ -1519,7 +1519,13 @@ where
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,
) {
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) => {
// This is a (boxed) empty enum on non-Gecko
match **rect {}

View file

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

View file

@ -16,7 +16,7 @@ doctest = false
app_units = "0.7"
atomic_refcell = "0.1.6"
canvas_traits = { path = "../canvas_traits" }
cssparser = "0.27"
cssparser = "0.28"
embedder_traits = { path = "../embedder_traits" }
euclid = "0.20"
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.
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 style::properties::longhands::list_style_type::computed_value::T as ListStyleType;
use style::properties::style_structs;
use style::values::computed::url::UrlOrNone;
use style::values::computed::Image;
/// https://drafts.csswg.org/css-lists/#content-property
pub(crate) fn make_marker<'dom, Node>(
@ -21,13 +21,18 @@ where
// https://drafts.csswg.org/css-lists/#marker-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(
info.node, context, url,
)?),
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(|| {
Some(vec![PseudoElementContentItem::Text(

View file

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

View file

@ -506,6 +506,7 @@ impl LayoutThread {
let device = Device::new(
MediaType::screen(),
QuirksMode::NoQuirks,
window_size.initial_viewport,
window_size.device_pixel_ratio,
);
@ -1267,7 +1268,12 @@ impl LayoutThread {
};
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);
self.stylist

View file

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

View file

@ -474,6 +474,7 @@ impl LayoutThread {
// but it will be set correctly when the initial reflow takes place.
let device = Device::new(
MediaType::screen(),
QuirksMode::NoQuirks,
window_size.initial_viewport,
window_size.device_pixel_ratio,
);
@ -952,7 +953,12 @@ impl LayoutThread {
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);
self.stylist

View file

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

View file

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

View file

@ -21,6 +21,7 @@ use servo_atoms::Atom;
use std::borrow::ToOwned;
use std::mem;
use style::attr::{AttrIdentifier, AttrValue};
use style::values::GenericAtomIdent;
// https://dom.spec.whatwg.org/#interface-attr
#[dom_struct]
@ -46,10 +47,10 @@ impl Attr {
Attr {
node_: Node::new_inherited(document),
identifier: AttrIdentifier {
local_name: local_name,
name: name,
namespace: namespace,
prefix: prefix,
local_name: GenericAtomIdent(local_name),
name: GenericAtomIdent(name),
namespace: GenericAtomIdent(namespace),
prefix: prefix.map(GenericAtomIdent),
},
value: DomRefCell::new(value),
owner: MutNullableDom::new(owner),
@ -75,17 +76,17 @@ impl Attr {
#[inline]
pub fn name(&self) -> &LocalName {
&self.identifier.name
&self.identifier.name.0
}
#[inline]
pub fn namespace(&self) -> &Namespace {
&self.identifier.namespace
&self.identifier.namespace.0
}
#[inline]
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
fn SetValue(&self, value: DOMString) {
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);
} else {
*self.value.borrow_mut() = AttrValue::String(value.into());
@ -115,12 +116,12 @@ impl AttrMethods for Attr {
// https://dom.spec.whatwg.org/#dom-attr-name
fn Name(&self) -> 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
fn GetNamespaceURI(&self) -> Option<DOMString> {
match self.identifier.namespace {
match *self.namespace() {
ns!() => None,
ref url => Some(DOMString::from(&**url)),
}
@ -170,7 +171,7 @@ impl Attr {
assert_eq!(Some(owner), self.owner().as_deref());
owner.will_mutate_attr(self);
self.swap_value(&mut value);
if self.identifier.namespace == ns!() {
if *self.namespace() == ns!() {
vtable_for(owner.upcast())
.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
/// or removed from its older parent.
pub fn set_owner(&self, owner: Option<&Element>) {
let ns = &self.identifier.namespace;
let ns = self.namespace();
match (self.owner(), owner) {
(Some(old), None) => {
// Already gone from the list of attributes of old owner.
@ -218,7 +219,7 @@ impl Attr {
pub fn summarize(&self) -> AttrInfo {
AttrInfo {
namespace: (*self.identifier.namespace).to_owned(),
namespace: (**self.namespace()).to_owned(),
name: String::from(self.Name()),
value: String::from(self.Value()),
}
@ -263,11 +264,11 @@ impl<'dom> AttrHelpersForLayout<'dom> for LayoutDom<'dom, Attr> {
#[inline]
fn local_name(self) -> &'dom LocalName {
unsafe { &self.unsafe_get().identifier.local_name }
unsafe { &self.unsafe_get().identifier.local_name.0 }
}
#[inline]
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::parser::ParserContext;
use style::stylesheets::supports_rule::{parse_condition_or_declaration, Declaration};
use style::stylesheets::CssRuleType;
use style::stylesheets::{CssRuleType, Origin};
use style_traits::ParsingMode;
#[dom_struct]
@ -39,7 +39,8 @@ impl CSS {
decl.push_str(&value);
let decl = Declaration(decl);
let url = win.Document().url();
let context = ParserContext::new_for_cssom(
let context = ParserContext::new(
Origin::Author,
&url,
Some(CssRuleType::Style),
ParsingMode::DEFAULT,
@ -60,7 +61,8 @@ impl CSS {
};
let url = win.Document().url();
let context = ParserContext::new_for_cssom(
let context = ParserContext::new(
Origin::Author,
&url,
Some(CssRuleType::Style),
ParsingMode::DEFAULT,

View file

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

View file

@ -63,7 +63,7 @@ impl CSSNamespaceRuleMethods for CSSNamespaceRule {
// https://drafts.csswg.org/cssom/#dom-cssnamespacerule-namespaceuri
fn NamespaceURI(&self) -> DOMString {
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::shared_lock::Locked;
use style::stylesheets::{CssRuleType, Origin};
use style_traits::ParsingMode;
// http://dev.w3.org/csswg/cssom/#the-cssstyledeclaration-interface
@ -302,10 +303,12 @@ impl CSSStyleDeclaration {
&mut declarations,
id,
&value,
Origin::Author,
&self.owner.base_url(),
window.css_error_reporter(),
ParsingMode::DEFAULT,
quirks_mode,
CssRuleType::Style,
);
// Step 6
@ -461,6 +464,7 @@ impl CSSStyleDeclarationMethods for CSSStyleDeclaration {
&self.owner.base_url(),
window.css_error_reporter(),
quirks_mode,
CssRuleType::Style,
);
});

View file

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

View file

@ -3454,7 +3454,12 @@ impl Document {
let window_size = self.window().window_size();
let viewport_size = window_size.initial_viewport;
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 {
@ -3551,8 +3556,9 @@ impl Document {
} else {
snapshot.other_attributes_changed = true;
}
if !snapshot.changed_attrs.contains(attr.local_name()) {
snapshot.changed_attrs.push(attr.local_name().clone());
let local_name = style::LocalName::cast(attr.local_name());
if !snapshot.changed_attrs.contains(local_name) {
snapshot.changed_attrs.push(local_name.clone());
}
if snapshot.attrs.is_none() {
let attrs = el

View file

@ -129,9 +129,10 @@ use style::selector_parser::{
NonTSPseudoClass, PseudoElement, RestyleDamage, SelectorImpl, SelectorParser,
};
use style::shared_lock::{Locked, SharedRwLock};
use style::stylesheets::CssRuleType;
use style::thread_state;
use style::values::generics::NonNegative;
use style::values::{computed, specified, CSSFloat};
use style::values::{computed, specified, AtomIdent, AtomString, CSSFloat};
use style::CaseSensitivityExt;
use xml5ever::serialize as xmlSerialize;
use xml5ever::serialize::SerializeOpts as XmlSerializeOpts;
@ -568,7 +569,7 @@ pub fn get_attr_for_layout<'dom>(
pub trait LayoutElementHelpers<'dom> {
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 synthesize_presentational_hints_for_legacy_attributes<V>(self, hints: &mut V)
@ -616,7 +617,7 @@ impl<'dom> LayoutElementHelpers<'dom> for LayoutDom<'dom, Element> {
}
#[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| {
attr.as_tokens()
.unwrap()
@ -2851,6 +2852,7 @@ impl VirtualMethods for Element {
&doc.base_url(),
win.css_error_reporter(),
doc.quirks_mode(),
CssRuleType::Style,
)))
};
@ -3135,16 +3137,16 @@ impl<'a> SelectorsElement for DomRoot<Element> {
fn attr_matches(
&self,
ns: &NamespaceConstraint<&Namespace>,
local_name: &LocalName,
operation: &AttrSelectorOperation<&String>,
ns: &NamespaceConstraint<&style::Namespace>,
local_name: &style::LocalName,
operation: &AttrSelectorOperation<&AtomString>,
) -> bool {
match *ns {
NamespaceConstraint::Specific(ref ns) => self
.get_attribute(ns, local_name)
.map_or(false, |attr| attr.value().eval_selector(operation)),
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
.borrow()
.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
}
fn imported_part(&self, _: &Atom) -> Option<Atom> {
fn imported_part(&self, _: &AtomIdent) -> Option<AtomIdent> {
None
}
fn has_class(&self, name: &Atom, case_sensitivity: CaseSensitivity) -> bool {
Element::has_class(&**self, name, case_sensitivity)
fn has_class(&self, name: &AtomIdent, case_sensitivity: CaseSensitivity) -> bool {
Element::has_class(&**self, &name, case_sensitivity)
}
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::parser::ParserContext as CssParserContext;
use style::str::HTML_SPACE_CHARACTERS;
use style::stylesheets::{CssRuleType, Stylesheet};
use style::stylesheets::{CssRuleType, Origin, Stylesheet};
use style_traits::ParsingMode;
#[derive(Clone, Copy, JSTraceable, MallocSizeOf, PartialEq)]
@ -310,7 +310,8 @@ impl HTMLLinkElement {
// FIXME(emilio): This looks somewhat fishy, since we use the context
// only to parse the media query list, CssRuleType::Media doesn't make
// much sense.
let context = CssParserContext::new_for_cssom(
let context = CssParserContext::new(
Origin::Author,
&doc_url,
Some(CssRuleType::Media),
ParsingMode::DEFAULT,

View file

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

View file

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

View file

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

View file

@ -19,7 +19,7 @@ bench = []
[dependencies]
bitflags = "1.0"
cssparser = "0.27"
cssparser = "0.28"
derive_more = "0.99"
fxhash = "0.2"
log = "0.4"
@ -27,7 +27,6 @@ phf = "0.8"
precomputed-hash = "0.1"
servo_arc = { version = "0.1", path = "../servo_arc" }
smallvec = "1.0"
thin-slice = "0.1.0"
to_shmem = { path = "../to_shmem" }
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.
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)]
pub enum ParsedCaseSensitivity {

View file

@ -27,7 +27,7 @@ fn main() {
}
/// <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-charset
align

View file

@ -17,7 +17,7 @@
//! is non-trivial. This module encapsulates those details and presents an
//! 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 servo_arc::{Arc, HeaderWithLength, ThinArc};
use smallvec::{self, SmallVec};
@ -322,15 +322,11 @@ where
Component::NthLastOfType(..) |
Component::FirstOfType |
Component::LastOfType |
Component::OnlyOfType => {
Component::OnlyOfType |
Component::NonTSPseudoClass(..) => {
specificity.class_like_selectors += 1;
},
Component::NonTSPseudoClass(ref pseudo) => {
if !pseudo.has_zero_specificity() {
specificity.class_like_selectors += 1;
}
},
Component::Is(ref list) => {
Component::Negation(ref list) | Component::Is(ref list) => {
// https://drafts.csswg.org/selectors/#specificity-rules:
//
// The specificity of an :is() pseudo-class is replaced by the
@ -350,11 +346,6 @@ where
Component::Namespace(..) => {
// 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
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;
let result = self.nest(f);
self.in_negation = false;
self.in_negation = old_in_negation;
result
}
@ -286,6 +286,6 @@ where
/// against.
#[inline]
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 servo_arc;
extern crate smallvec;
extern crate thin_slice;
extern crate to_shmem;
#[macro_use]
extern crate to_shmem_derive;

View file

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

View file

@ -37,7 +37,7 @@ pub struct NthIndexCacheInner(FxHashMap<OpaqueElement, i32>);
impl NthIndexCacheInner {
/// Does a lookup for a given element in the cache.
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.

View file

@ -10,18 +10,17 @@ use crate::builder::{SelectorBuilder, SelectorFlags, SpecificityAndFlags};
use crate::context::QuirksMode;
use crate::sink::Push;
pub use crate::visitor::SelectorVisitor;
use cssparser::{parse_nth, serialize_identifier};
use cssparser::parse_nth;
use cssparser::{BasicParseError, BasicParseErrorKind, ParseError, ParseErrorKind};
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 servo_arc::ThinArc;
use smallvec::SmallVec;
use std::borrow::{Borrow, Cow};
use std::fmt::{self, Debug, Display, Write};
use std::fmt::{self, Debug};
use std::iter::Rev;
use std::slice;
use thin_slice::ThinBoxedSlice;
/// A trait that represents a pseudo-element.
pub trait PseudoElement: Sized + ToCss {
@ -53,9 +52,6 @@ pub trait NonTSPseudoClass: Sized + ToCss {
/// https://drafts.csswg.org/selectors-4/#useraction-pseudos
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
where
V: SelectorVisitor<Impl = Self::Impl>,
@ -79,9 +75,10 @@ fn to_ascii_lowercase(s: &str) -> Cow<str> {
bitflags! {
/// Flags that indicate at which point of parsing a selector are we.
struct SelectorParsingState: u8 {
/// Whether we're inside a negation. If we're inside a negation, we're
/// not allowed to add another negation or such, for example.
const INSIDE_NEGATION = 1 << 0;
/// Whether we should avoid adding default namespaces to selectors that
/// aren't type or universal selectors.
const SKIP_DEFAULT_NAMESPACE = 1 << 0;
/// Whether we've parsed a ::slotted() pseudo-element already.
///
/// If so, then we can only parse a subset of pseudo-elements, and
@ -165,7 +162,6 @@ pub enum SelectorParseErrorKind<'i> {
NoQualifiedNameInAttributeSelector(Token<'i>),
EmptySelector,
DanglingCombinator,
NonSimpleSelectorInNegation,
NonCompoundSelector,
NonPseudoElementAfterSlotted,
InvalidPseudoElementAfterSlotted,
@ -183,7 +179,6 @@ pub enum SelectorParseErrorKind<'i> {
InvalidQualNameInAttr(Token<'i>),
ExplicitNamespaceUnexpectedToken(Token<'i>),
ClassNeedsIdent(Token<'i>),
EmptyNegation,
}
macro_rules! with_all_bounds {
@ -202,8 +197,6 @@ macro_rules! with_all_bounds {
type ExtraMatchingData: Sized + Default + 'static;
type AttrValue: $($InSelector)*;
type Identifier: $($InSelector)*;
type ClassName: $($InSelector)*;
type PartName: $($InSelector)*;
type LocalName: $($InSelector)* + Borrow<Self::BorrowedLocalName>;
type NamespaceUrl: $($CommonBounds)* + Default + Borrow<Self::BorrowedNamespaceUrl>;
type NamespacePrefix: $($InSelector)* + Default;
@ -223,7 +216,7 @@ macro_rules! with_all_bounds {
macro_rules! with_bounds {
( [ $( $CommonBounds: tt )* ] [ $( $FromStr: tt )* ]) => {
with_all_bounds! {
[$($CommonBounds)* + $($FromStr)* + Display]
[$($CommonBounds)* + $($FromStr)* + ToCss]
[$($CommonBounds)*]
[$($FromStr)*]
}
@ -254,6 +247,16 @@ pub trait Parser<'i> {
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.
fn parse_host(&self) -> bool {
false
@ -327,6 +330,17 @@ pub struct SelectorList<Impl: SelectorImpl>(
#[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> {
/// Parse a comma-separated list of Selectors.
/// <https://drafts.csswg.org/selectors/#grouping>
@ -339,26 +353,47 @@ impl<Impl: SelectorImpl> SelectorList<Impl> {
where
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>(
parser: &P,
input: &mut CssParser<'i, 't>,
state: SelectorParsingState,
recovery: ParseErrorRecovery,
) -> Result<Self, ParseError<'i, P::Error>>
where
P: Parser<'i, Impl = Impl>,
{
let mut values = SmallVec::new();
loop {
values.push(input.parse_until_before(Delimiter::Comma, |input| {
let selector = input.parse_until_before(Delimiter::Comma, |input| {
parse_selector(parser, input, state)
})?);
match input.next() {
Err(_) => return Ok(SelectorList(values)),
Ok(&Token::Comma) => continue,
Ok(_) => unreachable!(),
});
let was_ok = selector.is_ok();
match selector {
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
/// inline with selectors to optimize cache performance during matching.
/// This matters a lot.
@ -430,7 +449,6 @@ fn collect_ancestor_hashes<Impl: SelectorImpl>(
) -> bool
where
Impl::Identifier: PrecomputedHash,
Impl::ClassName: PrecomputedHash,
Impl::LocalName: PrecomputedHash,
Impl::NamespaceUrl: PrecomputedHash,
{
@ -461,10 +479,10 @@ where
// :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
// exclude elements that may match one of the other selectors.
if list.len() == 1 {
if !collect_ancestor_hashes(list[0].iter(), quirks_mode, hashes, len) {
return false;
}
if list.len() == 1 &&
!collect_ancestor_hashes(list[0].iter(), quirks_mode, hashes, len)
{
return false;
}
continue;
},
@ -484,7 +502,6 @@ impl AncestorHashes {
pub fn new<Impl: SelectorImpl>(selector: &Selector<Impl>, quirks_mode: QuirksMode) -> Self
where
Impl::Identifier: PrecomputedHash,
Impl::ClassName: PrecomputedHash,
Impl::LocalName: PrecomputedHash,
Impl::NamespaceUrl: PrecomputedHash,
{
@ -563,7 +580,7 @@ impl<Impl: SelectorImpl> Selector<Impl> {
}
#[inline]
pub fn parts(&self) -> Option<&[Impl::PartName]> {
pub fn parts(&self) -> Option<&[Impl::Identifier]> {
if !self.is_part() {
return None;
}
@ -659,7 +676,7 @@ impl<Impl: SelectorImpl> Selector<Impl> {
pub fn iter_from(&self, offset: usize) -> SelectorIter<Impl> {
let iter = self.0.slice[offset..].iter();
SelectorIter {
iter: iter,
iter,
next_combinator: None,
}
}
@ -982,7 +999,7 @@ pub enum Component<Impl: SelectorImpl> {
LocalName(LocalName<Impl>),
ID(#[shmem(field_bound)] Impl::Identifier),
Class(#[shmem(field_bound)] Impl::ClassName),
Class(#[shmem(field_bound)] Impl::Identifier),
AttributeInNoNamespaceExists {
#[shmem(field_bound)]
@ -1002,16 +1019,7 @@ pub enum Component<Impl: SelectorImpl> {
AttributeOther(Box<AttrSelectorWithOptionalNamespace<Impl>>),
/// Pseudo-classes
///
/// 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>>),
Negation(Box<[Selector<Impl>]>),
FirstChild,
LastChild,
OnlyChild,
@ -1040,7 +1048,7 @@ pub enum Component<Impl: SelectorImpl> {
Slotted(Selector<Impl>),
/// The `::part` pseudo-element.
/// 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:
///
/// 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 {
match *self {
Component::NonTSPseudoClass(..) => true,
Component::Negation(ref components) => components
.iter()
.all(|c| c.maybe_allowed_after_pseudo_element()),
Component::Is(ref selectors) | Component::Where(ref selectors) => {
selectors.iter().all(|selector| {
selector
.iter_raw_match_order()
.all(|c| c.maybe_allowed_after_pseudo_element())
})
},
Component::Negation(ref selectors) |
Component::Is(ref selectors) |
Component::Where(ref selectors) => selectors.iter().all(|selector| {
selector
.iter_raw_match_order()
.all(|c| c.maybe_allowed_after_pseudo_element())
}),
_ => false,
}
}
@ -1114,9 +1119,11 @@ impl<Impl: SelectorImpl> Component<Impl> {
*self
);
match *self {
Component::Negation(ref components) => !components
.iter()
.all(|c| c.matches_for_stateless_pseudo_element()),
Component::Negation(ref selectors) => !selectors.iter().all(|selector| {
selector
.iter_raw_match_order()
.all(|c| c.matches_for_stateless_pseudo_element())
}),
Component::Is(ref selectors) | Component::Where(ref selectors) => {
selectors.iter().any(|selector| {
selector
@ -1148,14 +1155,6 @@ impl<Impl: SelectorImpl> Component<Impl> {
return false;
}
},
Negation(ref negated) => {
for component in negated.iter() {
if !component.visit(visitor) {
return false;
}
}
},
AttributeInNoNamespaceExists {
ref local_name,
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) {
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
Impl: SelectorImpl,
I: Iterator<Item = &'a Selector<Impl>>,
W: fmt::Write,
{
let first = iter
.next()
.expect("Empty SelectorList, should contain at least one selector");
first.to_css(dest)?;
let mut first = true;
for selector in iter {
dest.write_str(", ")?;
if !first {
dest.write_str(", ")?;
}
first = false;
selector.to_css(dest)?;
}
Ok(())
@ -1454,18 +1453,18 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
if i != 0 {
dest.write_char(' ')?;
}
display_to_css_identifier(name, dest)?;
name.to_css(dest)?;
}
dest.write_char(')')
},
PseudoElement(ref p) => p.to_css(dest),
ID(ref s) => {
dest.write_char('#')?;
display_to_css_identifier(s, dest)
s.to_css(dest)
},
Class(ref s) => {
dest.write_char('.')?;
display_to_css_identifier(s, dest)
s.to_css(dest)
},
LocalName(ref s) => s.to_css(dest),
ExplicitUniversalType => dest.write_char('*'),
@ -1474,13 +1473,13 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
ExplicitNoNamespace => dest.write_char('|'),
ExplicitAnyNamespace => dest.write_str("*|"),
Namespace(ref prefix, _) => {
display_to_css_identifier(prefix, dest)?;
prefix.to_css(dest)?;
dest.write_char('|')
},
AttributeInNoNamespaceExists { ref local_name, .. } => {
dest.write_char('[')?;
display_to_css_identifier(local_name, dest)?;
local_name.to_css(dest)?;
dest.write_char(']')
},
AttributeInNoNamespace {
@ -1491,10 +1490,10 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
..
} => {
dest.write_char('[')?;
display_to_css_identifier(local_name, dest)?;
local_name.to_css(dest)?;
operator.to_css(dest)?;
dest.write_char('"')?;
write!(CssStringWriter::new(dest), "{}", value)?;
value.to_css(dest)?;
dest.write_char('"')?;
match case_sensitivity {
ParsedCaseSensitivity::CaseSensitive |
@ -1507,14 +1506,6 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
AttributeOther(ref attr_selector) => attr_selector.to_css(dest),
// 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"),
LastChild => dest.write_str(":last-child"),
OnlyChild => dest.write_str(":only-child"),
@ -1544,10 +1535,11 @@ impl<Impl: SelectorImpl> ToCss for Component<Impl> {
write_affine(dest, a, b)?;
dest.write_char(')')
},
Is(ref list) | Where(ref list) => {
Is(ref list) | Where(ref list) | Negation(ref list) => {
match *self {
Where(..) => dest.write_str(":where(")?,
Is(..) => dest.write_str(":is(")?,
Negation(..) => dest.write_str(":not(")?,
_ => unreachable!(),
}
serialize_selector_list(list.iter(), dest)?;
@ -1566,13 +1558,13 @@ impl<Impl: SelectorImpl> ToCss for AttrSelectorWithOptionalNamespace<Impl> {
dest.write_char('[')?;
match self.namespace {
Some(NamespaceConstraint::Specific((ref prefix, _))) => {
display_to_css_identifier(prefix, dest)?;
prefix.to_css(dest)?;
dest.write_char('|')?
},
Some(NamespaceConstraint::Any) => dest.write_str("*|")?,
None => {},
}
display_to_css_identifier(&self.local_name, dest)?;
self.local_name.to_css(dest)?;
match self.operation {
ParsedAttrSelectorOperation::Exists => {},
ParsedAttrSelectorOperation::WithValue {
@ -1582,7 +1574,7 @@ impl<Impl: SelectorImpl> ToCss for AttrSelectorWithOptionalNamespace<Impl> {
} => {
operator.to_css(dest)?;
dest.write_char('"')?;
write!(CssStringWriter::new(dest), "{}", expected_value)?;
expected_value.to_css(dest)?;
dest.write_char('"')?;
match case_sensitivity {
ParsedCaseSensitivity::CaseSensitive |
@ -1601,29 +1593,10 @@ impl<Impl: SelectorImpl> ToCss for LocalName<Impl> {
where
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.
/// selector : simple_selector_sequence [ combinator simple_selector_sequence ]* ;
///
@ -1794,7 +1767,7 @@ enum SimpleSelectorParseResult<Impl: SelectorImpl> {
SimpleSelector(Component<Impl>),
PseudoElement(Impl::PseudoElement),
SlottedPseudo(Selector<Impl>),
PartPseudo(Box<[Impl::PartName]>),
PartPseudo(Box<[Impl::Identifier]>),
}
#[derive(Debug)]
@ -1959,16 +1932,16 @@ where
return Ok(Component::AttributeOther(Box::new(
AttrSelectorWithOptionalNamespace {
namespace: Some(namespace),
local_name: local_name,
local_name_lower: local_name_lower,
local_name,
local_name_lower,
operation: ParsedAttrSelectorOperation::Exists,
never_matches: false,
},
)));
} else {
return Ok(Component::AttributeInNoNamespaceExists {
local_name: local_name,
local_name_lower: local_name_lower,
local_name,
local_name_lower,
});
}
},
@ -2032,19 +2005,19 @@ where
local_name_lower,
never_matches,
operation: ParsedAttrSelectorOperation::WithValue {
operator: operator,
case_sensitivity: case_sensitivity,
operator,
case_sensitivity,
expected_value: value,
},
},
)))
} else {
Ok(Component::AttributeInNoNamespace {
local_name: local_name,
operator: operator,
value: value,
case_sensitivity: case_sensitivity,
never_matches: never_matches,
local_name,
operator,
value,
case_sensitivity,
never_matches,
})
}
}
@ -2118,44 +2091,16 @@ where
P: Parser<'i, Impl = Impl>,
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.
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(),
))
Ok(Component::Negation(list.0.into_vec().into_boxed_slice()))
}
/// simple_selector_sequence
@ -2191,7 +2136,8 @@ where
if let Some(url) = parser.default_namespace() {
// If there was no explicit type selector, but there is a
// 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:
//
@ -2206,10 +2152,22 @@ where
// given selector is allowed to match a featureless element,
// it must do so while ignoring the default namespace.
//
if !matches!(
result,
SimpleSelectorParseResult::SimpleSelector(Component::Host(..))
) {
// https://drafts.csswg.org/selectors-4/#matches
//
// 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));
}
}
@ -2263,7 +2221,10 @@ where
let inner = SelectorList::parse_with_state(
parser,
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()))
}
@ -2292,16 +2253,15 @@ where
return Ok(Component::Host(Some(parse_inner_compound_selector(parser, input, state)?)));
},
"not" => {
if state.intersects(SelectorParsingState::INSIDE_NEGATION) {
return Err(input.new_custom_error(
SelectorParseErrorKind::UnexpectedIdent("not".into())
));
}
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() {
return Err(input.new_custom_error(SelectorParseErrorKind::InvalidState));
}
@ -2555,11 +2515,6 @@ pub mod tests {
fn is_user_action_state(&self) -> bool {
self.is_active_or_hover()
}
#[inline]
fn has_zero_specificity(&self) -> bool {
false
}
}
impl ToCss for PseudoClass {
@ -2611,10 +2566,8 @@ pub mod tests {
impl SelectorImpl for DummySelectorImpl {
type ExtraMatchingData = ();
type AttrValue = DummyAtom;
type AttrValue = DummyAttrValue;
type Identifier = DummyAtom;
type ClassName = DummyAtom;
type PartName = DummyAtom;
type LocalName = DummyAtom;
type NamespaceUrl = DummyAtom;
type NamespacePrefix = DummyAtom;
@ -2624,12 +2577,35 @@ pub mod tests {
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)]
pub struct DummyAtom(String);
impl fmt::Display for DummyAtom {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
<String as fmt::Display>::fmt(&self.0, fmt)
impl ToCss for DummyAtom {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
where
W: fmt::Write,
{
serialize_identifier(&self.0, dest)
}
}
@ -2657,6 +2633,10 @@ pub mod tests {
true
}
fn is_and_where_error_recovery(&self) -> ParseErrorRecovery {
ParseErrorRecovery::DiscardList
}
fn parse_part(&self) -> bool {
true
}
@ -2777,8 +2757,8 @@ pub mod tests {
assert!(list.is_ok());
}
const MATHML: &'static str = "http://www.w3.org/1998/Math/MathML";
const SVG: &'static str = "http://www.w3.org/2000/svg";
const MATHML: &str = "http://www.w3.org/1998/Math/MathML";
const SVG: &str = "http://www.w3.org/2000/svg";
#[test]
fn test_parsing() {
@ -3042,9 +3022,12 @@ pub mod tests {
vec![
Component::DefaultNamespace(MATHML.into()),
Component::Negation(
vec![Component::Class(DummyAtom::from("cl"))]
.into_boxed_slice()
.into(),
vec![Selector::from_vec(
vec![Component::Class(DummyAtom::from("cl"))],
specificity(0, 1, 0),
Default::default(),
)]
.into_boxed_slice()
),
],
specificity(0, 1, 0),
@ -3057,12 +3040,15 @@ pub mod tests {
vec![
Component::DefaultNamespace(MATHML.into()),
Component::Negation(
vec![
Component::DefaultNamespace(MATHML.into()),
Component::ExplicitUniversalType,
]
.into_boxed_slice()
.into(),
vec![Selector::from_vec(
vec![
Component::DefaultNamespace(MATHML.into()),
Component::ExplicitUniversalType,
],
specificity(0, 0, 0),
Default::default(),
)]
.into_boxed_slice(),
),
],
specificity(0, 0, 0),
@ -3075,15 +3061,18 @@ pub mod tests {
vec![
Component::DefaultNamespace(MATHML.into()),
Component::Negation(
vec![
Component::DefaultNamespace(MATHML.into()),
Component::LocalName(LocalName {
name: DummyAtom::from("e"),
lower_name: DummyAtom::from("e"),
}),
]
vec![Selector::from_vec(
vec![
Component::DefaultNamespace(MATHML.into()),
Component::LocalName(LocalName {
name: DummyAtom::from("e"),
lower_name: DummyAtom::from("e"),
}),
],
specificity(0, 0, 1),
Default::default(),
),]
.into_boxed_slice()
.into(),
),
],
specificity(0, 0, 1),
@ -3096,7 +3085,7 @@ pub mod tests {
vec![Component::AttributeInNoNamespace {
local_name: DummyAtom::from("attr"),
operator: AttrSelectorOperator::DashMatch,
value: DummyAtom::from("foo"),
value: DummyAttrValue::from("foo"),
never_matches: false,
case_sensitivity: ParsedCaseSensitivity::CaseSensitive,
}],
@ -3178,47 +3167,20 @@ pub mod tests {
)]))
);
parser.default_ns = None;
assert!(parse(":not(#provel.old)").is_err());
assert!(parse(":not(#provel > old)").is_err());
assert!(parse(":not(#provel.old)").is_ok());
assert!(parse(":not(#provel > old)").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
assert_eq!(
parse_ns(":not(*)", &parser),
Ok(SelectorList::from_vec(vec![Selector::from_vec(
vec![Component::Negation(
vec![Component::ExplicitUniversalType]
.into_boxed_slice()
.into(),
vec![Selector::from_vec(
vec![Component::ExplicitUniversalType],
specificity(0, 0, 0),
Default::default(),
)]
.into_boxed_slice()
)],
specificity(0, 0, 0),
Default::default(),
@ -3228,12 +3190,15 @@ pub mod tests {
parse_ns(":not(|*)", &parser),
Ok(SelectorList::from_vec(vec![Selector::from_vec(
vec![Component::Negation(
vec![
Component::ExplicitNoNamespace,
Component::ExplicitUniversalType,
]
.into_boxed_slice()
.into(),
vec![Selector::from_vec(
vec![
Component::ExplicitNoNamespace,
Component::ExplicitUniversalType,
],
specificity(0, 0, 0),
Default::default(),
)]
.into_boxed_slice(),
)],
specificity(0, 0, 0),
Default::default(),
@ -3245,25 +3210,12 @@ pub mod tests {
parse_ns_expected(":not(*|*)", &parser, Some(":not(*)")),
Ok(SelectorList::from_vec(vec![Selector::from_vec(
vec![Component::Negation(
vec![Component::ExplicitUniversalType]
.into_boxed_slice()
.into(),
)],
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,
]
vec![Selector::from_vec(
vec![Component::ExplicitUniversalType],
specificity(0, 0, 0),
Default::default()
)]
.into_boxed_slice()
.into(),
)],
specificity(0, 0, 0),
Default::default(),

View file

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

View file

@ -35,7 +35,7 @@ arrayvec = "0.5"
atomic_refcell = "0.1"
bitflags = "1.0"
byteorder = "1.0"
cssparser = "0.27"
cssparser = "0.28"
derive_more = "0.99"
encoding_rs = { version = "0.8", optional = true }
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_numbers, split_commas, split_html_space_chars};
use crate::values::specified::Length;
use crate::values::AtomString;
use crate::{Atom, LocalName, Namespace, Prefix};
use app_units::Au;
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
// and doing Atom comparisons instead of string comparisons where possible,
// with SelectorImpl::AttrValue changed to Atom.

View file

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

View file

@ -232,12 +232,12 @@ macro_rules! counter_style_descriptors {
fn to_css(&self, _guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
dest.write_str("@counter-style ")?;
self.name.to_css(&mut CssWriter::new(dest))?;
dest.write_str(" {\n")?;
dest.write_str(" { ")?;
$(
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))?;
dest.write_str(";\n")?;
dest.write_str("; ")?;
}
)+
dest.write_str("}")

View file

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

View file

@ -174,11 +174,11 @@ impl VariableValue {
/// Prevent values from getting terribly big since you can use custom
/// properties exponentially.
///
/// This number (1MB) is somewhat arbitrary, but silly enough that no
/// sane page would hit it. We could limit by number of total
/// This number (2MB) is somewhat arbitrary, but silly enough that no
/// reasonable page should hit it. We could limit by number of total
/// substitutions, but that was very easy to work around in practice
/// (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 {
return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError));
@ -247,8 +247,11 @@ impl VariableValue {
.collect::<Vec<_>>()
.into_boxed_slice();
let mut css = css.into_owned();
css.shrink_to_fit();
Ok(Arc::new(VariableValue {
css: css.into_owned(),
css,
first_token_type,
last_token_type,
references: custom_property_references,
@ -268,9 +271,11 @@ impl VariableValue {
unit: CowRcStr::from("px"),
};
let token_type = token.serialization_type();
let mut css = token.to_css_string();
css.shrink_to_fit();
VariableValue {
css: token.to_css_string(),
css,
first_token_type: token_type,
last_token_type: token_type,
references: Default::default(),
@ -577,7 +582,7 @@ impl<'a> CustomPropertiesBuilder<'a> {
let value = if !has_references && unparsed_value.references_environment {
let result = substitute_references_in_value(unparsed_value, &map, &self.device);
match result {
Ok(new_value) => Arc::new(new_value),
Ok(new_value) => new_value,
Err(..) => {
// Don't touch the map, this has the same effect as
// making it compute to the inherited one.
@ -657,6 +662,7 @@ impl<'a> CustomPropertiesBuilder<'a> {
let inherited = self.inherited.as_ref().map(|m| &***m);
substitute_all(&mut map, inherited, self.device);
}
map.shrink_to_fit();
Some(Arc::new(map))
}
}
@ -847,7 +853,7 @@ fn substitute_all(
let result = substitute_references_in_value(&value, &context.map, &context.device);
match result {
Ok(computed_value) => {
context.map.insert(name, Arc::new(computed_value));
context.map.insert(name, computed_value);
},
Err(..) => {
// This is invalid, reset it to the unset (inherited) value.
@ -889,7 +895,7 @@ fn substitute_references_in_value<'i>(
value: &'i VariableValue,
custom_properties: &CustomPropertiesMap,
device: &Device,
) -> Result<ComputedValue, ParseError<'i>> {
) -> Result<Arc<ComputedValue>, ParseError<'i>> {
debug_assert!(!value.references.is_empty() || value.references_environment);
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)?;
Ok(computed_value)
computed_value.css.shrink_to_fit();
Ok(Arc::new(computed_value))
}
/// 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::stylist::CascadeData;
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 selectors::matching::{ElementSelectorFlags, QuirksMode, VisitedHandlingMode};
use selectors::sink::Push;
@ -121,7 +122,7 @@ pub trait TDocument: Sized + Copy + Clone {
/// return an empty slice.
fn elements_with_id<'a>(
&self,
_id: &Atom,
_id: &AtomIdent,
) -> Result<&'a [<Self::ConcreteNode as TNode>::ConcreteElement], ()>
where
Self: 'a,
@ -187,22 +188,18 @@ pub trait TNode: Sized + Copy + Clone + Debug + NodeInfo + PartialEq {
return Some(c);
}
if Some(*self) == scoped_to {
return None;
}
let mut current = *self;
let mut current = Some(*self);
loop {
if let Some(s) = current.next_sibling() {
return Some(s);
}
let parent = current.parent_node();
if parent == scoped_to {
if current == scoped_to {
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.
fn elements_with_id<'a>(
&self,
_id: &Atom,
_id: &AtomIdent,
) -> Result<&'a [<Self::ConcreteNode as TNode>::ConcreteElement], ()>
where
Self: 'a,
@ -520,20 +517,20 @@ pub trait TElement:
/// Internal iterator for the classes of this element.
fn each_class<F>(&self, callback: F)
where
F: FnMut(&Atom);
F: FnMut(&AtomIdent);
/// Internal iterator for the part names of this element.
fn each_part<F>(&self, _callback: F)
where
F: FnMut(&Atom),
F: FnMut(&AtomIdent),
{
}
/// Internal iterator for the part names that this element exports for a
/// given part name.
fn each_exported_part<F>(&self, _name: &Atom, _callback: F)
fn each_exported_part<F>(&self, _name: &AtomIdent, _callback: F)
where
F: FnMut(&Atom),
F: FnMut(&AtomIdent),
{
}
@ -584,14 +581,35 @@ pub trait TElement:
traversal_flags: TraversalFlags,
) -> bool {
if traversal_flags.for_animation_only() {
// In animation-only restyle we never touch snapshots and don't
// care about them. But we can't assert '!self.handled_snapshot()'
// In animation-only restyle we never touch snapshots and don't care
// about them. But we can't assert '!self.handled_snapshot()'
// here since there are some cases that a second animation-only
// restyle which is a result of normal restyle (e.g. setting
// animation-name in normal restyle and creating a new CSS
// animation in a SequentialTask) is processed after the normal
// 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() {

View file

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

View file

@ -53,18 +53,8 @@ bitflags! {
const IN_MOZ_UI_INVALID_STATE = 1 << 13;
/// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-broken
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
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>
const IN_REQUIRED_STATE = 1 << 21;
/// <https://html.spec.whatwg.org/multipage/#selector-optional>
@ -107,13 +97,9 @@ bitflags! {
/// Non-standard & undocumented.
const IN_INCREMENT_SCRIPT_LEVEL_STATE = 1 << 38;
/// 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;
/// 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>
const IN_FOCUS_WITHIN_STATE = 1 << 43;
/// :dir matching; the states are used for dynamic change detection.
@ -137,14 +123,17 @@ bitflags! {
const IN_AUTOFILL_STATE = 1 << 50;
/// Non-standard & undocumented.
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
///
/// https://html.spec.whatwg.org/multipage/#centered-alignment
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 {
$(
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))?;
dest.write_str(";\n")?;
dest.write_str("; ")?;
}
)*
Ok(())
@ -469,8 +469,11 @@ macro_rules! font_face_descriptors_common {
type Declaration = ();
type Error = StyleParseErrorKind<'i>;
fn parse_value<'t>(&mut self, name: CowRcStr<'i>, input: &mut Parser<'i, 't>)
-> Result<(), ParseError<'i>> {
fn parse_value<'t>(
&mut self,
name: CowRcStr<'i>,
input: &mut Parser<'i, 't>,
) -> Result<(), ParseError<'i>> {
match_ignore_ascii_case! { &*name,
$(
$name if is_descriptor_enabled!($name) => {
@ -493,7 +496,7 @@ macro_rules! font_face_descriptors_common {
impl ToCssWithGuard for FontFaceRuleData {
// Serialization of FontFaceRule is not specced.
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)?;
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_expression::RangeOrOperator;
use crate::media_queries::{Device, MediaType};
use crate::values::computed::position::Ratio;
use crate::values::computed::CSSPixelLength;
use crate::values::computed::Ratio;
use crate::values::computed::Resolution;
use crate::Atom;
use app_units::Au;
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> {
let mut width = 0;
let mut height = 0;
@ -47,7 +35,7 @@ fn eval_width(
RangeOrOperator::evaluate(
range_or_operator,
value.map(Au::from),
viewport_size(device).width,
device.au_viewport_size().width,
)
}
@ -73,7 +61,7 @@ fn eval_height(
RangeOrOperator::evaluate(
range_or_operator,
value.map(Au::from),
viewport_size(device).height,
device.au_viewport_size().height,
)
}
@ -99,8 +87,12 @@ fn eval_aspect_ratio_for<F>(
where
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 {
Some(v) => v,
Some(v) => v.used_value(),
None => return true,
};
@ -115,7 +107,12 @@ fn eval_aspect_ratio(
query_value: Option<Ratio>,
range_or_operator: Option<RangeOrOperator>,
) -> 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
@ -168,7 +165,7 @@ where
/// https://drafts.csswg.org/mediaqueries-4/#orientation
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`.
@ -247,13 +244,13 @@ fn eval_color_index(
/// https://drafts.csswg.org/mediaqueries-4/#monochrome
fn eval_monochrome(
_: &Device,
device: &Device,
query_value: Option<u32>,
range_or_operator: Option<RangeOrOperator>,
) -> bool {
// For color devices we should return 0.
// FIXME: On a monochrome device, return the actual color depth, not 0!
let depth = 0;
let depth =
unsafe { bindings::Gecko_MediaFeatures_GetMonochromeBitsPerPixel(device.document()) };
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)]
#[repr(u8)]
enum OverflowBlock {
@ -468,6 +509,28 @@ fn eval_moz_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(
device: &Device,
query_value: Option<bool>,
@ -494,19 +557,6 @@ fn eval_system_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(
device: &Device,
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
/// nsPresContext::MediaFeatureValuesChanged is called when the value that
/// would be returned by the evaluator function could change.
pub static MEDIA_FEATURES: [MediaFeatureDescription; 53] = [
pub static MEDIA_FEATURES: [MediaFeatureDescription; 56] = [
feature!(
atom!("width"),
AllowsRanges::Yes,
@ -666,6 +716,23 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 53] = [
keyword_evaluator!(eval_prefers_reduced_motion, PrefersReducedMotion),
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!(
atom!("overflow-block"),
AllowsRanges::No,
@ -729,6 +796,18 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 53] = [
Evaluator::Ident(eval_moz_os_version),
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-forward")),
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-windows-default-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-compositor")),
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-reversed-placement")),
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.
use crate::context::QuirksMode;
use crate::custom_properties::CssEnvironment;
use crate::gecko::values::{convert_nscolor_to_rgba, convert_rgba_to_nscolor};
use crate::gecko_bindings::bindings;
@ -11,6 +12,7 @@ use crate::gecko_bindings::structs;
use crate::media_queries::MediaType;
use crate::properties::ComputedValues;
use crate::string_cache::Atom;
use crate::values::computed::Length;
use crate::values::specified::font::FONT_MEDIUM_PX;
use crate::values::{CustomIdent, KeyframesName};
use app_units::{Au, AU_PER_PX};
@ -18,8 +20,8 @@ use cssparser::RGBA;
use euclid::default::Size2D;
use euclid::{Scale, SideOffsets2D};
use servo_arc::Arc;
use std::fmt;
use std::sync::atomic::{AtomicBool, AtomicIsize, AtomicUsize, Ordering};
use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
use std::{cmp, fmt};
use style_traits::viewport::ViewportConstraints;
use style_traits::{CSSPixel, DevicePixel};
@ -30,15 +32,16 @@ pub struct Device {
/// `Device`, so having a raw document pointer here is fine.
document: *const structs::Document,
default_values: Arc<ComputedValues>,
/// 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.
/// The font size of the root element.
///
/// When computing the style of the root element, there can't be any
/// other 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: AtomicIsize,
/// 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 other
/// 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
/// inherit from body" quirk.
///
@ -85,8 +88,7 @@ impl Device {
Device {
document,
default_values: ComputedValues::default_values(doc),
// FIXME(bz): Seems dubious?
root_font_size: AtomicIsize::new(Au::from_px(FONT_MEDIUM_PX as i32).0 as isize),
root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()),
body_text_color: AtomicUsize::new(prefs.mDefaultColor as usize),
used_root_font_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)
pub fn root_font_size(&self) -> Au {
pub fn root_font_size(&self) -> Length {
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)
pub fn set_root_font_size(&self, size: Au) {
pub fn set_root_font_size(&self, size: Length) {
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.
@ -205,6 +212,15 @@ impl Device {
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.
pub fn media_type(&self) -> MediaType {
let pc = match self.pres_context() {
@ -222,12 +238,29 @@ impl Device {
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.
pub fn au_viewport_size(&self) -> Size2D<Au> {
let pc = match self.pres_context() {
Some(pc) => pc,
None => return Size2D::new(Au(0), Au(0)),
};
if pc.mIsRootPaginatedDocument() != 0 {
return self.page_size_minus_default_margin(pc);
}
let area = &pc.mVisibleArea;
Size2D::new(Au(area.width), Au(area.height))
}
@ -236,11 +269,15 @@ impl Device {
/// used for viewport unit resolution.
pub fn au_viewport_size_for_viewport_unit_resolution(&self) -> Size2D<Au> {
self.used_viewport_size.store(true, Ordering::Relaxed);
let pc = match self.pres_context() {
Some(pc) => pc,
None => return Size2D::new(Au(0), Au(0)),
};
if pc.mIsRootPaginatedDocument() != 0 {
return self.page_size_minus_default_margin(pc);
}
let size = &pc.mSizeForViewportUnits;
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).
#[inline]
pub fn zoom_text(&self, size: Au) -> Au {
pub fn zoom_text(&self, size: Length) -> Length {
size.scale_by(self.effective_text_zoom())
}
/// Un-apply text zoom.
#[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())
}

View file

@ -32,44 +32,38 @@ macro_rules! apply_non_ts_list {
[
("-moz-table-border-nonzero", MozTableBorderNonzero, _, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("-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, _),
("any-link", AnyLink, IN_VISITED_OR_UNVISITED_STATE, _),
("visited", Visited, IN_VISITED_STATE, _),
("active", Active, IN_ACTIVE_STATE, _),
("autofill", Autofill, IN_AUTOFILL_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS_AND_CHROME),
("checked", Checked, IN_CHECKED_STATE, _),
("defined", Defined, IN_DEFINED_STATE, _),
("disabled", Disabled, IN_DISABLED_STATE, _),
("enabled", Enabled, IN_ENABLED_STATE, _),
("focus", Focus, IN_FOCUS_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, _),
("-moz-drag-over", MozDragOver, IN_DRAGOVER_STATE, _),
("target", Target, IN_TARGET_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-styleeditor-transitioning", MozStyleeditorTransitioning, IN_STYLEEDITOR_TRANSITIONING_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("fullscreen", Fullscreen, IN_FULLSCREEN_STATE, _),
("-moz-modal-dialog", MozModalDialog, IN_MODAL_DIALOG_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
// TODO(emilio): This is inconsistently named (the capital R).
("-moz-focusring", MozFocusRing, IN_FOCUSRING_STATE, _),
("-moz-topmost-modal-dialog", MozTopmostModalDialog, IN_TOPMOST_MODAL_DIALOG_STATE, PSEUDO_CLASS_ENABLED_IN_UA_SHEETS),
("-moz-broken", MozBroken, IN_BROKEN_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-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-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-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, _),
("required", Required, IN_REQUIRED_STATE, _),
@ -83,19 +77,16 @@ macro_rules! apply_non_ts_list {
("read-only", ReadOnly, IN_READONLY_STATE, _),
("read-write", ReadWrite, IN_READWRITE_STATE, _),
("-moz-submit-invalid", MozSubmitInvalid, IN_MOZ_SUBMITINVALID_STATE, _),
("-moz-ui-valid", MozUIValid, IN_MOZ_UI_VALID_STATE, _),
("-moz-ui-invalid", MozUIInvalid, IN_MOZ_UI_INVALID_STATE, _),
("user-valid", UserValid, IN_MOZ_UI_VALID_STATE, _),
("user-invalid", UserInvalid, IN_MOZ_UI_INVALID_STATE, _),
("-moz-meter-optimum", MozMeterOptimum, IN_OPTIMUM_STATE, _),
("-moz-meter-sub-optimum", MozMeterSubOptimum, IN_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-last-node", MozLastNode, _, _),
("-moz-only-whitespace", MozOnlyWhitespace, _, _),
("-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-is-html", MozIsHTML, _, _),
("-moz-placeholder", MozPlaceholder, _, _),

View file

@ -38,7 +38,7 @@ impl ::selectors::parser::PseudoElement for PseudoElement {
PseudoElement::After |
PseudoElement::Marker |
PseudoElement::Placeholder |
PseudoElement::FileChooserButton
PseudoElement::FileSelectorButton
)
}
@ -160,15 +160,7 @@ impl PseudoElement {
/// Whether this pseudo-element is enabled for all content.
pub fn enabled_in_content(&self) -> bool {
match *self {
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,
}
self.flags() & structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME == 0
}
/// Whether this pseudo is enabled explicitly in UA sheets.

View file

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

View file

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

View file

@ -57,7 +57,7 @@ impl GeckoRestyleDamage {
&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
// property structs, so we check the custom properties here. Since
// 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::str::starts_with_ignore_ascii_case;
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::{CowRcStr, SourceLocation, ToCss, Token};
use selectors::parser::SelectorParseErrorKind;
use selectors::parser::{self as selector_parser, Selector};
use selectors::visitor::SelectorVisitor;
use selectors::parser::{ParseErrorRecovery, SelectorParseErrorKind};
use selectors::SelectorList;
use std::fmt;
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_};
use thin_slice::ThinBoxedSlice;
pub use crate::gecko::pseudo_element::{
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.
pub type Lang = Atom;
pub type Lang = AtomIdent;
macro_rules! pseudo_class_name {
([$(($css:expr, $name:ident, $state:tt, $flags:tt),)*]) => {
@ -54,11 +51,6 @@ macro_rules! pseudo_class_name {
Lang(Lang),
/// The `:dir` pseudo-class.
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.
MozLocaleDir(Direction),
}
@ -77,7 +69,7 @@ impl ToCss for NonTSPseudoClass {
$(NonTSPseudoClass::$name => concat!(":", $css),)*
NonTSPseudoClass::Lang(ref s) => {
dest.write_str(":lang(")?;
serialize_atom_identifier(s, dest)?;
s.to_css(dest)?;
return dest.write_char(')');
},
NonTSPseudoClass::MozLocaleDir(ref dir) => {
@ -90,17 +82,6 @@ impl ToCss for NonTSPseudoClass {
dir.to_css(&mut CssWriter::new(dest))?;
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-read-only" => Some(NonTSPseudoClass::ReadOnly),
"-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,
}
}
@ -144,8 +129,7 @@ impl NonTSPseudoClass {
$(NonTSPseudoClass::$name => check_flag!($flags),)*
NonTSPseudoClass::MozLocaleDir(_) |
NonTSPseudoClass::Lang(_) |
NonTSPseudoClass::Dir(_) |
NonTSPseudoClass::MozAny(_) => false,
NonTSPseudoClass::Dir(_) => false,
}
}
}
@ -155,8 +139,11 @@ impl NonTSPseudoClass {
/// Returns whether the pseudo-class is enabled in content sheets.
#[inline]
fn is_enabled_in_content(&self) -> bool {
if matches!(*self, NonTSPseudoClass::FocusVisible) {
return static_prefs::pref!("layout.css.focus-visible.enabled");
if let NonTSPseudoClass::Autofill = *self {
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)
}
@ -177,8 +164,7 @@ impl NonTSPseudoClass {
$(NonTSPseudoClass::$name => flag!($state),)*
NonTSPseudoClass::Dir(..) |
NonTSPseudoClass::MozLocaleDir(..) |
NonTSPseudoClass::Lang(..) |
NonTSPseudoClass::MozAny(..) => ElementState::empty(),
NonTSPseudoClass::Lang(..) => ElementState::empty(),
}
}
}
@ -200,15 +186,11 @@ impl NonTSPseudoClass {
self.state_flag().is_empty() &&
!matches!(
*self,
// :-moz-any is handled by the revalidation visitor walking
// the things inside it; it does not need to cause
// revalidation on its own.
NonTSPseudoClass::MozAny(_) |
// :dir() depends on state only, but doesn't use state_flag
// 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(_) |
// :dir() depends on state only, but doesn't use state_flag
// 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
// the namespace of the element; the former is invariant
// across all the elements involved and the latter is already
@ -216,7 +198,6 @@ impl NonTSPseudoClass {
NonTSPseudoClass::MozIsHTML |
// We prevent style sharing for NAC.
NonTSPseudoClass::MozNativeAnonymous |
NonTSPseudoClass::MozNativeAnonymousNoSpecificity |
// :-moz-placeholder is parsed but never matches.
NonTSPseudoClass::MozPlaceholder |
// :-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
)
}
#[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.
@ -276,12 +237,10 @@ pub struct SelectorImpl;
impl ::selectors::SelectorImpl for SelectorImpl {
type ExtraMatchingData = InvalidationMatchingData;
type AttrValue = Atom;
type Identifier = Atom;
type ClassName = Atom;
type PartName = Atom;
type LocalName = Atom;
type NamespacePrefix = Atom;
type AttrValue = AtomString;
type Identifier = AtomIdent;
type LocalName = AtomIdent;
type NamespacePrefix = AtomIdent;
type NamespaceUrl = Namespace;
type BorrowedNamespaceUrl = WeakNamespace;
type BorrowedLocalName = WeakAtom;
@ -344,13 +303,26 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
#[inline]
fn parse_is_and_where(&self) -> bool {
self.in_user_agent_stylesheet() ||
static_prefs::pref!("layout.css.is-where-selectors.enabled")
true
}
#[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]
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(
@ -378,7 +350,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
let pseudo_class = match_ignore_ascii_case! { &name,
"lang" => {
let name = parser.expect_ident_or_string()?;
NonTSPseudoClass::Lang(Atom::from(name.as_ref()))
NonTSPseudoClass::Lang(Lang::from(name.as_ref()))
},
"-moz-locale-dir" => {
NonTSPseudoClass::MozLocaleDir(Direction::parse(parser)?)
@ -386,14 +358,6 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
"dir" => {
NonTSPseudoClass::Dir(Direction::parse(parser)?)
},
"-moz-any" => {
NonTSPseudoClass::MozAny(
selector_parser::parse_compound_selector_list(
self,
parser,
)?.into()
)
},
_ => return Err(parser.new_custom_error(
SelectorParseErrorKind::UnsupportedPseudoClassOrElement(name.clone())
))
@ -464,10 +428,10 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> {
}
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()
}
}

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::ServoElementSnapshotTable;
use crate::invalidation::element::element_wrapper::ElementSnapshot;
use crate::selector_parser::AttrValue;
use crate::string_cache::{Atom, Namespace};
use crate::values::{AtomIdent, AtomString};
use crate::LocalName;
use crate::WeakAtom;
use selectors::attr::{AttrSelectorOperation, AttrSelectorOperator};
use selectors::attr::{CaseSensitivity, NamespaceConstraint};
@ -74,10 +77,10 @@ impl GeckoElementSnapshot {
#[inline]
pub fn each_attr_changed<F>(&self, mut callback: F)
where
F: FnMut(&Atom),
F: FnMut(&AtomIdent),
{
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(
&self,
ns: &NamespaceConstraint<&Namespace>,
local_name: &Atom,
operation: &AttrSelectorOperation<&Atom>,
local_name: &LocalName,
operation: &AttrSelectorOperation<&AttrValue>,
) -> bool {
unsafe {
match *operation {
@ -188,7 +191,7 @@ impl ElementSnapshot for GeckoElementSnapshot {
}
#[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")) {
Some(attr) => attr,
None => return false,
@ -198,12 +201,12 @@ impl ElementSnapshot for GeckoElementSnapshot {
}
#[inline]
fn imported_part(&self, name: &Atom) -> Option<Atom> {
fn imported_part(&self, name: &AtomIdent) -> Option<AtomIdent> {
snapshot_helpers::imported_part(&*self.mAttrs, name)
}
#[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) {
return false;
}
@ -214,7 +217,7 @@ impl ElementSnapshot for GeckoElementSnapshot {
#[inline]
fn each_class<F>(&self, callback: F)
where
F: FnMut(&Atom),
F: FnMut(&AtomIdent),
{
if !self.has_any(Flags::MaybeClass) {
return;
@ -224,12 +227,12 @@ impl ElementSnapshot for GeckoElementSnapshot {
}
#[inline]
fn lang_attr(&self) -> Option<Atom> {
fn lang_attr(&self) -> Option<AtomString> {
let ptr = unsafe { bindings::Gecko_SnapshotLangValue(self) };
if ptr.is_null() {
None
} 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::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 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 {
debug_assert!(bindings::Gecko_AssertClassAttrValueIsSane(attr));
let base_type = base_type(attr);
if base_type == structs::nsAttrValue_ValueBaseType_eStringBase {
return Class::None;
}
if base_type == structs::nsAttrValue_ValueBaseType_eAtomBase {
return Class::One(ptr::<nsAtom>(attr));
}
debug_assert_eq!(base_type, structs::nsAttrValue_ValueBaseType_eOtherBase);
let container = ptr::<structs::MiscContainer>(attr);
debug_assert_eq!(
(*container).mType,
structs::nsAttrValue_ValueType_eAtomArray
);
let array = (*container)
.__bindgen_anon_1
.mValue
.as_ref()
.__bindgen_anon_1
.mAtomArray
.as_ref();
Class::More(&***array)
if base_type == structs::nsAttrValue_ValueBaseType_eOtherBase {
let container = ptr::<structs::MiscContainer>(attr);
debug_assert_eq!(
(*container).mType,
structs::nsAttrValue_ValueType_eAtomArray
);
// NOTE: Bindgen doesn't deal with AutoTArray, so cast it below.
let array: *mut u8 = *(*container)
.__bindgen_anon_1
.mValue
.as_ref()
.__bindgen_anon_1
.mAtomArray
.as_ref();
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)]
@ -85,8 +88,8 @@ pub fn get_id(attrs: &[structs::AttrArray_InternalAttr]) -> Option<&WeakAtom> {
#[inline(always)]
pub(super) fn each_exported_part(
attrs: &[structs::AttrArray_InternalAttr],
name: &Atom,
mut callback: impl FnMut(&Atom),
name: &AtomIdent,
mut callback: impl FnMut(&AtomIdent),
) {
let attr = match find_attr(attrs, &atom!("exportparts")) {
Some(attr) => attr,
@ -100,7 +103,7 @@ pub(super) fn each_exported_part(
unsafe {
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)]
pub(super) fn imported_part(
attrs: &[structs::AttrArray_InternalAttr],
name: &Atom,
) -> Option<Atom> {
name: &AtomIdent,
) -> Option<AtomIdent> {
let attr = find_attr(attrs, &atom!("exportparts"))?;
let atom = unsafe { bindings::Gecko_Element_ImportedPart(attr, name.as_ptr()) };
if atom.is_null() {
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,
/// returns whether the attribute has that name.
#[inline(always)]
pub fn has_class_or_part(
name: &Atom,
name: &AtomIdent,
case_sensitivity: CaseSensitivity,
attr: &structs::nsAttrValue,
) -> bool {
@ -131,7 +134,8 @@ pub fn has_class_or_part(
Class::One(atom) => unsafe { case_sensitivity.eq_atom(name, WeakAtom::new(atom)) },
Class::More(atoms) => match case_sensitivity {
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 {
atoms
@ -147,15 +151,15 @@ pub fn has_class_or_part(
#[inline(always)]
pub fn each_class_or_part<F>(attr: &structs::nsAttrValue, mut callback: F)
where
F: FnMut(&Atom),
F: FnMut(&AtomIdent),
{
unsafe {
match get_class_or_part_from_attr(attr) {
Class::None => {},
Class::One(atom) => Atom::with(atom, callback),
Class::One(atom) => AtomIdent::with(atom, callback),
Class::More(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.
/// See https://drafts.csswg.org/css-values/#local-urls
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
@ -285,14 +289,13 @@ impl SpecifiedImageUrl {
/// Provides an alternate method for parsing that associates the URL
/// with anonymous CORS headers.
pub fn parse_with_cors_anonymous<'i, 't>(
pub fn parse_with_cors_mode<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
cors_mode: CorsMode,
) -> Result<Self, ParseError<'i>> {
Ok(SpecifiedImageUrl(SpecifiedUrl::parse_with_cors_mode(
context,
input,
CorsMode::Anonymous,
context, input, cors_mode,
)?))
}
}

View file

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

View file

@ -13,6 +13,7 @@ use std::slice;
impl<T> Deref for nsTArray<T> {
type Target = [T];
#[inline]
fn deref<'a>(&'a self) -> &'a [T] {
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};
macro_rules! local_name {
($s:tt) => {
atom!($s)
};
}
/// 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
@ -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 Sync for Atom {}
unsafe impl Sync for WeakAtom {}

View file

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

View file

@ -9,7 +9,8 @@ use crate::dom::TElement;
use crate::element_state::ElementState;
use crate::selector_parser::{AttrValue, NonTSPseudoClass, PseudoElement, SelectorImpl};
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::matching::{ElementSelectorFlags, MatchingContext};
use selectors::{Element, OpaqueElement};
@ -56,20 +57,20 @@ pub trait ElementSnapshot: Sized {
/// Whether this snapshot contains the class `name`. Should only be called
/// 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
/// called if `has_attrs()` returns true.
fn is_part(&self, name: &Atom) -> bool;
fn is_part(&self, name: &AtomIdent) -> bool;
/// 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
/// only be called if `has_attrs()` returns true.
fn each_class<F>(&self, _: F)
where
F: FnMut(&Atom);
F: FnMut(&AtomIdent);
/// The `xml:lang=""` or `lang=""` attribute value per this snapshot.
fn lang_attr(&self) -> Option<AttrValue>;
@ -177,16 +178,6 @@ where
// Some pseudo-classes need special handling to evaluate them against
// the snapshot.
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
// 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
@ -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="" attribtue from snapshots.
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() {
Some(snapshot) if snapshot.has_attrs() => snapshot
.id_attr()
@ -361,21 +361,21 @@ where
}
}
fn is_part(&self, name: &Atom) -> bool {
fn is_part(&self, name: &AtomIdent) -> bool {
match self.snapshot() {
Some(snapshot) if snapshot.has_attrs() => snapshot.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() {
Some(snapshot) if snapshot.has_attrs() => snapshot.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() {
Some(snapshot) if snapshot.has_attrs() => snapshot.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,
_ => 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,
Err(err) => {
*self.alloc_error = Some(err);
@ -506,6 +506,12 @@ impl<'a> SelectorVisitor for SelectorDependencyCollector<'a> {
NonTSPseudoClass::MozTableBorderNonzero => local_name!("border"),
#[cfg(feature = "gecko")]
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"),
_ => return true,
};

View file

@ -216,13 +216,13 @@ where
// TODO(emilio): Do this more efficiently!
snapshot.each_class(|c| {
if !element.has_class(c, CaseSensitivity::CaseSensitive) {
classes_removed.push(c.clone())
classes_removed.push(c.0.clone())
}
});
element.each_class(|c| {
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)]
use crate::context::QuirksMode;
use crate::dom::{TDocument, TElement, TNode};
use crate::hash::HashSet;
use crate::invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper};
use crate::invalidation::element::restyle_hints::RestyleHint;
use crate::media_queries::Device;
use crate::selector_map::{MaybeCaseInsensitiveHashMap, PrecomputedHashMap};
use crate::selector_parser::{SelectorImpl, Snapshot, SnapshotMap};
use crate::shared_lock::SharedRwLockReadGuard;
use crate::stylesheets::{CssRule, StylesheetInDocument};
use crate::stylesheets::{EffectiveRules, EffectiveRulesIterator};
use crate::values::AtomIdent;
use crate::Atom;
use crate::CaseSensitivityExt;
use crate::LocalName as SelectorLocalName;
use fxhash::FxHasher;
use selectors::attr::CaseSensitivity;
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
/// 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
/// StylesheetInvalidationSet's invalid_scopes or invalid_elements table.
/// element is determined by the given InvalidationKind in
/// StylesheetInvalidationSet's maps.
#[derive(Debug, Eq, Hash, MallocSizeOf, PartialEq)]
enum Invalidation {
/// An element with a given id.
ID(Atom),
ID(AtomIdent),
/// An element with a given class name.
Class(Atom),
Class(AtomIdent),
/// An element with a given local name.
LocalName {
name: SelectorLocalName,
@ -50,56 +62,35 @@ impl Invalidation {
fn is_id_or_class(&self) -> bool {
matches!(*self, Invalidation::ID(..) | Invalidation::Class(..))
}
}
fn matches<E>(
&self,
element: E,
snapshot: Option<&Snapshot>,
case_sensitivity: CaseSensitivity,
) -> bool
where
E: TElement,
{
match *self {
Invalidation::Class(ref class) => {
if element.has_class(class, case_sensitivity) {
return true;
}
/// Whether we should invalidate just the element, or the whole subtree within
/// it.
#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd)]
enum InvalidationKind {
None = 0,
Element,
Scope,
}
if let Some(snapshot) = snapshot {
if snapshot.has_class(class, case_sensitivity) {
return true;
}
}
},
Invalidation::ID(ref id) => {
if let Some(ref element_id) = element.id() {
if case_sensitivity.eq_atom(element_id, id) {
return true;
}
}
impl std::ops::BitOrAssign for InvalidationKind {
#[inline]
fn bitor_assign(&mut self, other: Self) {
*self = std::cmp::max(*self, other);
}
}
if let Some(snapshot) = snapshot {
if let Some(ref old_id) = snapshot.id_attr() {
if case_sensitivity.eq_atom(old_id, id) {
return true;
}
}
}
},
Invalidation::LocalName {
ref name,
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;
},
impl InvalidationKind {
#[inline]
fn is_scope(self) -> bool {
matches!(self, Self::Scope)
}
#[inline]
fn add(&mut self, other: Option<&InvalidationKind>) {
if let Some(other) = other {
*self |= *other;
}
false
}
}
@ -107,31 +98,24 @@ impl Invalidation {
///
/// TODO(emilio): We might be able to do the same analysis for media query
/// changes too (or even selector changes?).
#[derive(MallocSizeOf)]
#[derive(Debug, Default, MallocSizeOf)]
pub struct StylesheetInvalidationSet {
/// The subtrees we know we have to restyle so far.
invalid_scopes: FxHashSet<Invalidation>,
/// The elements we know we have to restyle so far.
invalid_elements: FxHashSet<Invalidation>,
/// Whether the whole document should be restyled.
classes: MaybeCaseInsensitiveHashMap<Atom, InvalidationKind>,
ids: MaybeCaseInsensitiveHashMap<Atom, InvalidationKind>,
local_names: PrecomputedHashMap<SelectorLocalName, InvalidationKind>,
fully_invalid: bool,
}
impl StylesheetInvalidationSet {
/// Create an empty `StylesheetInvalidationSet`.
pub fn new() -> Self {
Self {
invalid_scopes: FxHashSet::default(),
invalid_elements: FxHashSet::default(),
fully_invalid: false,
}
Default::default()
}
/// Mark the DOM tree styles' as fully invalid.
pub fn invalidate_fully(&mut self) {
debug!("StylesheetInvalidationSet::invalidate_fully");
self.invalid_scopes.clear();
self.invalid_elements.clear();
self.clear();
self.fully_invalid = true;
}
@ -157,22 +141,19 @@ impl StylesheetInvalidationSet {
return; // Nothing to do here.
}
let quirks_mode = device.quirks_mode();
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 {
self.invalid_scopes.clear();
self.invalid_elements.clear();
break;
}
}
debug!(" > resulting class invalidations: {:?}", self.classes);
debug!(" > resulting id invalidations: {:?}", self.ids);
debug!(
" > resulting subtree invalidations: {:?}",
self.invalid_scopes
);
debug!(
" > resulting self invalidations: {:?}",
self.invalid_elements
" > resulting local name invalidations: {:?}",
self.local_names
);
debug!(" > fully_invalid: {}", self.fully_invalid);
}
@ -198,21 +179,84 @@ impl StylesheetInvalidationSet {
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.
pub fn clear(&mut self) {
self.invalid_scopes.clear();
self.invalid_elements.clear();
self.classes.clear();
self.ids.clear();
self.local_names.clear();
self.fully_invalid = false;
debug_assert!(self.is_empty());
}
fn process_invalidations<E>(&self, element: E, snapshots: Option<&SnapshotMap>) -> bool
where
E: TElement,
{
debug!(
"Stylist::process_invalidations({:?}, {:?}, {:?})",
element, self.invalid_scopes, self.invalid_elements,
);
debug!("Stylist::process_invalidations({:?}, {:?})", element, self);
{
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");
return false;
}
let case_sensitivity = element
.as_node()
.owner_doc()
.quirks_mode()
.classes_and_ids_case_sensitivity();
self.process_invalidations_in_subtree(element, snapshots, case_sensitivity)
let quirks_mode = element.as_node().owner_doc().quirks_mode();
self.process_invalidations_in_subtree(element, snapshots, quirks_mode)
}
/// Process style invalidations in a given subtree. This traverses the
/// subtree looking for elements that match the invalidations in
/// invalid_scopes and invalid_elements.
/// subtree looking for elements that match the invalidations in our hash
/// map members.
///
/// Returns whether it invalidated at least one element's style.
#[allow(unsafe_code)]
@ -250,7 +290,7 @@ impl StylesheetInvalidationSet {
&self,
element: E,
snapshots: Option<&SnapshotMap>,
case_sensitivity: CaseSensitivity,
quirks_mode: QuirksMode,
) -> bool
where
E: TElement,
@ -275,31 +315,24 @@ impl StylesheetInvalidationSet {
let element_wrapper = snapshots.map(|s| ElementWrapper::new(element, s));
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!(
"process_invalidations_in_subtree: {:?} matched subtree {:?}",
element, invalidation
"process_invalidations_in_subtree: {:?} matched self",
element
);
data.hint.insert(RestyleHint::RESTYLE_SELF);
},
InvalidationKind::Scope => {
debug!(
"process_invalidations_in_subtree: {:?} matched subtree",
element
);
data.hint.insert(RestyleHint::restyle_subtree());
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;
@ -311,7 +344,7 @@ impl StylesheetInvalidationSet {
};
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 {
@ -322,9 +355,11 @@ impl StylesheetInvalidationSet {
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(
component: &Component<SelectorImpl>,
invalidation: &mut Option<Invalidation>,
@ -334,7 +369,7 @@ impl StylesheetInvalidationSet {
ref 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 {
name: name.clone(),
lower_name: lower_name.clone(),
@ -342,12 +377,12 @@ impl StylesheetInvalidationSet {
}
},
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()));
}
},
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()));
}
},
@ -371,7 +406,11 @@ impl StylesheetInvalidationSet {
/// prefer to generate subtree invalidations for the outermost part
/// of the selector, to reduce the amount of traversal we need to do
/// when flushing invalidations.
fn collect_invalidations(&mut self, selector: &Selector<SelectorImpl>) {
fn collect_invalidations(
&mut self,
selector: &Selector<SelectorImpl>,
quirks_mode: QuirksMode,
) {
debug!(
"StylesheetInvalidationSet::collect_invalidations({:?})",
selector
@ -404,13 +443,14 @@ impl StylesheetInvalidationSet {
if let Some(s) = subtree_invalidation {
debug!(" > Found subtree invalidation: {:?}", s);
if self.invalid_scopes.try_insert(s).is_ok() {
if self.insert_invalidation(s, InvalidationKind::Scope, quirks_mode) {
return;
}
}
if let Some(s) = element_invalidation {
debug!(" > Found element invalidation: {:?}", s);
if self.invalid_elements.try_insert(s).is_ok() {
if self.insert_invalidation(s, InvalidationKind::Element, quirks_mode) {
return;
}
}
@ -418,7 +458,120 @@ impl StylesheetInvalidationSet {
// The selector was of a form that we can't handle. Any element could
// match it, so let's just bail out.
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.
@ -427,6 +580,7 @@ impl StylesheetInvalidationSet {
rule: &CssRule,
guard: &SharedRwLockReadGuard,
device: &Device,
quirks_mode: QuirksMode,
) {
use crate::stylesheets::CssRule::*;
debug!("StylesheetInvalidationSet::collect_invalidations_for_rule");
@ -436,7 +590,7 @@ impl StylesheetInvalidationSet {
Style(ref lock) => {
let style_rule = lock.read_with(guard);
for selector in &style_rule.selectors.0 {
self.collect_invalidations(selector);
self.collect_invalidations(selector, quirks_mode);
if self.fully_invalid {
return;
}

View file

@ -131,22 +131,26 @@ pub use crate::gecko_string_cache as string_cache;
pub use crate::gecko_string_cache::Atom;
/// The namespace prefix type for Gecko, which is just an atom.
#[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.
#[cfg(feature = "gecko")]
pub type LocalName = crate::gecko_string_cache::Atom;
pub type LocalName = crate::values::AtomIdent;
#[cfg(feature = "gecko")]
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")]
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::owned_slice::OwnedSlice;
pub use style_traits::owned_str::OwnedStr;

View file

@ -133,3 +133,27 @@ macro_rules! profiler_label {
macro_rules! profiler_label {
($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() {
self.handle_display_change_for_smil_if_needed(
context,
old_values.as_ref().map(|v| &**v),
old_values.as_deref(),
new_values,
restyle_hint,
);
@ -408,7 +408,7 @@ trait PrivateMatchMethods: TElement {
let mut tasks = UpdateAnimationsTasks::empty();
if self.needs_animations_update(
context,
old_values.as_ref().map(|s| &**s),
old_values.as_deref(),
new_values,
/* pseudo_element = */ None,
) {
@ -417,7 +417,7 @@ trait PrivateMatchMethods: TElement {
let before_change_style = if self.might_need_transitions_update(
context,
old_values.as_ref().map(|s| &**s),
old_values.as_deref(),
new_values,
/* pseudo_element = */ None,
) {
@ -460,7 +460,7 @@ trait PrivateMatchMethods: TElement {
if important_rules_changed {
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);
}
}
@ -638,14 +638,14 @@ trait PrivateMatchMethods: TElement {
// map because this call will do a RwLock::read().
let needs_animations_update = self.needs_animations_update(
context,
old_values.as_ref().map(|s| &**s),
old_values.as_deref(),
new_values,
pseudo_element,
);
let might_need_transitions_update = self.might_need_transitions_update(
context,
old_values.as_ref().map(|s| &**s),
old_values.as_deref(),
new_values,
pseudo_element,
);

View file

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

View file

@ -15,8 +15,7 @@ use crate::parser::{Parse, ParserContext};
#[cfg(feature = "servo")]
use crate::servo::media_queries::MEDIA_FEATURES;
use crate::str::{starts_with_ignore_ascii_case, string_as_ascii_lowercase};
use crate::values::computed::position::Ratio;
use crate::values::computed::{self, ToComputedValue};
use crate::values::computed::{self, Ratio, ToComputedValue};
use crate::values::specified::{Integer, Length, Number, Resolution};
use crate::values::{serialize_atom_identifier, CSSFloat};
use crate::{Atom, Zero};
@ -218,11 +217,17 @@ fn consume_operation_or_colon(input: &mut Parser) -> Result<Option<Operator>, ()
}
#[allow(unused_variables)]
fn disabled_by_pref(feature: &Atom) -> bool {
fn disabled_by_pref(feature: &Atom, context: &ParserContext) -> bool {
#[cfg(feature = "gecko")]
{
if *feature == atom!("-moz-touch-enabled") {
return !static_prefs::pref!("layout.css.moz-touch-enabled.enabled");
if *feature == atom!("forced-colors") {
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
@ -305,7 +310,7 @@ impl MediaFeatureExpression {
},
};
if disabled_by_pref(&feature.name) ||
if disabled_by_pref(&feature.name, context) ||
!requirements.contains(feature.requirements) ||
(range.is_some() && !feature.allows_ranges())
{
@ -492,15 +497,9 @@ impl MediaExpressionValue {
MediaExpressionValue::Float(number.get())
},
Evaluator::NumberRatio(..) => {
use crate::values::generics::position::Ratio as GenericRatio;
use crate::values::generics::NonNegative;
use crate::values::specified::position::Ratio;
let ratio = Ratio::parse(context, input)?;
MediaExpressionValue::NumberRatio(GenericRatio(
NonNegative(ratio.0.get()),
NonNegative(ratio.1.get()),
))
use crate::values::specified::Ratio as SpecifiedRatio;
let ratio = SpecifiedRatio::parse(context, input)?;
MediaExpressionValue::NumberRatio(Ratio::new(ratio.0.get(), ratio.1.get()))
},
Evaluator::Resolution(..) => {
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
/// rule type.
#[inline]
@ -161,7 +140,7 @@ impl<'a> ParserContext<'a> {
/// Returns whether chrome-only rules should be parsed.
#[inline]
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.
@ -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 {
fn parse<'i, 't>(
_: &ParserContext,

View file

@ -7,7 +7,7 @@ import os.path
import re
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, BASE) # For importing `data.py`
@ -17,7 +17,7 @@ from mako.template import Template
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", "")
@ -48,15 +48,20 @@ STYLE_STRUCT_LIST = [
def main():
usage = ("Usage: %s [ servo-2013 | servo-2020 | gecko ] [ style-crate | geckolib <template> | html ]" %
sys.argv[0])
usage = (
"Usage: %s [ servo-2013 | servo-2020 | gecko ] [ style-crate | geckolib <template> | html ]"
% sys.argv[0]
)
if len(sys.argv) < 3:
abort(usage)
engine = sys.argv[1]
output = sys.argv[2]
if engine not in ["servo-2013", "servo-2020", "gecko"] \
or output not in ["style-crate", "geckolib", "html"]:
if engine not in ["servo-2013", "servo-2020", "gecko"] or output not in [
"style-crate",
"geckolib",
"html",
]:
abort(usage)
properties = data.PropertiesData(engine=engine)
@ -103,19 +108,19 @@ def main():
pref_attr = "servo_2020_pref"
properties_dict = {
kind: {
p.name: {
"pref": getattr(p, pref_attr)
}
p.name: {"pref": getattr(p, pref_attr)}
for prop in properties_list
if prop.enabled_in_content()
for p in [prop] + prop.alias
for p in [prop] + prop.aliases
}
for kind, properties_list in [
("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)
doc_servo = os.path.join(BASE, "..", "..", "..", "target", "doc", "servo")
write(doc_servo, "css-properties.html", as_html)
@ -136,14 +141,16 @@ def abort(message):
def render(filename, **context):
try:
lookup = TemplateLookup(directories=[BASE],
input_encoding="utf8",
strict_undefined=True)
template = Template(open(filename, "rb").read(),
filename=filename,
input_encoding="utf8",
lookup=lookup,
strict_undefined=True)
lookup = TemplateLookup(
directories=[BASE], input_encoding="utf8", strict_undefined=True
)
template = Template(
open(filename, "rb").read(),
filename=filename,
input_encoding="utf8",
lookup=lookup,
strict_undefined=True,
)
# Uncomment to debug generated Python code:
# write("/tmp", "mako_%s.py" % os.path.basename(filename), template.code)
return template.render(**context)
@ -161,7 +168,7 @@ def write(directory, filename, content):
python_addr = RE_PYTHON_ADDR.search(content)
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__":

View file

@ -10,21 +10,22 @@ use crate::dom::TElement;
use crate::font_metrics::FontMetricsProvider;
use crate::logical_geometry::WritingMode;
use crate::media_queries::Device;
use crate::properties::{ComputedValues, StyleBuilder, Importance};
use crate::properties::{LonghandId, LonghandIdSet, CSSWideKeyword, PropertyFlags};
use crate::properties::{PropertyDeclaration, PropertyDeclarationId, DeclarationImportanceIterator};
use crate::properties::{CASCADE_PROPERTY, ComputedValueFlags};
use crate::properties::{
CSSWideKeyword, ComputedValueFlags, ComputedValues, DeclarationImportanceIterator, Importance,
LonghandId, LonghandIdSet, PropertyDeclaration, PropertyDeclarationId, PropertyFlags,
ShorthandsWithPropertyReferencesCache, StyleBuilder, CASCADE_PROPERTY,
};
use crate::rule_cache::{RuleCache, RuleCacheConditions};
use crate::rule_tree::StrongRuleNode;
use crate::selector_parser::PseudoElement;
use crate::stylesheets::{Origin, PerOrigin};
use servo_arc::Arc;
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 std::borrow::Cow;
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'
/// properties.
@ -121,7 +122,11 @@ struct DeclarationIterator<'a> {
impl<'a> DeclarationIterator<'a> {
#[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 mut iter = Self {
guards,
@ -148,7 +153,6 @@ impl<'a> DeclarationIterator<'a> {
None => DeclarationImportanceIterator::default(),
};
}
}
impl<'a> Iterator for DeclarationIterator<'a> {
@ -282,10 +286,7 @@ where
let mut declarations = SmallVec::<[(&_, Origin); 32]>::new();
let custom_properties = {
let mut builder = CustomPropertiesBuilder::new(
inherited_style.custom_properties(),
device,
);
let mut builder = CustomPropertiesBuilder::new(inherited_style.custom_properties(), device);
for (declaration, origin) in iter {
declarations.push((declaration, origin));
@ -297,9 +298,7 @@ where
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 {
// 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 mut cascade = Cascade::new(&mut context, cascade_mode);
let mut shorthand_cache = ShorthandsWithPropertyReferencesCache::default();
cascade
.apply_properties::<EarlyProperties, _>(ApplyResetProperties::Yes, declarations.iter().cloned());
cascade.apply_properties::<EarlyProperties, _>(
ApplyResetProperties::Yes,
declarations.iter().cloned(),
&mut shorthand_cache,
);
cascade.compute_visited_style_if_needed(
element,
@ -346,7 +349,11 @@ where
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
};
@ -376,7 +383,7 @@ where
/// given initial value if nothing in other origins did override it.
///
/// 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(
builder: &StyleBuilder,
@ -385,6 +392,8 @@ fn tweak_when_ignoring_colors(
declaration: &mut Cow<PropertyDeclaration>,
declarations_to_apply_unless_overriden: &mut DeclarationsToApplyUnlessOverriden,
) {
use crate::values::specified::Color;
if !longhand_id.ignored_when_document_colors_disabled() {
return;
}
@ -397,25 +406,58 @@ fn tweak_when_ignoring_colors(
// 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
// 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;
}
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.
match **declaration {
// We honor color and background-color: transparent, and
// "revert-or-initial" otherwise.
PropertyDeclaration::BackgroundColor(ref color) => {
if !color.is_transparent() {
let color = builder.device.default_background_color();
declarations_to_apply_unless_overriden.push(
PropertyDeclaration::BackgroundColor(color.into())
)
// For background-color, we revert or initial-with-preserved-alpha
// otherwise, this is needed to preserve semi-transparent
// backgrounds.
//
// 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) => {
// otherwise.
if color.0.is_transparent() {
// We honor color: transparent, and "revert-or-initial" otherwise.
if alpha_channel(&color.0) == 0 {
return;
}
// 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.
if builder.get_parent_inherited_text().clone_color().alpha == 0 {
let color = builder.device.default_color();
declarations_to_apply_unless_overriden.push(
PropertyDeclaration::Color(specified::ColorPropertyValue(color.into()))
)
declarations_to_apply_unless_overriden.push(PropertyDeclaration::Color(
specified::ColorPropertyValue(color.into()),
))
}
},
// 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> {
@ -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,
declaration: &'decl PropertyDeclaration,
) -> Cow<'decl, PropertyDeclaration> {
cache: &'cache mut ShorthandsWithPropertyReferencesCache,
) -> Cow<'decl, PropertyDeclaration>
where
'cache: 'decl,
{
let declaration = match *declaration {
PropertyDeclaration::WithVariables(ref declaration) => declaration,
ref d => return Cow::Borrowed(d),
@ -478,22 +524,39 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
.rule_cache_conditions
.borrow_mut()
.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,
self.context.builder.writing_mode,
self.context.builder.custom_properties.as_ref(),
self.context.quirks_mode,
self.context.device(),
))
cache,
)
}
#[inline(always)]
fn apply_declaration(
&mut self,
longhand_id: LonghandId,
declaration: &PropertyDeclaration,
) {
fn apply_declaration(&mut self, longhand_id: LonghandId, declaration: &PropertyDeclaration) {
// We could (and used to) use a pattern match here, but that bloats this
// function to over 100K of compiled code!
//
@ -507,6 +570,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
&mut self,
apply_reset: ApplyResetProperties,
declarations: I,
mut shorthand_cache: &mut ShorthandsWithPropertyReferencesCache,
) where
Phase: CascadePhase,
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 mut declarations_to_apply_unless_overriden =
DeclarationsToApplyUnlessOverriden::new();
let mut declarations_to_apply_unless_overriden = DeclarationsToApplyUnlessOverriden::new();
for (declaration, origin) in declarations {
let declaration_id = declaration.id();
@ -551,7 +614,11 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
continue;
}
if self.reverted.borrow_for_origin(&origin).contains(physical_longhand_id) {
if self
.reverted
.borrow_for_origin(&origin)
.contains(physical_longhand_id)
{
continue;
}
@ -564,7 +631,8 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
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
// 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);
}
let unset = css_wide_keyword.map_or(false, |css_wide_keyword| {
match css_wide_keyword {
CSSWideKeyword::Unset => true,
CSSWideKeyword::Inherit => inherited,
CSSWideKeyword::Initial => !inherited,
CSSWideKeyword::Revert => unreachable!(),
}
let unset = css_wide_keyword.map_or(false, |css_wide_keyword| match css_wide_keyword {
CSSWideKeyword::Unset => true,
CSSWideKeyword::Inherit => inherited,
CSSWideKeyword::Initial => !inherited,
CSSWideKeyword::Revert => unreachable!(),
});
if unset {
@ -703,7 +769,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
// styles, we cache the unvisited style instead. We still do
// need to set the caching dependencies properly if present
// though, so the cache conditions need to match.
/* rule_cache = */ None,
None, // rule_cache
&mut *self.context.rule_cache_conditions.borrow_mut(),
element,
);
@ -722,13 +788,18 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
if let Some(svg) = builder.get_svg_if_mutated() {
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);
}
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);
}
@ -765,8 +836,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
//
// 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.
let reset_props_bits =
ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND |
let reset_props_bits = ComputedValueFlags::HAS_AUTHOR_SPECIFIED_BORDER_BACKGROUND |
ComputedValueFlags::HAS_AUTHOR_SPECIFIED_PADDING;
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::values::computed::font::GenericFontFamily;
if !self.seen.contains(LonghandId::XLang) &&
!self.seen.contains(LonghandId::FontFamily) {
if !self.seen.contains(LonghandId::XLang) && !self.seen.contains(LonghandId::FontFamily) {
return;
}
@ -798,7 +867,10 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
// System fonts are all right, and should have the default font type
// set to none already, so bail out early.
if font.mFont.systemFont {
debug_assert_eq!(font.mFont.fontlist.mDefaultFontType, GenericFontFamily::None);
debug_assert_eq!(
font.mFont.fontlist.mDefaultFontType,
GenericFontFamily::None
);
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
// cursive or fantasy, since they're ignored, see bug 789788), and
// we have a generic family to actually replace it with.
let prioritize_user_fonts =
!use_document_fonts &&
let prioritize_user_fonts = !use_document_fonts &&
matches!(
font.mGenericID,
GenericFontFamily::None |
GenericFontFamily::Fantasy |
GenericFontFamily::Cursive
GenericFontFamily::Fantasy |
GenericFontFamily::Cursive
) &&
default_font_type != GenericFontFamily::None;
@ -834,9 +905,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
let font = builder.mutate_font().gecko_mut();
font.mFont.fontlist.mDefaultFontType = default_font_type;
if prioritize_user_fonts {
unsafe {
bindings::Gecko_nsStyleFont_PrioritizeUserFonts(font, default_font_type)
}
unsafe { 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) {
use crate::values::computed::ToComputedValue;
use crate::values::specified;
use app_units::Au;
if !self.seen.contains(LonghandId::XLang) &&
!self.seen.contains(LonghandId::FontFamily) {
if !self.seen.contains(LonghandId::XLang) && !self.seen.contains(LonghandId::FontFamily) {
return;
}
@ -860,10 +927,10 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
_ => {
self.context.for_non_inherited_property = None;
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;
}
@ -878,11 +945,13 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
#[cfg(feature = "gecko")]
fn constrain_font_size_if_needed(&mut self) {
use crate::gecko_bindings::bindings;
use crate::values::generics::NonNegative;
if !self.seen.contains(LonghandId::XLang) &&
!self.seen.contains(LonghandId::FontFamily) &&
!self.seen.contains(LonghandId::MozMinFontSizeRatio) &&
!self.seen.contains(LonghandId::FontSize) {
!self.seen.contains(LonghandId::FontFamily) &&
!self.seen.contains(LonghandId::MozMinFontSizeRatio) &&
!self.seen.contains(LonghandId::FontSize)
{
return;
}
@ -890,17 +959,14 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
let min_font_size = {
let font = builder.get_font().gecko();
let min_font_size = unsafe {
bindings::Gecko_nsStyleFont_ComputeMinSize(
font,
builder.device.document(),
)
bindings::Gecko_nsStyleFont_ComputeMinSize(font, builder.device.document())
};
if font.mFont.size >= min_font_size {
if font.mFont.size.0 >= min_font_size {
return;
}
min_font_size
NonNegative(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.
#[cfg(feature = "gecko")]
fn handle_mathml_scriptlevel_if_needed(&mut self) {
use app_units::Au;
use std::cmp;
use crate::values::generics::NonNegative;
if !self.seen.contains(LonghandId::MozScriptLevel) &&
!self.seen.contains(LonghandId::MozScriptMinSize) &&
!self.seen.contains(LonghandId::MozScriptSizeMultiplier) {
if !self.seen.contains(LonghandId::MathDepth) &&
!self.seen.contains(LonghandId::MozScriptMinSize) &&
!self.seen.contains(LonghandId::MozScriptSizeMultiplier)
{
return;
}
@ -961,21 +1027,20 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> {
let font = builder.get_font().gecko();
let parent_font = builder.get_parent_font().gecko();
let delta =
font.mScriptLevel.saturating_sub(parent_font.mScriptLevel);
let delta = font.mMathDepth.saturating_sub(parent_font.mMathDepth);
if delta == 0 {
return;
}
let mut min = Au(parent_font.mScriptMinSize);
let mut min = parent_font.mScriptMinSize;
if font.mAllowZoomAndMinSize {
min = builder.device.zoom_text(min);
}
let scale = (parent_font.mScriptSizeMultiplier as f32).powi(delta as i32);
let parent_size = Au(parent_font.mSize);
let parent_unconstrained_size = Au(parent_font.mScriptUnconstrainedSize);
let parent_size = parent_font.mSize.0;
let parent_unconstrained_size = parent_font.mScriptUnconstrainedSize.0;
let new_size = parent_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 {
(parent_size, new_unconstrained_size)
} else {
(cmp::max(min, new_size), new_unconstrained_size)
(min.max(new_size), new_unconstrained_size)
}
} else {
// 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
// of em units), use that instead.
(
cmp::min(new_size, cmp::max(new_unconstrained_size, min)),
new_unconstrained_size
new_size.min(new_unconstrained_size.max(min)),
new_unconstrained_size,
)
}
};
let font = builder.mutate_font().gecko_mut();
font.mFont.size = new_size.0;
font.mSize = new_size.0;
font.mScriptUnconstrainedSize = new_unconstrained_size.0;
font.mFont.size = NonNegative(new_size);
font.mSize = NonNegative(new_size);
font.mScriptUnconstrainedSize = NonNegative(new_unconstrained_size);
}
/// 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.
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
/// computing the style of pseudo-elements, given whether the
/// 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.
const INHERITS_CONTENT = 1 << 7;
const CONTENT_DEPENDS_ON_INHERITED_STYLE = 1 << 7;
/// Whether the child explicitly inherits any reset property.
const INHERITS_RESET_STYLE = 1 << 8;
/// Whether any value on our style is font-metric-dependent.
const DEPENDS_ON_FONT_METRICS = 1 << 9;
/// Whether any value on our style is font-metric-dependent on our
/// 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.
///
/// 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.
const IS_ROOT_ELEMENT_STYLE = 1 << 11;
const IS_ROOT_ELEMENT_STYLE = 1 << 12;
/// 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
/// (except border-image-*), background-color, or background-image.
@ -77,13 +82,13 @@ bitflags! {
/// TODO(emilio): Maybe do include border-image, see:
///
/// 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.
///
/// FIXME(emilio): Try to merge this with BORDER_BACKGROUND, see
/// 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"]
# bool is True when logical
ALL_SIDES = [(side, False) for side in PHYSICAL_SIDES] + [(side, True) for side in LOGICAL_SIDES]
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]
ALL_SIDES = [(side, False) for side in PHYSICAL_SIDES] + [
(side, True) for side in LOGICAL_SIDES
]
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
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_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):
if engine == "gecko" and side[1]:
@ -50,7 +84,9 @@ def to_snake_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):
@ -72,23 +108,32 @@ def parse_aliases(value):
class Keyword(object):
def __init__(self, name, values, gecko_constant_prefix=None,
gecko_enum_prefix=None, custom_consts=None,
extra_gecko_values=None,
extra_servo_2013_values=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):
def __init__(
self,
name,
values,
gecko_constant_prefix=None,
gecko_enum_prefix=None,
custom_consts=None,
extra_gecko_values=None,
extra_servo_2013_values=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.values = values.split()
if gecko_constant_prefix and gecko_enum_prefix:
raise TypeError("Only one of gecko_constant_prefix and gecko_enum_prefix "
"can be specified")
self.gecko_constant_prefix = gecko_constant_prefix or \
"NS_STYLE_" + self.name.upper().replace("-", "_")
raise TypeError(
"Only one of gecko_constant_prefix and gecko_enum_prefix "
"can be specified"
)
self.gecko_constant_prefix = (
gecko_constant_prefix or "NS_STYLE_" + self.name.upper().replace("-", "_")
)
self.gecko_enum_prefix = gecko_enum_prefix
self.extra_gecko_values = (extra_gecko_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_2020_aliases = parse_aliases(servo_2020_aliases or "")
self.consts_map = {} if custom_consts is None else custom_consts
self.gecko_strip_moz_prefix = True \
if gecko_strip_moz_prefix is None else gecko_strip_moz_prefix
self.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)
def values_for(self, engine):
@ -122,11 +168,14 @@ class Keyword(object):
raise Exception("Bad engine: " + engine)
def gecko_constant(self, value):
moz_stripped = (value.replace("-moz-", '')
if self.gecko_strip_moz_prefix else value.replace("-moz-", 'moz-'))
moz_stripped = (
value.replace("-moz-", "")
if self.gecko_strip_moz_prefix
else value.replace("-moz-", "moz-")
)
mapped = self.consts_map.get(value)
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]
return self.gecko_enum_prefix + "::" + "".join(parts)
else:
@ -146,13 +195,19 @@ class Keyword(object):
if self.gecko_enum_prefix is None:
return cast_type.upper() + "_" + self.gecko_constant(value)
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):
if isinstance(arg, bool):
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"
@ -169,41 +224,30 @@ def to_phys(name, logical, physical):
return name.replace(logical, physical).replace("inset-", "")
class Longhand(object):
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,
allowed_in_keyframe_block=True, cast_type='u8',
logical=False, logical_group=None, alias=None, extra_prefixes=None, boxed=False,
flags=None, allowed_in_page_rule=False, allow_quirks="No",
ignored_when_colors_disabled=False,
simple_vector_bindings=False,
vector=False, servo_restyle_damage="repaint"):
class Property(object):
def __init__(
self,
name,
spec,
servo_2013_pref,
servo_2020_pref,
gecko_pref,
enabled_in,
rule_types_allowed,
aliases,
extra_prefixes,
flags,
):
self.name = name
if not spec:
raise TypeError("Spec should be specified for %s" % name)
raise TypeError("Spec should be specified for " + name)
self.spec = spec
self.keyword = keyword
self.predefined_type = predefined_type
self.ident = to_rust_ident(name)
self.camel_case = to_camel_case(self.ident)
self.style_struct = style_struct
self.servo_2013_pref = servo_2013_pref
self.servo_2020_pref = servo_2020_pref
self.gecko_pref = gecko_pref
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.rule_types_allowed = rule_values_from_arg(rule_types_allowed)
# For enabled_in, the setup is as follows:
# It needs to be one of the four values: ["", "ua", "chrome", "content"]
# * "chrome" implies "ua", and implies that they're explicitly
@ -211,8 +255,94 @@ class Longhand(object):
# * "" implies the property will never be parsed.
# * "content" implies the property is accessible unconditionally,
# 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.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.gecko_ffi_name = gecko_ffi_name or "m" + self.camel_case
self.cast_type = cast_type
@ -221,34 +351,28 @@ class Longhand(object):
if self.logical:
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.flags = flags.split() if flags else []
self.allowed_in_page_rule = arg_to_bool(allowed_in_page_rule)
self.allow_quirks = allow_quirks
self.ignored_when_colors_disabled = ignored_when_colors_disabled
self.is_vector = vector
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
# really random.
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.animatable = animation_value_type != "none"
self.transitionable = animation_value_type != "none" \
and animation_value_type != "discrete"
self.is_animatable_with_computed_value = animation_value_type == "ComputedValue" \
self.transitionable = (
animation_value_type != "none" and animation_value_type != "discrete"
)
self.is_animatable_with_computed_value = (
animation_value_type == "ComputedValue"
or animation_value_type == "discrete"
)
# See compute_damage for the various values this can take
self.servo_restyle_damage = servo_restyle_damage
@ -263,45 +387,39 @@ class Longhand(object):
if not self.logical:
return []
candidates = [s for s in LOGICAL_SIDES + LOGICAL_SIZES + LOGICAL_CORNERS
if s in self.name] + [s for s in LOGICAL_AXES if self.name.endswith(s)]
assert(len(candidates) == 1)
candidates = [
s for s in LOGICAL_SIDES + LOGICAL_SIZES + LOGICAL_CORNERS if s in self.name
] + [s for s in LOGICAL_AXES if self.name.endswith(s)]
assert len(candidates) == 1
logical_side = candidates[0]
physical = PHYSICAL_SIDES if logical_side in LOGICAL_SIDES \
else PHYSICAL_SIZES if logical_side in LOGICAL_SIZES \
else PHYSICAL_AXES if logical_side in LOGICAL_AXES \
physical = (
PHYSICAL_SIDES
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
return [data.longhands_by_name[to_phys(self.name, logical_side, physical_side)]
for physical_side in physical]
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"
)
return [
data.longhands_by_name[to_phys(self.name, logical_side, physical_side)]
for physical_side in physical
]
def may_be_disabled_in(self, shorthand, engine):
if engine == "gecko":
return self.gecko_pref and self.gecko_pref != shorthand.gecko_pref
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":
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:
raise Exception("Bad engine: " + engine)
@ -334,6 +452,7 @@ class Longhand(object):
"BackgroundRepeat",
"BorderImageRepeat",
"BorderStyle",
"table::CaptionSide",
"Clear",
"ColumnCount",
"Contain",
@ -360,7 +479,7 @@ class Longhand(object):
"MasonryAutoFlow",
"MozForceBrokenImageIcon",
"MozListReversed",
"MozScriptLevel",
"MathDepth",
"MozScriptMinSize",
"MozScriptSizeMultiplier",
"TextDecorationSkipInk",
@ -407,41 +526,36 @@ class Longhand(object):
return computed
return "<{} as ToAnimatedValue>::AnimatedValue".format(computed)
def nscsspropertyid(self):
return "nsCSSPropertyID::eCSSProperty_%s" % self.ident
class Shorthand(object):
def __init__(self, name, sub_properties, spec=None,
servo_2013_pref=None,
servo_2020_pref=None,
gecko_pref=None,
enabled_in="content",
allowed_in_keyframe_block=True, alias=None, extra_prefixes=None,
allowed_in_page_rule=False, flags=None):
self.name = name
if not spec:
raise TypeError("Spec should be specified for %s" % name)
self.spec = spec
self.ident = to_rust_ident(name)
self.camel_case = to_camel_case(self.ident)
self.servo_2013_pref = servo_2013_pref
self.servo_2020_pref = servo_2020_pref
self.gecko_pref = gecko_pref
class Shorthand(Property):
def __init__(
self,
name,
sub_properties,
spec=None,
servo_2013_pref=None,
servo_2020_pref=None,
gecko_pref=None,
enabled_in="content",
rule_types_allowed=DEFAULT_RULES,
aliases=None,
extra_prefixes=None,
flags=None,
):
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.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):
for sub in self.sub_properties:
@ -464,29 +578,6 @@ class Shorthand(object):
def type():
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):
def __init__(self, name, original, gecko_pref):
@ -500,8 +591,7 @@ class Alias(object):
self.servo_2020_pref = original.servo_2020_pref
self.gecko_pref = gecko_pref
self.transitionable = original.transitionable
self.allowed_in_page_rule = original.allowed_in_page_rule
self.allowed_in_keyframe_block = original.allowed_in_keyframe_block
self.rule_types_allowed = original.rule_types_allowed
@staticmethod
def type():
@ -580,7 +670,9 @@ class PropertiesData(object):
self.shorthands = []
self.shorthands_by_name = {}
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):
style_struct = StyleStruct(*args, **kwargs)
@ -595,7 +687,7 @@ class PropertiesData(object):
# See servo/servo#14941.
if self.engine == "gecko":
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):
engines = engines.split()
@ -604,13 +696,15 @@ class PropertiesData(object):
longhand = Longhand(self.current_style_struct, name, **kwargs)
self.add_prefixed_aliases(longhand)
longhand.alias = [Alias(xp[0], longhand, xp[1]) for xp in longhand.alias]
self.longhand_aliases += longhand.alias
longhand.aliases = [Alias(xp[0], longhand, xp[1]) for xp in longhand.aliases]
self.longhand_aliases += longhand.aliases
self.current_style_struct.longhands.append(longhand)
self.longhands.append(longhand)
self.longhands_by_name[name] = longhand
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
@ -622,8 +716,8 @@ class PropertiesData(object):
sub_properties = [self.longhands_by_name[s] for s in sub_properties]
shorthand = Shorthand(name, sub_properties, *args, **kwargs)
self.add_prefixed_aliases(shorthand)
shorthand.alias = [Alias(xp[0], shorthand, xp[1]) for xp in shorthand.alias]
self.shorthand_aliases += shorthand.alias
shorthand.aliases = [Alias(xp[0], shorthand, xp[1]) for xp in shorthand.aliases]
self.shorthand_aliases += shorthand.aliases
self.shorthands.append(shorthand)
self.shorthands_by_name[name] = shorthand
return shorthand
@ -686,31 +780,31 @@ class PropertyRestrictions:
# https://drafts.csswg.org/css-pseudo/#first-letter-styling
@staticmethod
def first_letter(data):
props = set([
"color",
"opacity",
"float",
"initial-letter",
# Kinda like css-fonts?
"-moz-osx-font-smoothing",
# Kinda like css-text?
"-webkit-text-stroke-width",
"-webkit-text-fill-color",
"-webkit-text-stroke-color",
"vertical-align",
"line-height",
# Kinda like css-backgrounds?
"background-blend-mode",
] + PropertyRestrictions.shorthand(data, "padding")
+ PropertyRestrictions.shorthand(data, "margin")
+ PropertyRestrictions.spec(data, "css-fonts")
+ PropertyRestrictions.spec(data, "css-backgrounds")
+ PropertyRestrictions.spec(data, "css-text")
+ PropertyRestrictions.spec(data, "css-shapes")
+ PropertyRestrictions.spec(data, "css-text-decor"))
props = set(
[
"color",
"opacity",
"float",
"initial-letter",
# Kinda like css-fonts?
"-moz-osx-font-smoothing",
# Kinda like css-text?
"-webkit-text-stroke-width",
"-webkit-text-fill-color",
"-webkit-text-stroke-color",
"vertical-align",
"line-height",
# Kinda like css-backgrounds?
"background-blend-mode",
]
+ PropertyRestrictions.shorthand(data, "padding")
+ PropertyRestrictions.shorthand(data, "margin")
+ PropertyRestrictions.spec(data, "css-fonts")
+ PropertyRestrictions.spec(data, "css-backgrounds")
+ PropertyRestrictions.spec(data, "css-text")
+ PropertyRestrictions.spec(data, "css-shapes")
+ PropertyRestrictions.spec(data, "css-text-decor")
)
_add_logical_props(data, props)
@ -720,27 +814,27 @@ class PropertyRestrictions:
# https://drafts.csswg.org/css-pseudo/#first-line-styling
@staticmethod
def first_line(data):
props = set([
# Per spec.
"color",
"opacity",
# Kinda like css-fonts?
"-moz-osx-font-smoothing",
# Kinda like css-text?
"-webkit-text-stroke-width",
"-webkit-text-fill-color",
"-webkit-text-stroke-color",
"vertical-align",
"line-height",
# Kinda like css-backgrounds?
"background-blend-mode",
] + PropertyRestrictions.spec(data, "css-fonts")
+ PropertyRestrictions.spec(data, "css-backgrounds")
+ PropertyRestrictions.spec(data, "css-text")
+ PropertyRestrictions.spec(data, "css-text-decor"))
props = set(
[
# Per spec.
"color",
"opacity",
# Kinda like css-fonts?
"-moz-osx-font-smoothing",
# Kinda like css-text?
"-webkit-text-stroke-width",
"-webkit-text-fill-color",
"-webkit-text-stroke-color",
"vertical-align",
"line-height",
# Kinda like css-backgrounds?
"background-blend-mode",
]
+ PropertyRestrictions.spec(data, "css-fonts")
+ PropertyRestrictions.spec(data, "css-backgrounds")
+ PropertyRestrictions.spec(data, "css-text")
+ PropertyRestrictions.spec(data, "css-text-decor")
)
# These are probably Gecko bugs and should be supported per spec.
for prop in PropertyRestrictions.shorthand(data, "border"):
@ -770,39 +864,46 @@ class PropertyRestrictions:
# https://drafts.csswg.org/css-pseudo/#marker-pseudo
@staticmethod
def marker(data):
return set([
"color",
"text-combine-upright",
"unicode-bidi",
"direction",
"content",
"-moz-osx-font-smoothing",
] + PropertyRestrictions.spec(data, "css-fonts"))
return set(
[
"color",
"text-combine-upright",
"text-transform",
"unicode-bidi",
"direction",
"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
@staticmethod
def cue(data):
return set([
"color",
"opacity",
"visibility",
"text-shadow",
"white-space",
"text-combine-upright",
"ruby-position",
# XXX Should these really apply to cue?
"font-synthesis",
"-moz-osx-font-smoothing",
# FIXME(emilio): background-blend-mode should be part of the
# background shorthand, and get reset, per
# https://drafts.fxtf.org/compositing/#background-blend-mode
"background-blend-mode",
] + PropertyRestrictions.shorthand(data, "text-decoration")
+ PropertyRestrictions.shorthand(data, "background")
+ PropertyRestrictions.shorthand(data, "outline")
+ PropertyRestrictions.shorthand(data, "font"))
return set(
[
"color",
"opacity",
"visibility",
"text-shadow",
"white-space",
"text-combine-upright",
"ruby-position",
# XXX Should these really apply to cue?
"font-synthesis",
"-moz-osx-font-smoothing",
# FIXME(emilio): background-blend-mode should be part of the
# background shorthand, and get reset, per
# https://drafts.fxtf.org/compositing/#background-blend-mode
"background-blend-mode",
]
+ PropertyRestrictions.shorthand(data, "text-decoration")
+ PropertyRestrictions.shorthand(data, "background")
+ PropertyRestrictions.shorthand(data, "outline")
+ PropertyRestrictions.shorthand(data, "font")
)
class CountedUnknownProperty:

View file

@ -14,7 +14,7 @@ use crate::parser::ParserContext;
use crate::properties::animated_properties::{AnimationValue, AnimationValueMap};
use crate::selector_parser::SelectorImpl;
use crate::shared_lock::Locked;
use crate::str::{CssString, CssStringBorrow, CssStringWriter};
use crate::str::{CssString, CssStringWriter};
use crate::stylesheets::{CssRuleType, Origin, UrlExtraData};
use crate::values::computed::Context;
use cssparser::{parse_important, CowRcStr, DeclarationListParser, ParserInput};
@ -348,21 +348,6 @@ impl PropertyDeclarationBlock {
.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
/// block.
pub fn shorthand_to_css(
@ -844,14 +829,16 @@ impl PropertyDeclarationBlock {
// getKeyframes() implementation for CSS animations, if
// |computed_values| is supplied, we use it to expand such variable
// 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
.value
.substitute_variables(
declaration.id,
computed_values.writing_mode,
custom_properties.as_ref(),
QuirksMode::NoQuirks,
device,
&mut Default::default()
)
.to_css(dest)
},
@ -945,7 +932,7 @@ impl PropertyDeclarationBlock {
let mut already_serialized = NonCustomPropertyIdSet::new();
// Step 3
for (declaration, importance) in self.declaration_importance_iter() {
'declaration_loop: for (declaration, importance) in self.declaration_importance_iter() {
// Step 3.1
let property = declaration.id();
let longhand_id = match property {
@ -971,11 +958,7 @@ impl PropertyDeclarationBlock {
continue;
}
// Step 3.3
// Step 3.3.1 is done by checking already_serialized while
// iterating below.
// Step 3.3.2
// Steps 3.3 & 3.4
for shorthand in longhand_id.shorthands() {
// We already attempted to serialize this shorthand before.
if already_serialized.contains(shorthand.into()) {
@ -987,74 +970,105 @@ impl PropertyDeclarationBlock {
continue;
}
// Substep 2 & 3
let mut current_longhands = SmallVec::<[_; 10]>::new();
let mut important_count = 0;
let mut found_system = None;
let is_system_font = shorthand == ShorthandId::Font &&
self.declarations.iter().any(|l| match l.id() {
PropertyDeclarationId::Longhand(id) => {
if already_serialized.contains(id.into()) {
return false;
}
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;
// Step 3.3.1:
// Let longhands be an array consisting of all CSS
// declarations in declaration blocks declarations that
// that are not in already serialized and have a property
// name that maps to one of the shorthand properties in
// shorthands.
let longhands = {
// TODO(emilio): This could just index in an array if we
// remove pref-controlled longhands.
let mut ids = LonghandIdSet::new();
for longhand in shorthand.longhands() {
match self.get(PropertyDeclarationId::Longhand(longhand)) {
Some((declaration, importance)) => {
current_longhands.push(declaration);
if importance.important() {
important_count += 1;
}
},
None => {
contains_all_longhands = false;
break;
},
}
ids.insert(longhand);
}
ids
};
// Substep 1:
if !contains_all_longhands {
continue;
// Step 3.4.2
// If all properties that map to shorthand are not present
// 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;
if is_important && important_count != current_longhands.len() {
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 {
Importance::Important
} else {
Importance::Normal
};
// Substep 5 - Let value be the result of invoking serialize
// a CSS value of current longhands.
// 3.4.7:
// Let value be the result of invoking serialize a CSS value
// of current longhands.
let appendable_value = match shorthand
.get_shorthand_appendable_value(current_longhands.iter().cloned())
{
@ -1065,44 +1079,41 @@ impl PropertyDeclarationBlock {
// We avoid re-serializing if we're already an
// AppendableValue::Css.
let mut v = CssString::new();
let value = match (appendable_value, found_system) {
(
AppendableValue::Css {
css,
with_variables,
},
_,
) => {
let value = match appendable_value {
AppendableValue::Css { css, with_variables } => {
debug_assert!(!css.is_empty());
AppendableValue::Css {
css: css,
with_variables: with_variables,
}
AppendableValue::Css { css, with_variables }
},
#[cfg(feature = "gecko")]
(_, Some(sys)) => {
sys.to_css(&mut CssWriter::new(&mut v))?;
AppendableValue::Css {
css: CssStringBorrow::from(&v),
with_variables: false,
}
},
(other, _) => {
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() {
continue;
}
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,
}
},
};
// 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<_>>, _>(
dest,
&shorthand,
@ -1111,6 +1122,9 @@ impl PropertyDeclarationBlock {
&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 {
let longhand_id = match current_longhand.id() {
PropertyDeclarationId::Longhand(id) => id,
@ -1121,24 +1135,21 @@ impl PropertyDeclarationBlock {
already_serialized.insert(longhand_id.into());
}
// FIXME(https://github.com/w3c/csswg-drafts/issues/1774)
// The specification does not include an instruction to abort
// the shorthand loop at this point, but doing so both matches
// Gecko and makes sense since shorthands are checked in
// preferred order.
break;
// 3.4.12:
// Continue with the steps labeled declaration loop.
continue 'declaration_loop;
}
// Step 3.3.4
if already_serialized.contains(longhand_id.into()) {
continue;
}
// Steps 3.3.5, 3.3.6 & 3.3.7
// Need to specify an iterator type here even though its unused to work around
// "error: unable to infer enough type information about `_`;
// type annotations or generic parameter binding required [E0282]"
// Use the same type as earlier call to reuse generated code.
// Steps 3.5, 3.6 & 3.7:
// Let value be the result of invoking serialize a CSS value of
// declaration.
//
// Let serialized declaration be the result of invoking
// serialize a CSS declaration with property name property,
// value value, and the important flag set if declaration has
// its important flag set.
//
// Append serialized declaration to list.
append_serialization::<Cloned<slice::Iter<_>>, _>(
dest,
&property,
@ -1147,7 +1158,8 @@ impl PropertyDeclarationBlock {
&mut is_first_serialization,
)?;
// Step 3.3.8
// Step 3.8:
// Append property to already serialized.
already_serialized.insert(longhand_id.into());
}
@ -1173,7 +1185,7 @@ where
/// or when storing a serialized shorthand value before appending directly.
Css {
/// The raw CSS string.
css: CssStringBorrow<'a>,
css: &'a str,
/// Whether the original serialization contained variables or not.
with_variables: bool,
},
@ -1201,7 +1213,7 @@ where
I: Iterator<Item = &'a PropertyDeclaration>,
{
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::DeclarationsForShorthand(shorthand, decls) => {
shorthand.longhands_to_css(decls, &mut CssWriter::new(dest))
@ -1263,11 +1275,12 @@ pub fn parse_style_attribute(
url_data: &UrlExtraData,
error_reporter: Option<&dyn ParseErrorReporter>,
quirks_mode: QuirksMode,
rule_type: CssRuleType,
) -> PropertyDeclarationBlock {
let context = ParserContext::new(
Origin::Author,
url_data,
Some(CssRuleType::Style),
Some(rule_type),
ParsingMode::DEFAULT,
quirks_mode,
error_reporter,
@ -1287,15 +1300,17 @@ pub fn parse_one_declaration_into(
declarations: &mut SourcePropertyDeclaration,
id: PropertyId,
input: &str,
origin: Origin,
url_data: &UrlExtraData,
error_reporter: Option<&dyn ParseErrorReporter>,
parsing_mode: ParsingMode,
quirks_mode: QuirksMode,
rule_type: CssRuleType,
) -> Result<(), ()> {
let context = ParserContext::new(
Origin::Author,
origin,
url_data,
Some(CssRuleType::Style),
Some(rule_type),
parsing_mode,
quirks_mode,
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>
</%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,
round_to_pixels=False)">
#[allow(non_snake_case)]
@ -593,11 +581,6 @@ impl Clone for ${style_struct.gecko_struct_name} {
longhands = [x for x in style_struct.longhands
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):
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)
if "font" in longhand.ident:
args.update(cast_type=longhand.cast_type)
elif longhand.predefined_type in predefined_types:
method = predefined_types[longhand.predefined_type]
else:
method = impl_simple
@ -920,9 +901,10 @@ fn static_assert() {
}
pub fn unzoom_fonts(&mut self, device: &Device) {
self.gecko.mSize = device.unzoom_text(Au(self.gecko.mSize)).0;
self.gecko.mScriptUnconstrainedSize = device.unzoom_text(Au(self.gecko.mScriptUnconstrainedSize)).0;
self.gecko.mFont.size = device.unzoom_text(Au(self.gecko.mFont.size)).0;
use crate::values::generics::NonNegative;
self.gecko.mSize = NonNegative(device.unzoom_text(self.gecko.mSize.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) {
@ -942,25 +924,27 @@ fn static_assert() {
}
pub fn set_font_size(&mut self, v: FontSize) {
let size = Au::from(v.size());
self.gecko.mScriptUnconstrainedSize = size.0;
let size = v.size;
self.gecko.mScriptUnconstrainedSize = size;
// These two may be changed from Cascade::fixup_font_stuff.
self.gecko.mSize = size.0;
self.gecko.mFont.size = size.0;
self.gecko.mSize = size;
self.gecko.mFont.size = size;
self.gecko.mFontSizeKeyword = v.keyword_info.kw;
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 {
use crate::values::specified::font::KeywordInfo;
FontSize {
size: Au(self.gecko.mSize).into(),
size: self.gecko.mSize,
keyword_info: KeywordInfo {
kw: self.gecko.mFontSizeKeyword,
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 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 = [
"border-top-width",
"border-bottom-width",
@ -2114,6 +2100,7 @@ pub fn assert_initial_values_match(data: &PerDocumentStyleData) {
"font-family",
"font-size",
"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]
%>

View file

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

View file

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

View file

@ -24,7 +24,7 @@
"border-%s-color" % side_name, "Color",
"computed_value::T::currentcolor()",
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"),
animation_value_type="AnimatedColor",
logical=is_logical,
@ -37,12 +37,11 @@
"border-%s-style" % side_name, "BorderStyle",
"specified::BorderStyle::None",
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"),
animation_value_type="discrete" if not is_logical else "none",
logical=is_logical,
logical_group="border-style",
needs_context=False,
)}
${helpers.predefined_type(
@ -51,7 +50,7 @@
"crate::values::computed::NonNegativeLength::new(3.)",
engines="gecko servo-2013 servo-2020",
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"),
animation_value_type="NonNegativeLength",
logical=is_logical,

View file

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

View file

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

View file

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

View file

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

View file

@ -72,7 +72,6 @@ ${helpers.predefined_type(
"FillRule",
"Default::default()",
engines="gecko",
needs_context=False,
animation_value_type="discrete",
spec="https://www.w3.org/TR/SVG11/painting.html#FillRuleProperty",
)}
@ -165,7 +164,6 @@ ${helpers.predefined_type(
"FillRule",
"Default::default()",
engines="gecko",
needs_context=False,
animation_value_type="discrete",
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",
)}
${helpers.single_keyword(
${helpers.predefined_type(
"caption-side",
"top bottom",
"table::CaptionSide",
"computed::table::CaptionSide::Top",
engines="gecko servo-2013",
extra_gecko_values="right left top-outside bottom-outside",
needs_conversion="True",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-tables/#propdef-caption-side",
servo_restyle_damage="rebuild_and_reflow",
@ -46,5 +45,5 @@ ${helpers.predefined_type(
animation_value_type="BorderSpacing",
boxed=True,
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",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-size-adjust/#adjustment-control",
alias="-webkit-text-size-adjust",
aliases="-webkit-text-size-adjust",
)}
${helpers.predefined_type(
@ -82,8 +82,7 @@ ${helpers.predefined_type(
servo_2020_pref="layout.2020.unimplemented",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text/#propdef-overflow-wrap",
alias="word-wrap",
needs_context=False,
aliases="word-wrap",
servo_restyle_damage="rebuild_and_reflow",
)}
@ -95,7 +94,6 @@ ${helpers.predefined_type(
servo_2020_pref="layout.2020.unimplemented",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text/#propdef-word-break",
needs_context=False,
servo_restyle_damage="rebuild_and_reflow",
)}
@ -109,8 +107,6 @@ ${helpers.predefined_type(
extra_specified="${'distribute' if engine == 'gecko' else ''}"
gecko_enum_prefix="StyleTextJustify"
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"
servo_restyle_damage="rebuild_and_reflow"
>
@ -145,7 +141,6 @@ ${helpers.predefined_type(
"text-align-last",
"TextAlignLast",
"computed::text::TextAlignLast::Auto",
needs_context=False,
engines="gecko",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text/#propdef-text-align-last",
@ -288,7 +283,6 @@ ${helpers.predefined_type(
engines="gecko",
animation_value_type="discrete",
spec="https://drafts.csswg.org/css-text-3/#line-break-property",
needs_context=False,
)}
// CSS Compatibility
@ -352,7 +346,7 @@ ${helpers.single_keyword(
"text-combine-upright",
"none all",
engines="gecko",
animation_value_type="discrete",
animation_value_type="none",
spec="https://drafts.csswg.org/css-writing-modes-3/#text-combine-upright",
)}
@ -385,8 +379,6 @@ ${helpers.predefined_type(
"computed::LengthPercentageOrAuto::auto()",
engines="gecko",
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",
)}
@ -397,8 +389,6 @@ ${helpers.predefined_type(
"computed::TextUnderlinePosition::AUTO",
engines="gecko",
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",
)}
@ -408,9 +398,6 @@ ${helpers.predefined_type(
"TextDecorationSkipInk",
"computed::TextDecorationSkipInk::Auto",
engines="gecko",
needs_context=False,
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",
)}

View file

@ -29,6 +29,17 @@ ${helpers.single_keyword(
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(
"-moz-user-input",
"auto none",

View file

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

View file

@ -3,7 +3,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
<%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) %>
% for side in ALL_SIDES:
@ -17,13 +17,13 @@
"LengthPercentageOrAuto",
"computed::LengthPercentageOrAuto::zero()",
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",
animation_value_type="ComputedValue",
logical=side[1],
logical_group="margin",
spec=spec,
allowed_in_page_rule=True,
rule_types_allowed=DEFAULT_RULES_AND_PAGE,
servo_restyle_damage="reflow"
)}
% endfor

View file

@ -17,7 +17,7 @@
"NonNegativeLengthPercentage",
"computed::NonNegativeLengthPercentage::zero()",
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",
logical=side[1],
logical_group="padding",

View file

@ -30,7 +30,6 @@
"computed::LengthPercentageOrAuto::auto()",
engines="gecko servo-2013 servo-2020",
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",
logical=True,
logical_group="inset",
@ -433,7 +432,7 @@ ${helpers.predefined_type(
"length::NonNegativeLengthPercentageOrNormal",
"computed::length::NonNegativeLengthPercentageOrNormal::normal()",
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",
servo_2013_pref="layout.columns.enabled",
spec="https://drafts.csswg.org/css-align-3/#propdef-column-gap",
@ -447,7 +446,7 @@ ${helpers.predefined_type(
"length::NonNegativeLengthPercentageOrNormal",
"computed::length::NonNegativeLengthPercentageOrNormal::normal()",
engines="gecko",
alias="grid-row-gap",
aliases="grid-row-gap",
spec="https://drafts.csswg.org/css-align-3/#propdef-row-gap",
animation_value_type="NonNegativeLengthPercentageOrNormal",
servo_restyle_damage="reflow",
@ -458,7 +457,7 @@ ${helpers.predefined_type(
"AspectRatio",
"computed::AspectRatio::auto()",
engines="gecko servo-2013",
animation_value_type="discrete",
animation_value_type="ComputedValue",
spec="https://drafts.csswg.org/css-sizing-4/#aspect-ratio",
gecko_pref="layout.css.aspect-ratio.enabled",
servo_restyle_damage="reflow",

View file

@ -75,6 +75,5 @@ ${helpers.predefined_type(
engines="gecko",
initial_specified_value="generics::text::GenericTextDecorationLength::Auto",
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"
)}

View file

@ -37,7 +37,6 @@ ${helpers.predefined_type(
engines="gecko",
extra_prefixes="moz webkit",
animation_value_type="discrete",
needs_context=False,
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_enum_prefix="StyleBoxAlign",
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)",
)}
@ -26,7 +26,7 @@ ${helpers.single_keyword(
gecko_ffi_name="mBoxDirection",
gecko_enum_prefix="StyleBoxDirection",
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)",
)}
@ -37,7 +37,7 @@ ${helpers.predefined_type(
engines="gecko",
gecko_ffi_name="mBoxFlex",
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)",
)}
@ -49,7 +49,7 @@ ${helpers.single_keyword(
gecko_aliases="inline-axis=horizontal block-axis=vertical",
gecko_enum_prefix="StyleBoxOrient",
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)",
)}
@ -60,7 +60,7 @@ ${helpers.single_keyword(
gecko_ffi_name="mBoxPack",
gecko_enum_prefix="StyleBoxPack",
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)",
)}
@ -72,7 +72,7 @@ ${helpers.predefined_type(
"1",
engines="gecko",
parse_method="parse_non_negative",
alias="-webkit-box-ordinal-group",
aliases="-webkit-box-ordinal-group",
gecko_ffi_name="mBoxOrdinal",
animation_value_type="discrete",
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 malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use crate::computed_value_flags::*;
use crate::hash::FxHashMap;
use crate::media_queries::Device;
use crate::parser::ParserContext;
use crate::properties::longhands::system_font::SystemFont;
use crate::selector_parser::PseudoElement;
use selectors::parser::SelectorParseErrorKind;
#[cfg(feature = "servo")] use servo_config::prefs;
use style_traits::{CssWriter, KeywordsCollectFn, ParseError, ParsingMode};
use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss};
@ -46,7 +46,7 @@ use crate::values::computed::NonNegativeLength;
use crate::values::serialize_atom_name;
use crate::rule_tree::StrongRuleNode;
use crate::Zero;
use crate::str::{CssString, CssStringBorrow, CssStringWriter};
use crate::str::{CssString, CssStringWriter};
use std::cell::Cell;
pub use self::declaration_block::*;
@ -54,7 +54,8 @@ pub use self::cascade::*;
<%!
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
%>
@ -542,31 +543,33 @@ impl NonCustomPropertyId {
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!(
matches!(
context.rule_type(),
rule_type,
CssRuleType::Keyframe | CssRuleType::Page | CssRuleType::Style
),
"Declarations are only expected inside a keyframe, page, or style rule."
);
${static_non_custom_property_id_set(
"DISALLOWED_IN_KEYFRAME_BLOCK",
lambda p: not p.allowed_in_keyframe_block
)}
${static_non_custom_property_id_set(
"DISALLOWED_IN_PAGE_RULE",
lambda p: not p.allowed_in_page_rule
)}
match context.rule_type() {
CssRuleType::Keyframe if DISALLOWED_IN_KEYFRAME_BLOCK.contains(self) => {
return false;
}
CssRuleType::Page if DISALLOWED_IN_PAGE_RULE.contains(self) => {
return false;
}
_ => {}
static MAP: [u8; NON_CUSTOM_PROPERTY_ID_COUNT] = [
% for property in data.longhands + data.shorthands + data.all_aliases():
${property.rule_types_allowed},
% endfor
];
match rule_type {
% for name in RULE_VALUES:
CssRuleType::${name} => MAP[self.0] & ${RULE_VALUES[name]} != 0,
% endfor
_ => true
}
}
fn allowed_in(self, context: &ParserContext) -> bool {
if !self.allowed_in_rule(context.rule_type()) {
return false;
}
self.allowed_in_ignoring_rule_type(context)
@ -788,14 +791,44 @@ static ${name}: LonghandIdSet = LonghandIdSet {
/// A group for properties which may override each other
/// via logical resolution.
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
#[repr(u8)]
pub enum LogicalGroup {
% for group in sorted(logical_groups.keys()):
% for i, group in enumerate(logical_groups.keys()):
/// ${group}
${to_camel_case(group)},
${to_camel_case(group)} = ${i},
% 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
#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq)]
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>(
&self,
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<PropertyDeclaration, ParseError<'i>> {
match *self {
% for property in data.longhands:
LonghandId::${property.camel_case} => {
longhands::${property.ident}::parse_declared(context, input)
}
% endfor
}
type ParsePropertyFn = for<'i, 't> fn(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<PropertyDeclaration, ParseError<'i>>;
static PARSE_PROPERTY: [ParsePropertyFn; ${len(data.longhands)}] = [
% for property in data.longhands:
longhands::${property.ident}::parse_declared,
% endfor
];
(PARSE_PROPERTY[*self as usize])(context, input)
}
/// Returns whether this property is animatable.
@ -1338,8 +1372,8 @@ impl LonghandId {
// preferences properly, see bug 1165538.
LonghandId::MozMinFontSizeRatio |
// Needed to do font-size for MathML. :(
LonghandId::MozScriptLevel |
// font-size depends on math-depth's computed value.
LonghandId::MathDepth |
% endif
// 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
if let Some(css) = first_declaration.with_variables_from_shorthand(self) {
if declarations2.all(|d| d.with_variables_from_shorthand(self) == Some(css)) {
return Some(AppendableValue::Css {
css: CssStringBorrow::from(css),
with_variables: true,
});
return Some(AppendableValue::Css { css, with_variables: true });
}
return None;
}
@ -1507,7 +1538,7 @@ impl ShorthandId {
if let Some(keyword) = first_declaration.get_css_wide_keyword() {
if declarations2.all(|d| d.get_css_wide_keyword() == Some(keyword)) {
return Some(AppendableValue::Css {
css: CssStringBorrow::from(keyword.to_str()),
css: keyword.to_str(),
with_variables: false,
});
}
@ -1567,15 +1598,36 @@ impl ShorthandId {
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<(), ParseError<'i>> {
match *self {
% for shorthand in data.shorthands_except_all():
ShorthandId::${shorthand.camel_case} => {
shorthands::${shorthand.ident}::parse_into(declarations, context, input)
}
% endfor
// 'all' accepts no value other than CSS-wide keywords
ShorthandId::All => Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError))
type ParseIntoFn = for<'i, 't> fn(
declarations: &mut SourcePropertyDeclaration,
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<(), ParseError<'i>>;
fn unreachable<'i, 't>(
_: &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 {
fn substitute_variables(
fn substitute_variables<'cache>(
&self,
longhand_id: LonghandId,
writing_mode: WritingMode,
custom_properties: Option<<&Arc<crate::custom_properties::CustomPropertiesMap>>,
quirks_mode: QuirksMode,
device: &Device,
) -> PropertyDeclaration {
shorthand_cache: &'cache mut ShorthandsWithPropertyReferencesCache,
) -> Cow<'cache, PropertyDeclaration> {
let invalid_at_computed_value_time = || {
let keyword = if longhand_id.inherited() {
CSSWideKeyword::Inherit
} else {
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(
&self.css,
self.first_token_type,
@ -1656,53 +1730,34 @@ impl UnparsedValue {
let mut input = Parser::new(&mut input);
input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less.
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| {
match self.from_shorthand {
None => longhand_id.parse_value(&context, input),
Some(ShorthandId::All) => {
// No need to parse the 'all' shorthand as anything other
// than a CSS-wide keyword, after variable substitution.
Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent("all".into())))
let shorthand = match self.from_shorthand {
None => {
return match input.parse_entirely(|input| longhand_id.parse_value(&context, input)) {
Ok(decl) => Cow::Owned(decl),
Err(..) => invalid_at_computed_value_time(),
}
% for shorthand in data.shorthands_except_all():
Some(ShorthandId::${shorthand.camel_case}) => {
shorthands::${shorthand.ident}::parse_value(&context, input)
.map(|longhands| {
match longhand_id {
<% seen = set() %>
% for property in shorthand.sub_properties:
// When animating logical properties, we end up
// physicalizing the value during the animation, but
// the value still comes from the logical shorthand.
//
// So we need to handle the physical properties too.
% for prop in [property] + property.all_physical_mapped_properties(data):
% if prop.camel_case not in seen:
LonghandId::${prop.camel_case} => {
PropertyDeclaration::${prop.camel_case}(
longhands.${property.ident}
)
}
<% seen.add(prop.camel_case) %>
% endif
% endfor
% endfor
<% del seen %>
_ => unreachable!()
}
})
}
% endfor
},
Some(shorthand) => shorthand,
};
let mut decls = SourcePropertyDeclaration::new();
// parse_into takes care of doing `parse_entirely` for us.
if shorthand.parse_into(&mut decls, &context, &mut input).is_err() {
return invalid_at_computed_value_time();
}
for declaration in decls.declarations.drain(..) {
let longhand = declaration.id().as_longhand().unwrap();
if longhand.is_logical() {
shorthand_cache.insert((shorthand, longhand.to_physical(writing_mode)), declaration.clone());
}
});
match declaration {
Ok(decl) => decl,
Err(..) => invalid_at_computed_value_time(),
shorthand_cache.insert((shorthand, longhand), declaration);
}
Cow::Borrowed(&shorthand_cache[&(shorthand, longhand_id)])
}
}
@ -1897,7 +1952,7 @@ impl PropertyId {
% for (kind, properties) in [("Longhand", data.longhands), ("Shorthand", data.shorthands)]:
% for property in properties:
"${property.name}" => StaticId::${kind}(${kind}Id::${property.camel_case}),
% for alias in property.alias:
% for alias in property.aliases:
"${alias.name}" => {
StaticId::${kind}Alias(
${kind}Id::${property.camel_case},
@ -2413,7 +2468,7 @@ impl PropertyDeclaration {
id,
value: Arc::new(UnparsedValue {
css: css.into_owned(),
first_token_type: first_token_type,
first_token_type,
url_data: context.url_data.clone(),
from_shorthand: None,
}),
@ -2450,7 +2505,7 @@ impl PropertyDeclaration {
crate::custom_properties::parse_non_custom_with_var(input)?;
let unparsed = Arc::new(UnparsedValue {
css: css.into_owned(),
first_token_type: first_token_type,
first_token_type,
url_data: context.url_data.clone(),
from_shorthand: Some(id),
});
@ -2962,7 +3017,7 @@ impl ComputedValues {
/// Returns the visited style, if any.
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.
@ -2975,6 +3030,27 @@ impl ComputedValues {
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:
/// Gets the computed value of a given property.
#[inline(always)]
@ -3621,11 +3697,11 @@ impl<'a> StyleBuilder<'a> {
self.add_flags(ComputedValueFlags::INHERITS_RESET_STYLE);
% if property.ident == "content":
self.add_flags(ComputedValueFlags::INHERITS_CONTENT);
self.add_flags(ComputedValueFlags::CONTENT_DEPENDS_ON_INHERITED_STYLE);
% endif
% if property.ident == "display":
self.add_flags(ComputedValueFlags::INHERITS_DISPLAY);
self.add_flags(ComputedValueFlags::DISPLAY_DEPENDS_ON_INHERITED_STYLE);
% endif
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 property in props:
% if property.enabled_in_content():
% for prop in [property] + property.alias:
% for prop in [property] + property.aliases:
% if '-' in prop.name:
[${prop.ident.capitalize()}, Set${prop.ident.capitalize()},
PropertyId::${kind}(${kind}Id::${property.camel_case})],

View file

@ -17,9 +17,7 @@ ${helpers.four_sides_shorthand(
${helpers.four_sides_shorthand(
"border-style",
"border-%s-style",
"specified::BorderStyle::parse",
engines="gecko servo-2013 servo-2020",
needs_context=False,
spec="https://drafts.csswg.org/css-backgrounds/#border-style",
)}
@ -114,7 +112,7 @@ pub fn parse_border<'i, 't>(
'border-%s-%s' % (side, prop)
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}">
pub fn parse_value<'i, 't>(

View file

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