mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Auto merge of #28217 - servo:gecko-sync, r=emilio,jdm
style: Sync changes from mozilla-central.
This commit is contained in:
commit
b196bfeeeb
206 changed files with 4276 additions and 3864 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -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",
|
||||
]
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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| ¶m.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()
|
||||
})
|
||||
},
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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(()) => {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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")
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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" }
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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" }
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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!(
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 haven’t 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 don’t 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(),
|
||||
|
|
|
@ -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`.
|
||||
///
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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("}")
|
||||
|
|
|
@ -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__":
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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>),
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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("}")
|
||||
}
|
||||
|
|
|
@ -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(),
|
||||
),
|
||||
];
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
||||
|
|
|
@ -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, _, _),
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) }))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
)?))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) }
|
||||
}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)?)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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__":
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 block’s 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 there’s 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 ¤t_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 it’s 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,
|
||||
|
|
|
@ -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]
|
||||
%>
|
||||
|
|
|
@ -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! {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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",
|
||||
)}
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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",
|
||||
)}
|
||||
|
||||
|
|
|
@ -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",
|
||||
)}
|
||||
|
|
|
@ -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",
|
||||
)}
|
||||
|
|
|
@ -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",
|
||||
)}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
)}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
)}
|
||||
|
|
|
@ -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",
|
||||
)}
|
||||
|
||||
|
|
|
@ -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)",
|
||||
|
|
|
@ -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})],
|
||||
|
|
|
@ -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>(
|
||||
|
|
|
@ -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
Loading…
Add table
Add a link
Reference in a new issue