diff options
53 files changed, 950 insertions, 1327 deletions
diff --git a/components/gfx/font.rs b/components/gfx/font.rs index 6a0000494c5..cd9513a3f43 100644 --- a/components/gfx/font.rs +++ b/components/gfx/font.rs @@ -367,7 +367,7 @@ impl FontGroup { let families = style .font_family - .0 + .families .iter() .map(|family| FontGroupFamily::new(descriptor.clone(), &family)) .collect(); diff --git a/components/gfx/tests/font_context.rs b/components/gfx/tests/font_context.rs index 2f3f12b97b9..3a3f73671ac 100644 --- a/components/gfx/tests/font_context.rs +++ b/components/gfx/tests/font_context.rs @@ -119,7 +119,10 @@ fn font_family(names: Vec<&str>) -> FontFamily { }) .collect(); - FontFamily(FontFamilyList::new(names.into_boxed_slice())) + FontFamily { + families: FontFamilyList::new(names.into_boxed_slice()), + is_system_font: false, + } } #[test] diff --git a/components/style/author_styles.rs b/components/style/author_styles.rs index 101c2e17349..37eb68ff4f7 100644 --- a/components/style/author_styles.rs +++ b/components/style/author_styles.rs @@ -77,7 +77,7 @@ where #[cfg(feature = "gecko")] unsafe impl HasFFI for AuthorStyles<crate::gecko::data::GeckoStyleSheet> { - type FFIType = crate::gecko_bindings::bindings::RawServoAuthorStyles; + type FFIType = crate::gecko_bindings::structs::RawServoAuthorStyles; } #[cfg(feature = "gecko")] unsafe impl HasSimpleFFI for AuthorStyles<crate::gecko::data::GeckoStyleSheet> {} diff --git a/components/style/bloom.rs b/components/style/bloom.rs index 4e6ae471e50..d75d548ff71 100644 --- a/components/style/bloom.rs +++ b/components/style/bloom.rs @@ -19,7 +19,7 @@ thread_local! { /// such that they can be reused across style traversals. StyleBloom is responsible /// for ensuring that the bloom filter is zeroed when it is dropped. static BLOOM_KEY: Arc<AtomicRefCell<BloomFilter>> = - Arc::new(AtomicRefCell::new(BloomFilter::new())); + Arc::new(AtomicRefCell::new(BloomFilter::new())); } /// A struct that allows us to fast-reject deep descendant selectors avoiding diff --git a/components/style/build_gecko.rs b/components/style/build_gecko.rs index 734d09569f5..4866909899c 100644 --- a/components/style/build_gecko.rs +++ b/components/style/build_gecko.rs @@ -32,7 +32,6 @@ mod bindings { use toml::value::Table; const STRUCTS_FILE: &'static str = "structs.rs"; - const BINDINGS_FILE: &'static str = "bindings.rs"; fn read_config(path: &PathBuf) -> Table { println!("cargo:rerun-if-changed={}", path.to_str().unwrap()); @@ -128,9 +127,6 @@ mod bindings { trait BuilderExt { fn get_initial_builder() -> Builder; fn include<T: Into<String>>(self, file: T) -> Builder; - fn zero_size_type(self, ty: &str, structs_list: &HashSet<&str>) -> Builder; - fn borrowed_type(self, ty: &str) -> Builder; - fn mutable_borrowed_type(self, ty: &str) -> Builder; } impl BuilderExt for Builder { @@ -181,42 +177,6 @@ mod bindings { fn include<T: Into<String>>(self, file: T) -> Builder { self.clang_arg("-include").clang_arg(file) } - // This makes an FFI-safe void type that can't be matched on - // &VoidType is UB to have, because you can match on it - // to produce a reachable unreachable. If it's wrapped in - // a struct as a private field it becomes okay again - // - // Not 100% sure of how safe this is, but it's what we're using - // in the XPCOM ffi too - // https://github.com/nikomatsakis/rust-memory-model/issues/2 - fn zero_size_type(self, ty: &str, structs_list: &HashSet<&str>) -> Builder { - if !structs_list.contains(ty) { - self.blacklist_type(ty) - .raw_line(format!("enum {}Void {{ }}", ty)) - .raw_line(format!("pub struct {0}({0}Void);", ty)) - } else { - self - } - } - fn borrowed_type(self, ty: &str) -> Builder { - self.blacklist_type(format!("{}Borrowed", ty)) - .raw_line(format!("pub type {0}Borrowed<'a> = &'a {0};", ty)) - .blacklist_type(format!("{}BorrowedOrNull", ty)) - .raw_line(format!( - "pub type {0}BorrowedOrNull<'a> = Option<&'a {0}>;", - ty - )) - } - fn mutable_borrowed_type(self, ty: &str) -> Builder { - self.borrowed_type(ty) - .blacklist_type(format!("{}BorrowedMut", ty)) - .raw_line(format!("pub type {0}BorrowedMut<'a> = &'a mut {0};", ty)) - .blacklist_type(format!("{}BorrowedMutOrNull", ty)) - .raw_line(format!( - "pub type {0}BorrowedMutOrNull<'a> = Option<&'a mut {0}>;", - ty - )) - } } struct Fixup { @@ -259,58 +219,6 @@ mod bindings { .expect("Unable to write output"); } - fn get_types(filename: &str, macro_pat: &str) -> Vec<(String, String)> { - // Read the file - let path = DISTDIR_PATH.join("include/mozilla/").join(filename); - let mut list_file = File::open(path).expect(&format!("Unable to open {}", filename)); - let mut content = String::new(); - list_file - .read_to_string(&mut content) - .expect(&format!("Failed to read {}", filename)); - // Remove comments - let block_comment_re = Regex::new(r#"(?s)/\*.*?\*/"#).unwrap(); - let line_comment_re = Regex::new(r#"//.*"#).unwrap(); - let content = block_comment_re.replace_all(&content, ""); - let content = line_comment_re.replace_all(&content, ""); - // Extract the list - let re_string = format!(r#"^({})\(.+,\s*(\w+)\)$"#, macro_pat); - let re = Regex::new(&re_string).unwrap(); - content - .lines() - .map(|line| line.trim()) - .filter(|line| !line.is_empty()) - .map(|line| { - let captures = re - .captures(&line) - .expect(&format!("Unrecognized line in {}: '{}'", filename, line)); - let macro_name = captures.get(1).unwrap().as_str().to_string(); - let type_name = captures.get(2).unwrap().as_str().to_string(); - (macro_name, type_name) - }) - .collect() - } - - fn get_borrowed_types() -> Vec<(bool, String)> { - get_types("BorrowedTypeList.h", "GECKO_BORROWED_TYPE(?:_MUT)?") - .into_iter() - .map(|(macro_name, type_name)| (macro_name.ends_with("MUT"), type_name)) - .collect() - } - - fn get_arc_types() -> Vec<String> { - get_types("ServoArcTypeList.h", "SERVO_ARC_TYPE") - .into_iter() - .map(|(_, type_name)| type_name) - .collect() - } - - fn get_boxed_types() -> Vec<String> { - get_types("ServoBoxedTypeList.h", "SERVO_BOXED_TYPE") - .into_iter() - .map(|(_, type_name)| type_name) - .collect() - } - struct BuilderWithConfig<'a> { builder: Builder, config: &'a Table, @@ -386,10 +294,13 @@ mod bindings { fn generate_structs() { let builder = Builder::get_initial_builder() .enable_cxx_namespaces() - .with_codegen_config(CodegenConfig::TYPES | CodegenConfig::VARS); + .with_codegen_config( + CodegenConfig::TYPES | CodegenConfig::VARS | CodegenConfig::FUNCTIONS, + ); let mut fixups = vec![]; let builder = BuilderWithConfig::new(builder, CONFIG["structs"].as_table().unwrap()) .handle_common(&mut fixups) + .handle_str_items("whitelist-functions", |b, item| b.whitelist_function(item)) .handle_str_items("bitfield-enums", |b, item| b.bitfield_enum(item)) .handle_str_items("rusty-enums", |b, item| b.rustified_enum(item)) .handle_str_items("whitelist-vars", |b, item| b.whitelist_var(item)) @@ -480,89 +391,6 @@ mod bindings { } } - fn generate_bindings() { - let builder = Builder::get_initial_builder() - .disable_name_namespacing() - .with_codegen_config(CodegenConfig::FUNCTIONS); - let config = CONFIG["bindings"].as_table().unwrap(); - let mut structs_types = HashSet::new(); - let mut fixups = vec![]; - let mut builder = BuilderWithConfig::new(builder, config) - .handle_common(&mut fixups) - .handle_str_items("whitelist-functions", |b, item| b.whitelist_function(item)) - .handle_str_items("structs-types", |mut builder, ty| { - builder = builder - .blacklist_type(ty) - .raw_line(format!("use gecko_bindings::structs::{};", ty)); - structs_types.insert(ty); - // TODO this is hacky, figure out a better way to do it without - // hardcoding everything... - if ty.starts_with("nsStyle") { - builder = builder - .raw_line(format!("unsafe impl Send for {} {{}}", ty)) - .raw_line(format!("unsafe impl Sync for {} {{}}", ty)); - } - builder - }) - // TODO This was added due to servo/rust-bindgen#75, but - // that has been fixed in clang 4.0+. When we switch people - // to libclang 4.0, we can remove this. - .handle_table_items("array-types", |builder, item| { - let cpp_type = item["cpp-type"].as_str().unwrap(); - let rust_type = item["rust-type"].as_str().unwrap(); - builder.raw_line(format!( - concat!( - "pub type nsTArrayBorrowed_{}<'a> = ", - "&'a mut ::gecko_bindings::structs::nsTArray<{}>;" - ), - cpp_type, rust_type - )) - }) - .handle_str_items("servo-immutable-borrow-types", |b, ty| b.borrowed_type(ty)) - // Right now the only immutable borrow types are ones which we import - // from the |structs| module. As such, we don't need to create an opaque - // type with zero_size_type. If we ever introduce immutable borrow types - // which _do_ need to be opaque, we'll need a separate mode. - .handle_str_items("servo-borrow-types", |b, ty| b.mutable_borrowed_type(ty)) - .get_builder(); - for (is_mut, ty) in get_borrowed_types().iter() { - if *is_mut { - builder = builder.mutable_borrowed_type(ty); - } else { - builder = builder.borrowed_type(ty); - } - } - for ty in get_arc_types().iter() { - builder = builder - .blacklist_type(format!("{}Strong", ty)) - .raw_line(format!( - "pub type {0}Strong = ::gecko_bindings::sugar::ownership::Strong<{0}>;", - ty - )) - .borrowed_type(ty) - .zero_size_type(ty, &structs_types); - } - for ty in get_boxed_types().iter() { - builder = builder - .blacklist_type(format!("{}Owned", ty)) - .raw_line(format!( - "pub type {0}Owned = ::gecko_bindings::sugar::ownership::Owned<{0}>;", - ty - )) - .blacklist_type(format!("{}OwnedOrNull", ty)) - .raw_line(format!( - concat!( - "pub type {0}OwnedOrNull = ", - "::gecko_bindings::sugar::ownership::OwnedOrNull<{0}>;" - ), - ty - )) - .mutable_borrowed_type(ty) - .zero_size_type(ty, &structs_types); - } - write_binding_file(builder, BINDINGS_FILE, &fixups); - } - fn generate_atoms() { let script = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap()) .join("gecko") @@ -580,24 +408,9 @@ mod bindings { } pub fn generate() { - use std::thread; - macro_rules! run_tasks { - ($($task:expr,)+) => { - if setup_logging() { - $($task;)+ - } else { - let threads = vec![$( thread::spawn(|| $task) ),+]; - for thread in threads.into_iter() { - thread.join().unwrap(); - } - } - } - } - run_tasks! { - generate_structs(), - generate_bindings(), - generate_atoms(), - } + setup_logging(); + generate_structs(); + generate_atoms(); for path in ADDED_PATHS.lock().unwrap().iter() { println!("cargo:rerun-if-changed={}", path.to_str().unwrap()); diff --git a/components/style/cbindgen.toml b/components/style/cbindgen.toml index 5de390cc8a6..cd910f0be9b 100644 --- a/components/style/cbindgen.toml +++ b/components/style/cbindgen.toml @@ -103,6 +103,15 @@ include = [ "ZIndex", "TransformOrigin", "WordBreak", + "Contain", + "RestyleHint", + "TouchAction", + "WillChangeBits", + "TextDecorationLine", + "MozListReversed", + "Owned", + "OwnedOrNull", + "Strong", ] item_types = ["enums", "structs", "typedefs"] @@ -196,3 +205,35 @@ item_types = ["enums", "structs", "typedefs"] "GenericBorderRadius" = """ inline const StyleLengthPercentage& Get(mozilla::HalfCorner) const; """ + +"RestyleHint" = """ + static inline StyleRestyleHint RestyleSubtree(); + static inline StyleRestyleHint RecascadeSubtree(); + static inline StyleRestyleHint ForAnimations(); +""" + +# TODO(emilio): Add hooks to cbindgen to be able to generate MOZ_MUST_USE_TYPE +# or MOZ_MUST_USE on the functions. +"Owned" = """ + UniquePtr<GeckoType> Consume() { + UniquePtr<GeckoType> ret(ptr); + ptr = nullptr; + return ret; + } +""" + +"OwnedOrNull" = """ + UniquePtr<GeckoType> Consume() { + UniquePtr<GeckoType> ret(ptr); + ptr = nullptr; + return ret; + } +""" + +"Strong" = """ + already_AddRefed<GeckoType> Consume() { + already_AddRefed<GeckoType> ret(const_cast<GeckoType*>(ptr)); + ptr = nullptr; + return ret; + } +""" diff --git a/components/style/dom.rs b/components/style/dom.rs index 1c64543310c..28798e280b6 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -441,6 +441,11 @@ pub trait TElement: None } + /// The ::marker pseudo-element of this element, if it exists. + fn marker_pseudo_element(&self) -> Option<Self> { + None + } + /// Execute `f` for each anonymous content child (apart from ::before and /// ::after) whose originating element is `self`. fn each_anonymous_content_child<F>(&self, _f: F) diff --git a/components/style/font_face.rs b/components/style/font_face.rs index 85dea932173..12ebe3b9864 100644 --- a/components/style/font_face.rs +++ b/components/style/font_face.rs @@ -545,7 +545,6 @@ macro_rules! font_face_descriptors { } } -// css-name rust_identifier: Type, #[cfg(feature = "gecko")] font_face_descriptors! { mandatory descriptors = [ diff --git a/components/style/font_metrics.rs b/components/style/font_metrics.rs index 0c37a444658..15b5fd453e8 100644 --- a/components/style/font_metrics.rs +++ b/components/style/font_metrics.rs @@ -7,51 +7,42 @@ #![deny(missing_docs)] use crate::context::SharedStyleContext; -use crate::logical_geometry::WritingMode; -use crate::media_queries::Device; -use crate::properties::style_structs::Font; use crate::Atom; use app_units::Au; /// Represents the font metrics that style needs from a font to compute the /// value of certain CSS units like `ex`. -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, Default, PartialEq)] pub struct FontMetrics { /// The x-height of the font. - pub x_height: Au, + pub x_height: Option<Au>, /// The zero advance. This is usually writing mode dependent - pub zero_advance_measure: Au, + pub zero_advance_measure: Option<Au>, } -/// The result for querying font metrics for a given font family. +/// Type of font metrics to retrieve. #[derive(Clone, Debug, PartialEq)] -pub enum FontMetricsQueryResult { - /// The font is available, but we may or may not have found any font metrics - /// for it. - Available(FontMetrics), - /// The font is not available. - NotAvailable, +pub enum FontMetricsOrientation { + /// Get metrics for horizontal or vertical according to the Context's + /// writing mode. + MatchContext, + /// Force getting horizontal metrics. + Horizontal, } /// A trait used to represent something capable of providing us font metrics. pub trait FontMetricsProvider { /// Obtain the metrics for given font family. - /// - /// TODO: We could make this take the full list, I guess, and save a few - /// virtual calls in the case we are repeatedly unable to find font metrics? - /// That is not too common in practice though. fn query( &self, - _font: &Font, - _font_size: Au, - _wm: WritingMode, - _in_media_query: bool, - _device: &Device, - ) -> FontMetricsQueryResult { - FontMetricsQueryResult::NotAvailable + _context: &crate::values::computed::Context, + _base_size: crate::values::specified::length::FontBaseSize, + _orientation: FontMetricsOrientation, + ) -> FontMetrics { + Default::default() } - /// Get default size of a given language and generic family + /// Get default size of a given language and generic family. fn get_size(&self, font_name: &Atom, font_family: u8) -> Au; /// Construct from a shared style context diff --git a/components/style/gecko/arc_types.rs b/components/style/gecko/arc_types.rs index 95a37f0d905..309f2395085 100644 --- a/components/style/gecko/arc_types.rs +++ b/components/style/gecko/arc_types.rs @@ -9,32 +9,29 @@ #![allow(non_snake_case, missing_docs)] use crate::gecko::url::CssUrlData; -use crate::gecko_bindings::bindings::RawServoCounterStyleRule; -use crate::gecko_bindings::bindings::RawServoFontFeatureValuesRule; -use crate::gecko_bindings::bindings::RawServoImportRule; -use crate::gecko_bindings::bindings::RawServoKeyframe; -use crate::gecko_bindings::bindings::RawServoKeyframesRule; -use crate::gecko_bindings::bindings::RawServoMediaRule; -use crate::gecko_bindings::bindings::RawServoMozDocumentRule; -use crate::gecko_bindings::bindings::RawServoNamespaceRule; -use crate::gecko_bindings::bindings::RawServoPageRule; -use crate::gecko_bindings::bindings::RawServoRuleNode; -use crate::gecko_bindings::bindings::RawServoRuleNodeStrong; -use crate::gecko_bindings::bindings::RawServoSupportsRule; -use crate::gecko_bindings::bindings::ServoCssRules; use crate::gecko_bindings::structs::RawServoAnimationValue; +use crate::gecko_bindings::structs::RawServoCounterStyleRule; use crate::gecko_bindings::structs::RawServoCssUrlData; use crate::gecko_bindings::structs::RawServoDeclarationBlock; use crate::gecko_bindings::structs::RawServoFontFaceRule; +use crate::gecko_bindings::structs::RawServoFontFeatureValuesRule; +use crate::gecko_bindings::structs::RawServoImportRule; +use crate::gecko_bindings::structs::RawServoKeyframe; +use crate::gecko_bindings::structs::RawServoKeyframesRule; use crate::gecko_bindings::structs::RawServoMediaList; +use crate::gecko_bindings::structs::RawServoMediaRule; +use crate::gecko_bindings::structs::RawServoMozDocumentRule; +use crate::gecko_bindings::structs::RawServoNamespaceRule; +use crate::gecko_bindings::structs::RawServoPageRule; use crate::gecko_bindings::structs::RawServoQuotes; use crate::gecko_bindings::structs::RawServoStyleRule; use crate::gecko_bindings::structs::RawServoStyleSheetContents; +use crate::gecko_bindings::structs::RawServoSupportsRule; +use crate::gecko_bindings::structs::ServoCssRules; use crate::gecko_bindings::sugar::ownership::{HasArcFFI, HasFFI, Strong}; use crate::media_queries::MediaList; use crate::properties::animated_properties::AnimationValue; use crate::properties::{ComputedValues, PropertyDeclarationBlock}; -use crate::rule_tree::StrongRuleNode; use crate::shared_lock::Locked; use crate::stylesheets::keyframes_rule::Keyframe; use crate::stylesheets::{CounterStyleRule, CssRules, FontFaceRule, FontFeatureValuesRule}; @@ -121,31 +118,6 @@ impl_arc_ffi!(CssUrlData => RawServoCssUrlData impl_arc_ffi!(Box<[QuotePair]> => RawServoQuotes [Servo_Quotes_AddRef, Servo_Quotes_Release]); -// RuleNode is a Arc-like type but it does not use Arc. - -impl StrongRuleNode { - pub fn into_strong(self) -> RawServoRuleNodeStrong { - let ptr = self.ptr(); - mem::forget(self); - unsafe { mem::transmute(ptr) } - } - - pub fn from_ffi<'a>(ffi: &'a &RawServoRuleNode) -> &'a Self { - unsafe { &*(ffi as *const &RawServoRuleNode as *const StrongRuleNode) } - } -} - -#[no_mangle] -pub unsafe extern "C" fn Servo_RuleNode_AddRef(obj: &RawServoRuleNode) { - mem::forget(StrongRuleNode::from_ffi(&obj).clone()); -} - -#[no_mangle] -pub unsafe extern "C" fn Servo_RuleNode_Release(obj: &RawServoRuleNode) { - let ptr = StrongRuleNode::from_ffi(&obj); - ptr::read(ptr as *const StrongRuleNode); -} - // ComputedStyle is not an opaque type on any side of FFI. // This means that doing the HasArcFFI type trick is actually unsound, // since it gives us a way to construct an Arc<ComputedStyle> from diff --git a/components/style/gecko/conversions.rs b/components/style/gecko/conversions.rs index 64f0726b21e..356636fc6bb 100644 --- a/components/style/gecko/conversions.rs +++ b/components/style/gecko/conversions.rs @@ -12,8 +12,7 @@ use crate::gecko::values::GeckoStyleCoordConvertible; use crate::gecko_bindings::bindings; -use crate::gecko_bindings::structs::RawGeckoGfxMatrix4x4; -use crate::gecko_bindings::structs::{self, nsStyleCoord_CalcValue}; +use crate::gecko_bindings::structs::{self, nsStyleCoord_CalcValue, Matrix4x4Components}; use crate::gecko_bindings::structs::{nsStyleImage, nsresult, SheetType}; use crate::gecko_bindings::sugar::ns_style_coord::{CoordData, CoordDataMut, CoordDataValue}; use crate::stylesheets::{Origin, RulesMutateError}; @@ -986,8 +985,8 @@ pub unsafe fn string_from_chars_pointer(p: *const u16) -> String { String::from_utf16_lossy(char_vec) } -impl<'a> From<&'a RawGeckoGfxMatrix4x4> for Matrix3D { - fn from(m: &'a RawGeckoGfxMatrix4x4) -> Matrix3D { +impl<'a> From<&'a Matrix4x4Components> for Matrix3D { + fn from(m: &'a Matrix4x4Components) -> Matrix3D { Matrix3D { m11: m[0], m12: m[1], @@ -1009,8 +1008,8 @@ impl<'a> From<&'a RawGeckoGfxMatrix4x4> for Matrix3D { } } -impl From<Matrix3D> for RawGeckoGfxMatrix4x4 { - fn from(matrix: Matrix3D) -> RawGeckoGfxMatrix4x4 { +impl From<Matrix3D> for Matrix4x4Components { + fn from(matrix: Matrix3D) -> Self { [ matrix.m11, matrix.m12, matrix.m13, matrix.m14, matrix.m21, matrix.m22, matrix.m23, matrix.m24, matrix.m31, matrix.m32, matrix.m33, matrix.m34, matrix.m41, matrix.m42, diff --git a/components/style/gecko/data.rs b/components/style/gecko/data.rs index b70f376d7e6..ead7d88502b 100644 --- a/components/style/gecko/data.rs +++ b/components/style/gecko/data.rs @@ -6,8 +6,8 @@ use crate::context::QuirksMode; use crate::dom::TElement; -use crate::gecko_bindings::bindings::{self, RawServoStyleSet}; -use crate::gecko_bindings::structs::{self, ServoStyleSetSizes}; +use crate::gecko_bindings::bindings; +use crate::gecko_bindings::structs::{self, RawServoStyleSet, ServoStyleSetSizes}; use crate::gecko_bindings::structs::{StyleSheet as DomStyleSheet, StyleSheetInfo}; use crate::gecko_bindings::sugar::ownership::{HasArcFFI, HasBoxFFI, HasFFI, HasSimpleFFI}; use crate::invalidation::media_queries::{MediaListKey, ToMediaListKey}; diff --git a/components/style/gecko/media_features.rs b/components/style/gecko/media_features.rs index 9c48836b4d9..12417d38ef0 100644 --- a/components/style/gecko/media_features.rs +++ b/components/style/gecko/media_features.rs @@ -369,8 +369,8 @@ fn eval_prefers_color_scheme(device: &Device, query_value: Option<PrefersColorSc } } -/// https://drafts.csswg.org/mediaqueries-4/#mf-interaction bitflags! { + /// https://drafts.csswg.org/mediaqueries-4/#mf-interaction struct PointerCapabilities: u8 { const COARSE = structs::PointerCapabilities_Coarse; const FINE = structs::PointerCapabilities_Fine; diff --git a/components/style/gecko/pseudo_element.rs b/components/style/gecko/pseudo_element.rs index 8160ea2c9b8..479c12b9cde 100644 --- a/components/style/gecko/pseudo_element.rs +++ b/components/style/gecko/pseudo_element.rs @@ -33,7 +33,10 @@ impl ::selectors::parser::PseudoElement for PseudoElement { fn valid_after_slotted(&self) -> bool { matches!( *self, - PseudoElement::Before | PseudoElement::After | PseudoElement::Placeholder + PseudoElement::Before | + PseudoElement::After | + PseudoElement::Marker | + PseudoElement::Placeholder ) } @@ -109,6 +112,12 @@ impl PseudoElement { *self == PseudoElement::After } + /// Whether this pseudo-element is the ::marker pseudo. + #[inline] + pub fn is_marker(&self) -> bool { + *self == PseudoElement::Marker + } + /// Whether this pseudo-element is ::first-letter. #[inline] pub fn is_first_letter(&self) -> bool { @@ -180,6 +189,8 @@ impl PseudoElement { /// Whether this pseudo-element should actually exist if it has /// the given styles. pub fn should_exist(&self, style: &ComputedValues) -> bool { + debug_assert!(self.is_eager()); + if style.get_box().clone_display() == Display::None { return false; } diff --git a/components/style/gecko/pseudo_element_definition.mako.rs b/components/style/gecko/pseudo_element_definition.mako.rs index be156b54a14..e2dffcbdd74 100644 --- a/components/style/gecko/pseudo_element_definition.mako.rs +++ b/components/style/gecko/pseudo_element_definition.mako.rs @@ -195,13 +195,16 @@ impl PseudoElement { return Some(${pseudo_element_variant(pseudo)}) } % endfor - // Alias "-moz-selection" to "selection" at parse time. + // Alias some legacy prefixed pseudos to their standardized name at parse time: "-moz-selection" => { return Some(PseudoElement::Selection); } "-moz-placeholder" => { return Some(PseudoElement::Placeholder); } + "-moz-list-bullet" | "-moz-list-number" => { + return Some(PseudoElement::Marker); + } _ => { if starts_with_ignore_ascii_case(name, "-moz-tree-") { return PseudoElement::tree_pseudo_element(name, Box::new([])) diff --git a/components/style/gecko/restyle_damage.rs b/components/style/gecko/restyle_damage.rs index 9c9d07b7112..a97098363cb 100644 --- a/components/style/gecko/restyle_damage.rs +++ b/components/style/gecko/restyle_damage.rs @@ -51,8 +51,8 @@ impl GeckoRestyleDamage { let mut reset_only = false; let hint = unsafe { bindings::Gecko_CalcStyleDifference( - old_style, - new_style, + old_style.as_gecko_computed_style(), + new_style.as_gecko_computed_style(), &mut any_style_changed, &mut reset_only, ) diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index f5058fc63c4..46eaa427b3a 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -20,7 +20,7 @@ use crate::context::{PostAnimationTasks, QuirksMode, SharedStyleContext, UpdateA use crate::data::ElementData; use crate::dom::{LayoutIterator, NodeInfo, OpaqueNode, TDocument, TElement, TNode, TShadowRoot}; use crate::element_state::{DocumentState, ElementState}; -use crate::font_metrics::{FontMetrics, FontMetricsProvider, FontMetricsQueryResult}; +use crate::font_metrics::{FontMetrics, FontMetricsOrientation, FontMetricsProvider}; use crate::gecko::data::GeckoStyleSheet; use crate::gecko::selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl}; use crate::gecko::snapshot_helpers; @@ -44,7 +44,6 @@ use crate::gecko_bindings::bindings::{Gecko_ElementState, Gecko_GetDocumentLWThe use crate::gecko_bindings::bindings::{Gecko_SetNodeFlags, Gecko_UnsetNodeFlags}; use crate::gecko_bindings::structs; use crate::gecko_bindings::structs::nsChangeHint; -use crate::gecko_bindings::structs::nsRestyleHint; use crate::gecko_bindings::structs::Document_DocumentTheme as DocumentTheme; use crate::gecko_bindings::structs::EffectCompositor_CascadeLevel as CascadeLevel; use crate::gecko_bindings::structs::ELEMENT_HANDLED_SNAPSHOT; @@ -54,14 +53,15 @@ use crate::gecko_bindings::structs::ELEMENT_HAS_SNAPSHOT; use crate::gecko_bindings::structs::NODE_DESCENDANTS_NEED_FRAMES; use crate::gecko_bindings::structs::NODE_NEEDS_FRAME; use crate::gecko_bindings::structs::{nsAtom, nsIContent, nsINode_BooleanFlag}; -use crate::gecko_bindings::structs::{RawGeckoElement, RawGeckoNode, RawGeckoXBLBinding}; +use crate::gecko_bindings::structs::{ + nsINode as RawGeckoNode, nsXBLBinding as RawGeckoXBLBinding, Element as RawGeckoElement, +}; use crate::gecko_bindings::sugar::ownership::{HasArcFFI, HasSimpleFFI}; use crate::global_style_data::GLOBAL_STYLE_DATA; use crate::hash::FxHashMap; -use crate::logical_geometry::WritingMode; +use crate::invalidation::element::restyle_hints::RestyleHint; use crate::media_queries::Device; use crate::properties::animated_properties::{AnimationValue, AnimationValueMap}; -use crate::properties::style_structs::Font; use crate::properties::{ComputedValues, LonghandId}; use crate::properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock}; use crate::rule_tree::CascadeLevel as ServoCascadeLevel; @@ -69,6 +69,7 @@ use crate::selector_parser::{AttrValue, HorizontalDirection, Lang}; use crate::shared_lock::Locked; use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; use crate::stylist::CascadeData; +use crate::values::specified::length::FontBaseSize; use crate::CaseSensitivityExt; use app_units::Au; use atomic_refcell::{AtomicRefCell, AtomicRefMut}; @@ -169,11 +170,7 @@ impl<'lr> TShadowRoot for GeckoShadowRoot<'lr> { where Self: 'a, { - let author_styles = unsafe { - (self.0.mServoStyles.mPtr as *const structs::RawServoAuthorStyles - as *const bindings::RawServoAuthorStyles) - .as_ref()? - }; + let author_styles = unsafe { self.0.mServoStyles.mPtr.as_ref()? }; let author_styles = AuthorStyles::<GeckoStyleSheet>::from_ffi(author_styles); @@ -326,7 +323,11 @@ impl<'ln> GeckoNode<'ln> { // `flattened_tree_parent`. if self.flattened_tree_parent_is_parent() { debug_assert_eq!( - unsafe { bindings::Gecko_GetFlattenedTreeParentNode(self.0).map(GeckoNode) }, + unsafe { + bindings::Gecko_GetFlattenedTreeParentNode(self.0) + .as_ref() + .map(GeckoNode) + }, self.parent_node(), "Fast path stopped holding!" ); @@ -336,7 +337,11 @@ impl<'ln> GeckoNode<'ln> { // NOTE(emilio): If this call is too expensive, we could manually // inline more aggressively. - unsafe { bindings::Gecko_GetFlattenedTreeParentNode(self.0).map(GeckoNode) } + unsafe { + bindings::Gecko_GetFlattenedTreeParentNode(self.0) + .as_ref() + .map(GeckoNode) + } } #[inline] @@ -381,12 +386,16 @@ impl<'ln> TNode for GeckoNode<'ln> { #[inline] fn last_child(&self) -> Option<Self> { - unsafe { bindings::Gecko_GetLastChild(self.0).map(GeckoNode) } + unsafe { bindings::Gecko_GetLastChild(self.0).as_ref().map(GeckoNode) } } #[inline] fn prev_sibling(&self) -> Option<Self> { - unsafe { bindings::Gecko_GetPreviousSibling(self.0).map(GeckoNode) } + unsafe { + bindings::Gecko_GetPreviousSibling(self.0) + .as_ref() + .map(GeckoNode) + } } #[inline] @@ -503,7 +512,9 @@ impl<'a> Iterator for GeckoChildrenIterator<'a> { // however we can't express this easily with bindgen, and it would // introduce functions with two input lifetimes into bindgen, // which would be out of scope for elision. - bindings::Gecko_GetNextStyleChild(&mut *(it as *mut _)).map(GeckoNode) + bindings::Gecko_GetNextStyleChild(&mut *(it as *mut _)) + .as_ref() + .map(GeckoNode) }, } } @@ -549,7 +560,7 @@ impl<'lb> GeckoXBLBinding<'lb> { base.each_xbl_cascade_data(f); } - let data = unsafe { bindings::Gecko_XBLBinding_GetRawServoStyles(self.0) }; + let data = unsafe { bindings::Gecko_XBLBinding_GetRawServoStyles(self.0).as_ref() }; if let Some(data) = data { let data: &'lb _ = AuthorStyles::<GeckoStyleSheet>::from_ffi(data); @@ -710,7 +721,11 @@ impl<'le> GeckoElement<'le> { // FIXME(heycam): Having trouble with bindgen on nsXULElement, // where the binding parent is stored in a member variable // rather than in slots. So just get it through FFI for now. - unsafe { bindings::Gecko_GetBindingParent(self.0).map(GeckoElement) } + unsafe { + bindings::Gecko_GetBindingParent(self.0) + .as_ref() + .map(GeckoElement) + } } else { let binding_parent = unsafe { self.non_xul_xbl_binding_parent_raw_content().as_ref() } .map(GeckoNode::from_content) @@ -718,7 +733,11 @@ impl<'le> GeckoElement<'le> { debug_assert!( binding_parent == - unsafe { bindings::Gecko_GetBindingParent(self.0).map(GeckoElement) } + unsafe { + bindings::Gecko_GetBindingParent(self.0) + .as_ref() + .map(GeckoElement) + } ); binding_parent } @@ -778,7 +797,11 @@ impl<'le> GeckoElement<'le> { return None; } - unsafe { bindings::Gecko_GetBeforeOrAfterPseudo(self.0, is_before).map(GeckoElement) } + unsafe { + bindings::Gecko_GetBeforeOrAfterPseudo(self.0, is_before) + .as_ref() + .map(GeckoElement) + } } #[inline] @@ -800,13 +823,8 @@ impl<'le> GeckoElement<'le> { /// animation. /// /// Also this function schedules style flush. - pub unsafe fn note_explicit_hints( - &self, - restyle_hint: nsRestyleHint, - change_hint: nsChangeHint, - ) { + pub unsafe fn note_explicit_hints(&self, restyle_hint: RestyleHint, change_hint: nsChangeHint) { use crate::gecko::restyle_damage::GeckoRestyleDamage; - use crate::invalidation::element::restyle_hints::RestyleHint; let damage = GeckoRestyleDamage::new(change_hint); debug!( @@ -814,7 +832,6 @@ impl<'le> GeckoElement<'le> { self, restyle_hint, change_hint ); - let restyle_hint: RestyleHint = restyle_hint.into(); debug_assert!( !(restyle_hint.has_animation_hint() && restyle_hint.has_non_animation_hint()), "Animation restyle hints should not appear with non-animation restyle hints" @@ -890,7 +907,7 @@ impl<'le> GeckoElement<'le> { let mut map = FxHashMap::with_capacity_and_hasher(collection_length, Default::default()); for i in 0..collection_length { - let raw_end_value = unsafe { Gecko_ElementTransitions_EndValueAt(self.0, i) }; + let raw_end_value = unsafe { Gecko_ElementTransitions_EndValueAt(self.0, i).as_ref() }; let end_value = AnimationValue::arc_from_borrowed(&raw_end_value) .expect("AnimationValue not found in ElementTransitions"); @@ -1025,44 +1042,60 @@ impl FontMetricsProvider for GeckoFontMetricsProvider { } fn get_size(&self, font_name: &Atom, font_family: u8) -> Au { - use crate::gecko_bindings::bindings::Gecko_GetBaseSize; let mut cache = self.font_size_cache.borrow_mut(); if let Some(sizes) = cache.iter().find(|el| el.0 == *font_name) { return sizes.1.size_for_generic(font_family); } - let sizes = unsafe { Gecko_GetBaseSize(font_name.as_ptr()) }; + let sizes = unsafe { bindings::Gecko_GetBaseSize(font_name.as_ptr()) }; cache.push((font_name.clone(), sizes)); sizes.size_for_generic(font_family) } fn query( &self, - font: &Font, - font_size: Au, - wm: WritingMode, - in_media_query: bool, - device: &Device, - ) -> FontMetricsQueryResult { - use crate::gecko_bindings::bindings::Gecko_GetFontMetrics; - let pc = match device.pres_context() { + context: &crate::values::computed::Context, + base_size: FontBaseSize, + orientation: FontMetricsOrientation, + ) -> FontMetrics { + let pc = match context.device().pres_context() { Some(pc) => pc, - None => return FontMetricsQueryResult::NotAvailable, + None => return Default::default(), + }; + + let size = base_size.resolve(context); + let style = context.style(); + + let (wm, font) = match base_size { + FontBaseSize::CurrentStyle => (style.writing_mode, style.get_font()), + // These are only used for font-size computation, and the first is + // really dubious... + FontBaseSize::InheritedStyleButStripEmUnits | FontBaseSize::InheritedStyle => { + (*style.inherited_writing_mode(), style.get_parent_font()) + }, + }; + + let vertical_metrics = match orientation { + FontMetricsOrientation::MatchContext => wm.is_vertical() && wm.is_upright(), + FontMetricsOrientation::Horizontal => false, }; let gecko_metrics = unsafe { - Gecko_GetFontMetrics( + bindings::Gecko_GetFontMetrics( pc, - wm.is_vertical() && !wm.is_sideways(), + vertical_metrics, font.gecko(), - font_size.0, + size.0, // we don't use the user font set in a media query - !in_media_query, + !context.in_media_query, ) }; - let metrics = FontMetrics { - x_height: Au(gecko_metrics.mXSize), - zero_advance_measure: Au(gecko_metrics.mChSize), - }; - FontMetricsQueryResult::Available(metrics) + FontMetrics { + x_height: Some(Au(gecko_metrics.mXSize)), + zero_advance_measure: if gecko_metrics.mChSize >= 0 { + Some(Au(gecko_metrics.mChSize)) + } else { + None + }, + } } } @@ -1125,6 +1158,18 @@ impl<'le> TElement for GeckoElement<'le> { self.before_or_after_pseudo(/* is_before = */ false) } + fn marker_pseudo_element(&self) -> Option<Self> { + if !self.has_properties() { + return None; + } + + unsafe { + bindings::Gecko_GetMarkerPseudo(self.0) + .as_ref() + .map(GeckoElement) + } + } + #[inline] fn is_html_element(&self) -> bool { self.namespace_id() == structs::kNameSpaceID_XHTML as i32 @@ -1254,7 +1299,7 @@ impl<'le> TElement for GeckoElement<'le> { return None; } - let declarations = unsafe { Gecko_GetStyleAttrDeclarationBlock(self.0) }; + let declarations = unsafe { Gecko_GetStyleAttrDeclarationBlock(self.0).as_ref() }; let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>> = declarations.and_then(|s| s.as_arc_opt()); declarations.map(|s| s.borrow_arc()) @@ -1516,7 +1561,7 @@ impl<'le> TElement for GeckoElement<'le> { ); unsafe { self.note_explicit_hints( - nsRestyleHint::eRestyle_Subtree, + RestyleHint::restyle_subtree(), nsChangeHint::nsChangeHint_Empty, ); } @@ -1535,8 +1580,12 @@ impl<'le> TElement for GeckoElement<'le> { // should destroy all CSS animations in display:none subtree. let computed_data = self.borrow_data(); let computed_values = computed_data.as_ref().map(|d| d.styles.primary()); - let before_change_values = before_change_style.as_ref().map(|x| &**x); - let computed_values_opt = computed_values.as_ref().map(|x| &***x); + let before_change_values = before_change_style + .as_ref() + .map_or(ptr::null(), |x| x.as_gecko_computed_style()); + let computed_values_opt = computed_values + .as_ref() + .map_or(ptr::null(), |x| x.as_gecko_computed_style()); unsafe { Gecko_UpdateAnimations( self.0, @@ -1812,7 +1861,8 @@ impl<'le> TElement for GeckoElement<'le> { hints.push(SVG_TEXT_DISABLE_ZOOM_RULE.clone()); } } - let declarations = unsafe { Gecko_GetHTMLPresentationAttrDeclarationBlock(self.0) }; + let declarations = + unsafe { Gecko_GetHTMLPresentationAttrDeclarationBlock(self.0).as_ref() }; let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>> = declarations.and_then(|s| s.as_arc_opt()); if let Some(decl) = declarations { @@ -1821,7 +1871,7 @@ impl<'le> TElement for GeckoElement<'le> { ServoCascadeLevel::PresHints, )); } - let declarations = unsafe { Gecko_GetExtraContentStyleDeclarations(self.0) }; + let declarations = unsafe { Gecko_GetExtraContentStyleDeclarations(self.0).as_ref() }; let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>> = declarations.and_then(|s| s.as_arc_opt()); if let Some(decl) = declarations { @@ -1843,10 +1893,10 @@ impl<'le> TElement for GeckoElement<'le> { ); }, VisitedHandlingMode::AllLinksUnvisited => unsafe { - Gecko_GetUnvisitedLinkAttrDeclarationBlock(self.0) + Gecko_GetUnvisitedLinkAttrDeclarationBlock(self.0).as_ref() }, VisitedHandlingMode::RelevantLinkVisited => unsafe { - Gecko_GetVisitedLinkAttrDeclarationBlock(self.0) + Gecko_GetVisitedLinkAttrDeclarationBlock(self.0).as_ref() }, }; let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>> = @@ -1862,7 +1912,8 @@ impl<'le> TElement for GeckoElement<'le> { .state() .intersects(NonTSPseudoClass::Active.state_flag()); if active { - let declarations = unsafe { Gecko_GetActiveLinkAttrDeclarationBlock(self.0) }; + let declarations = + unsafe { Gecko_GetActiveLinkAttrDeclarationBlock(self.0).as_ref() }; let declarations: Option<&RawOffsetArc<Locked<PropertyDeclarationBlock>>> = declarations.and_then(|s| s.as_arc_opt()); if let Some(decl) = declarations { diff --git a/components/style/gecko_bindings/mod.rs b/components/style/gecko_bindings/mod.rs index 78fc8e3ae76..87f444f5a91 100644 --- a/components/style/gecko_bindings/mod.rs +++ b/components/style/gecko_bindings/mod.rs @@ -4,11 +4,6 @@ //! Gecko's C++ bindings, along with some rust helpers to ease its use. -#[allow(dead_code, improper_ctypes, non_camel_case_types, missing_docs)] -pub mod bindings { - include!(concat!(env!("OUT_DIR"), "/gecko/bindings.rs")); -} - // FIXME: We allow `improper_ctypes` (for now), because the lint doesn't allow // foreign structs to have `PhantomData`. We should remove this once the lint // ignores this case. @@ -25,4 +20,6 @@ pub mod structs { include!(concat!(env!("OUT_DIR"), "/gecko/structs.rs")); } +pub use self::structs as bindings; + pub mod sugar; diff --git a/components/style/gecko_bindings/sugar/ownership.rs b/components/style/gecko_bindings/sugar/ownership.rs index 3f1f1315eda..04fbf2e55c8 100644 --- a/components/style/gecko_bindings/sugar/ownership.rs +++ b/components/style/gecko_bindings/sugar/ownership.rs @@ -135,12 +135,12 @@ pub unsafe trait HasArcFFI: HasFFI { } } -#[repr(C)] /// Gecko-FFI-safe Arc (T is an ArcInner). /// /// This can be null. /// /// Leaks on drop. Please don't drop this. +#[repr(C)] pub struct Strong<GeckoType> { ptr: *const GeckoType, _marker: PhantomData<GeckoType>, @@ -320,27 +320,6 @@ impl<GeckoType> OwnedOrNull<GeckoType> { self.ptr.is_null() } - /// Returns an owned pointer if this is non-null, and `None` otherwise. - pub fn into_box_opt<ServoType>(self) -> Option<Box<ServoType>> - where - ServoType: HasBoxFFI<FFIType = GeckoType>, - { - if self.is_null() { - None - } else { - Some(unsafe { transmute(self) }) - } - } - - /// Returns an `Owned<GeckoType>` if non-null, `None` otherwise. - pub fn into_owned_opt(self) -> Option<Owned<GeckoType>> { - if self.is_null() { - None - } else { - Some(unsafe { transmute(self) }) - } - } - /// Gets a immutable reference to the underlying Gecko type, or `None` if /// null. pub fn borrow(&self) -> Option<&GeckoType> { diff --git a/components/style/gecko_bindings/sugar/refptr.rs b/components/style/gecko_bindings/sugar/refptr.rs index b289e2e21ec..1ad71afa6bc 100644 --- a/components/style/gecko_bindings/sugar/refptr.rs +++ b/components/style/gecko_bindings/sugar/refptr.rs @@ -285,7 +285,7 @@ macro_rules! impl_threadsafe_refcount { } impl_threadsafe_refcount!( - structs::RawGeckoURLExtraData, + structs::mozilla::URLExtraData, bindings::Gecko_AddRefURLExtraDataArbitraryThread, bindings::Gecko_ReleaseURLExtraDataArbitraryThread ); diff --git a/components/style/global_style_data.rs b/components/style/global_style_data.rs index 8f852dccb45..90936ff7172 100644 --- a/components/style/global_style_data.rs +++ b/components/style/global_style_data.rs @@ -11,7 +11,6 @@ use crate::parallel::STYLE_THREAD_STACK_SIZE_KB; use crate::shared_lock::SharedRwLock; use crate::thread_state; use rayon; -use servo_config::pref; use std::env; /// Global style data @@ -67,6 +66,7 @@ lazy_static! { Ok(num) => num, #[cfg(feature = "servo")] _ => { + use servo_config::pref; // We always set this pref on startup, before layout or script // have had a chance of accessing (and thus creating) the // thread-pool. diff --git a/components/style/invalidation/element/invalidator.rs b/components/style/invalidation/element/invalidator.rs index 4640c7c0442..7ed97c0b197 100644 --- a/components/style/invalidation/element/invalidator.rs +++ b/components/style/invalidation/element/invalidator.rs @@ -542,6 +542,10 @@ where any_descendant |= self.invalidate_dom_descendants_of(anon_content, invalidations); } + if let Some(marker) = self.element.marker_pseudo_element() { + any_descendant |= self.invalidate_pseudo_element_or_nac(marker, invalidations); + } + if let Some(before) = self.element.before_pseudo_element() { any_descendant |= self.invalidate_pseudo_element_or_nac(before, invalidations); } diff --git a/components/style/invalidation/element/restyle_hints.rs b/components/style/invalidation/element/restyle_hints.rs index de84fe56c85..9dbd12bab59 100644 --- a/components/style/invalidation/element/restyle_hints.rs +++ b/components/style/invalidation/element/restyle_hints.rs @@ -4,12 +4,11 @@ //! Restyle hints: an optimization to avoid unnecessarily matching selectors. -#[cfg(feature = "gecko")] -use crate::gecko_bindings::structs::nsRestyleHint; use crate::traversal_flags::TraversalFlags; bitflags! { /// The kind of restyle we need to do for a given element. + #[repr(C)] pub struct RestyleHint: u8 { /// Do a selector match of the element. const RESTYLE_SELF = 1 << 0; @@ -190,75 +189,5 @@ impl Default for RestyleHint { } } -#[cfg(feature = "gecko")] -impl From<nsRestyleHint> for RestyleHint { - fn from(mut raw: nsRestyleHint) -> Self { - let mut hint = RestyleHint::empty(); - - debug_assert!( - raw.0 & nsRestyleHint::eRestyle_LaterSiblings.0 == 0, - "Handle later siblings manually if necessary plz." - ); - - if (raw.0 & (nsRestyleHint::eRestyle_Self.0 | nsRestyleHint::eRestyle_Subtree.0)) != 0 { - raw.0 &= !nsRestyleHint::eRestyle_Self.0; - hint.insert(RestyleHint::RESTYLE_SELF); - } - - if (raw.0 & (nsRestyleHint::eRestyle_Subtree.0 | nsRestyleHint::eRestyle_SomeDescendants.0)) != - 0 - { - raw.0 &= !nsRestyleHint::eRestyle_Subtree.0; - raw.0 &= !nsRestyleHint::eRestyle_SomeDescendants.0; - hint.insert(RestyleHint::RESTYLE_DESCENDANTS); - } - - if (raw.0 & (nsRestyleHint::eRestyle_ForceDescendants.0 | nsRestyleHint::eRestyle_Force.0)) != - 0 - { - raw.0 &= !nsRestyleHint::eRestyle_Force.0; - hint.insert(RestyleHint::RECASCADE_SELF); - } - - if (raw.0 & nsRestyleHint::eRestyle_ForceDescendants.0) != 0 { - raw.0 &= !nsRestyleHint::eRestyle_ForceDescendants.0; - hint.insert(RestyleHint::RECASCADE_DESCENDANTS); - } - - hint.insert(RestyleHint::from_bits_truncate(raw.0 as u8)); - - hint - } -} - #[cfg(feature = "servo")] malloc_size_of_is_0!(RestyleHint); - -/// Asserts that all replacement hints have a matching nsRestyleHint value. -#[cfg(feature = "gecko")] -#[inline] -pub fn assert_restyle_hints_match() { - use crate::gecko_bindings::structs; - - macro_rules! check_restyle_hints { - ( $( $a:ident => $b:path),*, ) => { - if cfg!(debug_assertions) { - let mut replacements = RestyleHint::replacements(); - $( - assert_eq!(structs::nsRestyleHint::$a.0 as usize, $b.bits() as usize, stringify!($b)); - replacements.remove($b); - )* - assert_eq!(replacements, RestyleHint::empty(), - "all RestyleHint replacement bits should have an \ - assertion"); - } - } - } - - check_restyle_hints! { - eRestyle_CSSTransitions => RestyleHint::RESTYLE_CSS_TRANSITIONS, - eRestyle_CSSAnimations => RestyleHint::RESTYLE_CSS_ANIMATIONS, - eRestyle_StyleAttribute => RestyleHint::RESTYLE_STYLE_ATTRIBUTE, - eRestyle_StyleAttribute_Animations => RestyleHint::RESTYLE_SMIL, - } -} diff --git a/components/style/macros.rs b/components/style/macros.rs index 6347e5fa4a0..fb189837181 100644 --- a/components/style/macros.rs +++ b/components/style/macros.rs @@ -102,20 +102,3 @@ macro_rules! define_keyword_type { } }; } - -#[cfg(feature = "gecko")] -macro_rules! impl_bitflags_conversions { - ($name:ident) => { - impl From<u8> for $name { - fn from(bits: u8) -> $name { - $name::from_bits(bits).expect("bits contain valid flag") - } - } - - impl From<$name> for u8 { - fn from(v: $name) -> u8 { - v.bits() - } - } - }; -} diff --git a/components/style/properties/cascade.rs b/components/style/properties/cascade.rs index 23970d64888..7d4c8c6275c 100644 --- a/components/style/properties/cascade.rs +++ b/components/style/properties/cascade.rs @@ -384,8 +384,6 @@ struct Cascade<'a, 'b: 'a> { cascade_mode: CascadeMode<'a>, seen: LonghandIdSet, reverted: PerOrigin<LonghandIdSet>, - saved_font_size: Option<PropertyDeclaration>, - saved_font_family: Option<PropertyDeclaration>, } impl<'a, 'b: 'a> Cascade<'a, 'b> { @@ -395,8 +393,6 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { cascade_mode, seen: LonghandIdSet::default(), reverted: Default::default(), - saved_font_size: None, - saved_font_family: None, } } @@ -424,33 +420,8 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { )) } - fn apply_declaration<Phase: CascadePhase>( - &mut self, - longhand_id: LonghandId, - declaration: &PropertyDeclaration, - ) { - // FIXME(emilio): Find a cleaner abstraction for this. - // - // font-size and font-family are special because in Gecko they're - // they're dependent on other early props, like lang and - // -moz-min-font-size-ratio. This sucks a bit, we should ideally - // move the font-size computation code somewhere else... - if Phase::is_early() { - if longhand_id == LonghandId::FontSize { - self.saved_font_size = Some(declaration.clone()); - return; - } - if longhand_id == LonghandId::FontFamily { - self.saved_font_family = Some(declaration.clone()); - return; - } - } - - self.apply_declaration_ignoring_phase(longhand_id, declaration); - } - #[inline(always)] - fn apply_declaration_ignoring_phase( + fn apply_declaration<Phase: CascadePhase>( &mut self, longhand_id: LonghandId, declaration: &PropertyDeclaration, @@ -577,7 +548,7 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { } if Phase::is_early() { - self.fixup_font_and_apply_saved_font_properties(); + self.fixup_font_stuff(); self.compute_writing_mode(); } else { self.finished_applying_properties(); @@ -702,142 +673,249 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { true } - // FIXME(emilio): It'd be really nice to simplify all this, somehow. This is - // very annoying code in lots of ways, and there are various bits about it - // which I think are broken or could be improved, see the various FIXMEs - // below. - fn fixup_font_and_apply_saved_font_properties(&mut self) { - let font_family = self.saved_font_family.take(); - let font_size = self.saved_font_size.take(); - let mut _skip_font_family = false; + /// The default font type (which is stored in FontFamilyList's + /// `mDefaultFontType`) depends on the current lang group and generic font + /// family, so we may need to recompute it if or the family changed. + /// + /// Also, we prioritize non-document fonts here if we need to (see the pref + /// `browser.display.use_document_fonts`). + #[inline] + #[cfg(feature = "gecko")] + fn recompute_default_font_family_type_if_needed(&mut self) { + use crate::gecko_bindings::{bindings, structs}; - #[cfg(feature = "gecko")] - { - // <svg:text> is not affected by text zoom, and it uses a preshint - // to disable it. We fix up the struct when this happens by - // unzooming its contained font values, which will have been zoomed - // in the parent. - // - // FIXME(emilio): Could be cleaner if we just removed this property - // and made a style adjustment o something like that. - if self.seen.contains(LonghandId::XTextZoom) { - let builder = &mut self.context.builder; - - let parent_zoom = builder.get_parent_font().gecko().mAllowZoom; - let zoom = builder.get_font().gecko().mAllowZoom; - if zoom != parent_zoom { - debug_assert!( - !zoom, - "We only ever disable text zoom (in svg:text), never enable it" - ); - let device = builder.device; - builder.mutate_font().unzoom_fonts(device); - } + if !self.seen.contains(LonghandId::XLang) && + !self.seen.contains(LonghandId::FontFamily) { + return; + } + + let use_document_fonts = unsafe { structs::StaticPrefs_sVarCache_browser_display_use_document_fonts != 0 }; + let builder = &mut self.context.builder; + let (default_font_type, prioritize_user_fonts) = { + let font = builder.get_font().gecko(); + + // 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, structs::FontFamilyType::eFamily_none); + return; } - // Whenever a single generic value is specified, Gecko used to do a - // bunch of recalculation walking up the rule tree, including - // handling the font-size stuff. - // - // It basically repopulated the font struct with the default font - // for a given generic and language. We handle the font-size stuff - // separately, so this boils down to just copying over the - // font-family lists (no other aspect of the default font can be - // configured). - if self.seen.contains(LonghandId::XLang) || self.seen.contains(LonghandId::FontFamily) { - // If just the language changed, the inherited generic is all we - // need. - let mut generic = self.context.builder.get_parent_font().gecko().mGenericID; - - // FIXME(emilio): Isn't this bogus for CSS wide keywords like - // reset or such? - if let Some(ref declaration) = font_family { - if let PropertyDeclaration::FontFamily(ref fam) = *declaration { - if let Some(id) = fam.single_generic() { - generic = id; - // In case of a specified font family with a single - // generic, we will end up setting font family - // below, but its value would get overwritten later - // in the pipeline when cascading. - // - // We instead skip cascading font-family in that - // case. - // - // In case of the language changing, we wish for a - // specified font-family to override this, so we do - // not skip cascading then. - _skip_font_family = true; - } - } - } + let default_font_type = unsafe { + bindings::Gecko_nsStyleFont_ComputeDefaultFontType( + builder.device.document(), + font.mGenericID, + font.mLanguage.mRawPtr, + ) + }; - // FIXME(emilio): Why both setting the generic and passing it - // down? - let doc = self.context.builder.device.document(); - let gecko_font = self.context.builder.mutate_font().gecko_mut(); - gecko_font.mGenericID = generic; - unsafe { - crate::gecko_bindings::bindings::Gecko_nsStyleFont_PrefillDefaultForGeneric( - gecko_font, - doc, - generic, - ); - } + // We prioritize user fonts over document fonts if the pref is set, + // 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 && + matches!( + font.mGenericID, + structs::kGenericFont_NONE | + structs::kGenericFont_fantasy | + structs::kGenericFont_cursive + ) && + default_font_type != structs::FontFamilyType::eFamily_none; + + if !prioritize_user_fonts && default_font_type == font.mFont.fontlist.mDefaultFontType { + // Nothing to do. + return; + } + (default_font_type, prioritize_user_fonts) + }; + + 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) } } + } - // It is important that font-size is computed before the late - // properties (for em units), but after font-family (for the - // base-font-size dependence for default and keyword font-sizes). - // - // It's important that font-family comes after the other font properties - // to support system fonts. - // - // NOTE(emilio): I haven't verified that comment, but it was there. - // Verify, and if it's false make font-size the only weird property? - if !_skip_font_family { - if let Some(ref declaration) = font_family { - self.apply_declaration_ignoring_phase(LonghandId::FontFamily, declaration); - #[cfg(feature = "gecko")] - { - let context = &mut self.context; - let device = context.builder.device; - if let PropertyDeclaration::FontFamily(ref val) = *declaration { - if val.get_system().is_some() { - let default = context - .cached_system_font - .as_ref() - .unwrap() - .default_font_type; - context.builder.mutate_font().fixup_system(default); - } else { - context.builder.mutate_font().fixup_none_generic(device); - } - } + /// Some keyword sizes depend on the font family and language. + #[cfg(feature = "gecko")] + fn recompute_keyword_font_size_if_needed(&mut self) { + use crate::values::computed::ToComputedValue; + use crate::values::specified; + + if !self.seen.contains(LonghandId::XLang) && + !self.seen.contains(LonghandId::FontFamily) { + return; + } + + let new_size = { + let font = self.context.builder.get_font(); + let new_size = match font.clone_font_size().keyword_info { + Some(info) => { + self.context.for_non_inherited_property = None; + specified::FontSize::Keyword(info).to_computed_value(self.context) } + None => return, + }; + + if font.gecko().mScriptUnconstrainedSize == new_size.size().0 { + return; + } + + new_size + }; + + self.context.builder.mutate_font().set_font_size(new_size); + } + + /// Some properties, plus setting font-size itself, may make us go out of + /// our minimum font-size range. + #[cfg(feature = "gecko")] + fn constrain_font_size_if_needed(&mut self) { + use crate::gecko_bindings::bindings; + + if !self.seen.contains(LonghandId::XLang) && + !self.seen.contains(LonghandId::FontFamily) && + !self.seen.contains(LonghandId::MozMinFontSizeRatio) && + !self.seen.contains(LonghandId::FontSize) { + return; + } + + let builder = &mut self.context.builder; + let min_font_size = { + let font = builder.get_font().gecko(); + let min_font_size = unsafe { + bindings::Gecko_nsStyleFont_ComputeMinSize( + font, + builder.device.document(), + ) + }; + + if font.mFont.size >= min_font_size { + return; } + + min_font_size + }; + + builder.mutate_font().gecko_mut().mFont.size = min_font_size; + } + + /// <svg:text> is not affected by text zoom, and it uses a preshint + /// to disable it. We fix up the struct when this happens by + /// unzooming its contained font values, which will have been zoomed + /// in the parent. + /// + /// FIXME(emilio): Also, why doing this _before_ handling font-size? That + /// sounds wrong. + #[cfg(feature = "gecko")] + fn unzoom_fonts_if_needed(&mut self) { + if !self.seen.contains(LonghandId::XTextZoom) { + return; } - if let Some(declaration) = font_size { - self.apply_declaration_ignoring_phase(LonghandId::FontSize, &declaration); - } else { - #[cfg(feature = "gecko")] - { - if self.seen.contains(LonghandId::XLang) || - self.seen.contains(LonghandId::MozScriptLevel) || - self.seen.contains(LonghandId::MozMinFontSizeRatio) || - self.seen.contains(LonghandId::FontFamily) - { - use crate::values::computed::FontSize; - - // font-size must be explicitly inherited to handle lang - // changes and scriptlevel changes. - // - // FIXME(emilio): That looks a bit bogus... - self.context.for_non_inherited_property = None; - FontSize::cascade_inherit_font_size(&mut self.context); + let builder = &mut self.context.builder; + + let parent_zoom = builder.get_parent_font().gecko().mAllowZoom; + let zoom = builder.get_font().gecko().mAllowZoom; + if zoom == parent_zoom { + return; + } + debug_assert!( + !zoom, + "We only ever disable text zoom (in svg:text), never enable it" + ); + let device = builder.device; + builder.mutate_font().unzoom_fonts(device); + } + + /// MathML script* attributes do some very weird shit with font-size. + /// + /// Handle them specially here, separate from other font-size stuff. + /// + /// How this should interact with lang="" and font-family-dependent sizes is + /// 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; + + if !self.seen.contains(LonghandId::MozScriptLevel) && + !self.seen.contains(LonghandId::MozScriptMinSize) && + !self.seen.contains(LonghandId::MozScriptSizeMultiplier) { + return; + } + + // If the user specifies a font-size, just let it be. + if self.seen.contains(LonghandId::FontSize) { + return; + } + + let builder = &mut self.context.builder; + let (new_size, new_unconstrained_size) = { + let font = builder.get_font().gecko(); + let parent_font = builder.get_parent_font().gecko(); + + let delta = + font.mScriptLevel.saturating_sub(parent_font.mScriptLevel); + + if delta == 0 { + return; + } + + let mut min = Au(parent_font.mScriptMinSize); + if font.mAllowZoom { + 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 new_size = parent_size.scale_by(scale); + let new_unconstrained_size = parent_unconstrained_size.scale_by(scale); + + if scale <= 1. { + // The parent size can be smaller than scriptminsize, e.g. if it + // was specified explicitly. Don't scale in this case, but we + // don't want to set it to scriptminsize either since that will + // make it larger. + if parent_size <= min { + (parent_size, new_unconstrained_size) + } else { + (cmp::max(min, new_size), new_unconstrained_size) } + } else { + // If the new unconstrained size is larger than the min size, + // this means we have escaped the grasp of scriptminsize and can + // revert to using the unconstrained size. + // 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 + ) } + }; + let font = builder.mutate_font().gecko_mut(); + font.mFont.size = new_size.0; + font.mSize = new_size.0; + font.mScriptUnconstrainedSize = new_unconstrained_size.0; + } + + /// Various properties affect how font-size and font-family are computed. + /// + /// These need to be handled here, since relative lengths and ex / ch units + /// for late properties depend on these. + fn fixup_font_stuff(&mut self) { + #[cfg(feature = "gecko")] + { + self.unzoom_fonts_if_needed(); + self.recompute_default_font_family_type_if_needed(); + self.recompute_keyword_font_size_if_needed(); + self.handle_mathml_scriptlevel_if_needed(); + self.constrain_font_size_if_needed() } } } diff --git a/components/style/properties/computed_value_flags.rs b/components/style/properties/computed_value_flags.rs index 18fc886c7ae..a86f8a4da3a 100644 --- a/components/style/properties/computed_value_flags.rs +++ b/components/style/properties/computed_value_flags.rs @@ -56,10 +56,13 @@ bitflags! { /// 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 the style or any of the ancestors has a multicol style. /// /// Only used in Servo. - const CAN_BE_FRAGMENTED = 1 << 9; + const CAN_BE_FRAGMENTED = 1 << 10; } } @@ -94,3 +97,22 @@ impl ComputedValueFlags { self & Self::maybe_inherited_flags() } } + +/// Asserts that the relevant servo and Gecko representations match. +#[cfg(feature = "gecko")] +#[inline] +pub fn assert_match() { + use crate::gecko_bindings::structs; + macro_rules! assert_bit { + ($rust:ident, $cpp:ident) => { + debug_assert_eq!(ComputedValueFlags::$rust.bits, structs::$cpp); + } + } + + assert_bit!(HAS_TEXT_DECORATION_LINES, ComputedStyleBit_HasTextDecorationLines); + assert_bit!(IS_IN_PSEUDO_ELEMENT_SUBTREE, ComputedStyleBit_HasPseudoElementData); + assert_bit!(SHOULD_SUPPRESS_LINEBREAK, ComputedStyleBit_SuppressLineBreak); + assert_bit!(IS_TEXT_COMBINED, ComputedStyleBit_IsTextCombined); + assert_bit!(IS_RELEVANT_LINK_VISITED, ComputedStyleBit_RelevantLinkVisited); + assert_bit!(DEPENDS_ON_FONT_METRICS, ComputedStyleBit_DependsOnFontMetrics); +} diff --git a/components/style/properties/data.py b/components/style/properties/data.py index 9347d9b9428..52cdcf15d22 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -324,6 +324,7 @@ class Longhand(object): "JustifyItems", "JustifySelf", "MozForceBrokenImageIcon", + "MozListReversed", "MozScriptLevel", "MozScriptMinSize", "MozScriptSizeMultiplier", diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 6a4d4d87a99..7c5f4a94c01 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -66,16 +66,25 @@ use crate::values::generics::url::UrlOrNone; pub mod style_structs { % for style_struct in data.style_structs: pub use super::${style_struct.gecko_struct_name} as ${style_struct.name}; + + unsafe impl Send for ${style_struct.name} {} + unsafe impl Sync for ${style_struct.name} {} % endfor + } /// FIXME(emilio): This is completely duplicated with the other properties code. -pub type ComputedValuesInner = crate::gecko_bindings::structs::ServoComputedData; +pub type ComputedValuesInner = structs::ServoComputedData; #[repr(C)] -pub struct ComputedValues(crate::gecko_bindings::structs::mozilla::ComputedStyle); +pub struct ComputedValues(structs::mozilla::ComputedStyle); impl ComputedValues { + #[inline] + pub (crate) fn as_gecko_computed_style(&self) -> &structs::ComputedStyle { + &self.0 + } + pub fn new( pseudo: Option<<&PseudoElement>, custom_properties: Option<Arc<CustomPropertiesMap>>, @@ -929,7 +938,7 @@ transform_functions = [ debug_assert!(!${item}${index + 1}.0.is_empty()); % endif ${css_value_setters[item] % ( - "bindings::Gecko_CSSValue_GetArrayItem(gecko_value, %d)" % (index + 1), + "(&mut *bindings::Gecko_CSSValue_GetArrayItem(gecko_value, %d))" % (index + 1), item + str(index + 1) )}; % endfor @@ -980,7 +989,7 @@ transform_functions = [ % endif <% getter = css_value_getters[item] % ( - "bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, %d)" % (index + 1) + "(&*bindings::Gecko_CSSValue_GetArrayItemConst(gecko_value, %d))" % (index + 1) ) %> ${getter}, @@ -989,6 +998,7 @@ transform_functions = [ }, </%def> +#[allow(unused_parens)] fn set_single_transform_function( servo_value: &values::computed::TransformOperation, gecko_value: &mut structs::nsCSSValue /* output */ @@ -1031,6 +1041,7 @@ pub fn convert_transform( output.set_move(list); } +#[allow(unused_parens)] fn clone_single_transform_function( gecko_value: &structs::nsCSSValue ) -> values::computed::TransformOperation { @@ -1236,6 +1247,7 @@ impl Clone for ${style_struct.gecko_struct_name} { "Length": impl_absolute_length, "LengthOrNormal": impl_style_coord, "LengthPercentageOrAuto": impl_style_coord, + "MozListReversed": impl_simple, "MozScriptMinSize": impl_absolute_length, "RGBAColor": impl_rgba_color, "SVGLength": impl_svg_length, @@ -1971,25 +1983,19 @@ fn static_assert() { <% impl_font_settings("font_feature_settings", "gfxFontFeature", "FeatureTagValue", "i32", "u32") %> <% impl_font_settings("font_variation_settings", "gfxFontVariation", "VariationValue", "f32", "f32") %> - pub fn fixup_none_generic(&mut self, device: &Device) { - self.gecko.mFont.systemFont = false; - unsafe { - bindings::Gecko_nsStyleFont_FixupNoneGeneric(&mut self.gecko, device.document()) - } - } - - pub fn fixup_system(&mut self, default_font_type: structs::FontFamilyType) { - self.gecko.mFont.systemFont = true; - self.gecko.mGenericID = structs::kGenericFont_NONE; - self.gecko.mFont.fontlist.mDefaultFontType = default_font_type; - } - pub fn set_font_family(&mut self, v: longhands::font_family::computed_value::T) { - self.gecko.mGenericID = structs::kGenericFont_NONE; - if let Some(generic) = v.0.single_generic() { - self.gecko.mGenericID = generic; - } - self.gecko.mFont.fontlist.mFontlist.mBasePtr.set_move((v.0).0.clone()); + use crate::gecko_bindings::structs::FontFamilyType; + + let is_system_font = v.is_system_font; + self.gecko.mFont.systemFont = is_system_font; + self.gecko.mGenericID = if is_system_font { + structs::kGenericFont_NONE + } else { + v.families.single_generic().unwrap_or(structs::kGenericFont_NONE) + }; + self.gecko.mFont.fontlist.mFontlist.mBasePtr.set_move(v.families.0.clone()); + // Fixed-up if needed in Cascade::fixup_font_stuff. + self.gecko.mFont.fontlist.mDefaultFontType = FontFamilyType::eFamily_none; } pub fn copy_font_family_from(&mut self, other: &Self) { @@ -2009,7 +2015,7 @@ fn static_assert() { let fontlist = &self.gecko.mFont.fontlist; let shared_fontlist = unsafe { fontlist.mFontlist.mBasePtr.to_safe() }; - if shared_fontlist.mNames.is_empty() { + let families = if shared_fontlist.mNames.is_empty() { let default = fontlist.mDefaultFontType; let default = match default { FontFamilyType::eFamily_serif => { @@ -2030,9 +2036,14 @@ fn static_assert() { SingleFontFamily::Generic(atom!("sans-serif")) } }; - FontFamily(FontFamilyList::new(Box::new([default]))) + FontFamilyList::new(Box::new([default])) } else { - FontFamily(FontFamilyList(shared_fontlist)) + FontFamilyList(shared_fontlist) + }; + + FontFamily { + families, + is_system_font: self.gecko.mFont.systemFont, } } @@ -2042,10 +2053,32 @@ fn static_assert() { self.gecko.mFont.size = device.unzoom_text(Au(self.gecko.mFont.size)).0; } + pub fn copy_font_size_from(&mut self, other: &Self) { + self.gecko.mScriptUnconstrainedSize = other.gecko.mScriptUnconstrainedSize; + + self.gecko.mSize = other.gecko.mScriptUnconstrainedSize; + self.gecko.mFont.size = other.gecko.mSize; + self.gecko.mFontSizeKeyword = other.gecko.mFontSizeKeyword; + + // TODO(emilio): Should we really copy over these two? + self.gecko.mFontSizeFactor = other.gecko.mFontSizeFactor; + self.gecko.mFontSizeOffset = other.gecko.mFontSizeOffset; + } + + pub fn reset_font_size(&mut self, other: &Self) { + self.copy_font_size_from(other) + } + pub fn set_font_size(&mut self, v: FontSize) { use crate::values::generics::font::KeywordSize; - self.gecko.mSize = v.size().0; - self.gecko.mScriptUnconstrainedSize = v.size().0; + + let size = v.size(); + self.gecko.mScriptUnconstrainedSize = size.0; + + // These two may be changed from Cascade::fixup_font_stuff. + self.gecko.mSize = size.0; + self.gecko.mFont.size = size.0; + if let Some(info) = v.keyword_info { self.gecko.mFontSizeKeyword = match info.kw { KeywordSize::XXSmall => structs::NS_STYLE_FONT_SIZE_XXSMALL, @@ -2066,196 +2099,6 @@ fn static_assert() { } } - /// Set font size, taking into account scriptminsize and scriptlevel - /// Returns Some(size) if we have to recompute the script unconstrained size - pub fn apply_font_size( - &mut self, - v: FontSize, - parent: &Self, - device: &Device, - ) -> Option<NonNegativeLength> { - let (adjusted_size, adjusted_unconstrained_size) = - self.calculate_script_level_size(parent, device); - // In this case, we have been unaffected by scriptminsize, ignore it - if parent.gecko.mSize == parent.gecko.mScriptUnconstrainedSize && - adjusted_size == adjusted_unconstrained_size { - self.set_font_size(v); - self.fixup_font_min_size(device); - None - } else { - self.gecko.mSize = v.size().0; - self.fixup_font_min_size(device); - Some(Au(parent.gecko.mScriptUnconstrainedSize).into()) - } - } - - pub fn fixup_font_min_size(&mut self, device: &Device) { - unsafe { bindings::Gecko_nsStyleFont_FixupMinFontSize(&mut self.gecko, device.document()) } - } - - pub fn apply_unconstrained_font_size(&mut self, v: NonNegativeLength) { - self.gecko.mScriptUnconstrainedSize = v.0.to_i32_au(); - } - - /// Calculates the constrained and unconstrained font sizes to be inherited - /// from the parent. - /// - /// This is a port of Gecko's old ComputeScriptLevelSize function: - /// https://searchfox.org/mozilla-central/rev/c05d9d61188d32b8/layout/style/nsRuleNode.cpp#3103 - /// - /// scriptlevel is a property that affects how font-size is inherited. If scriptlevel is - /// +1, for example, it will inherit as the script size multiplier times - /// the parent font. This does not affect cases where the font-size is - /// explicitly set. - /// - /// However, this transformation is not allowed to reduce the size below - /// scriptminsize. If this inheritance will reduce it to below - /// scriptminsize, it will be set to scriptminsize or the parent size, - /// whichever is smaller (the parent size could be smaller than the min size - /// because it was explicitly specified). - /// - /// Now, within a node that has inherited a font-size which was - /// crossing scriptminsize once the scriptlevel was applied, a negative - /// scriptlevel may be used to increase the size again. - /// - /// This should work, however if we have already been capped by the - /// scriptminsize multiple times, this can lead to a jump in the size. - /// - /// For example, if we have text of the form: - /// - /// huge large medium small tiny reallytiny tiny small medium huge - /// - /// which is represented by progressive nesting and scriptlevel values of - /// +1 till the center after which the scriptlevel is -1, the "tiny"s should - /// be the same size, as should be the "small"s and "medium"s, etc. - /// - /// However, if scriptminsize kicked it at around "medium", then - /// medium/tiny/reallytiny will all be the same size (the min size). - /// A -1 scriptlevel change after this will increase the min size by the - /// multiplier, making the second tiny larger than medium. - /// - /// Instead, we wish for the second "tiny" to still be capped by the script - /// level, and when we reach the second "large", it should be the same size - /// as the original one. - /// - /// We do this by cascading two separate font sizes. The font size (mSize) - /// is the actual displayed font size. The unconstrained font size - /// (mScriptUnconstrainedSize) is the font size in the situation where - /// scriptminsize never applied. - /// - /// We calculate the proposed inherited font size based on scriptlevel and - /// the parent unconstrained size, instead of using the parent font size. - /// This is stored in the node's unconstrained size and will also be stored - /// in the font size provided that it is above the min size. - /// - /// All of this only applies when inheriting. When the font size is - /// manually set, scriptminsize does not apply, and both the real and - /// unconstrained size are set to the explicit value. However, if the font - /// size is manually set to an em or percent unit, the unconstrained size - /// will be set to the value of that unit computed against the parent - /// unconstrained size, whereas the font size will be set computing against - /// the parent font size. - pub fn calculate_script_level_size(&self, parent: &Self, device: &Device) -> (Au, Au) { - use std::cmp; - - let delta = self.gecko.mScriptLevel.saturating_sub(parent.gecko.mScriptLevel); - - let parent_size = Au(parent.gecko.mSize); - let parent_unconstrained_size = Au(parent.gecko.mScriptUnconstrainedSize); - - if delta == 0 { - return (parent_size, parent_unconstrained_size) - } - - - let mut min = Au(parent.gecko.mScriptMinSize); - if self.gecko.mAllowZoom { - min = device.zoom_text(min); - } - - let scale = (parent.gecko.mScriptSizeMultiplier as f32).powi(delta as i32); - - let new_size = parent_size.scale_by(scale); - let new_unconstrained_size = parent_unconstrained_size.scale_by(scale); - - if scale < 1. { - // The parent size can be smaller than scriptminsize, - // e.g. if it was specified explicitly. Don't scale - // in this case, but we don't want to set it to scriptminsize - // either since that will make it larger. - if parent_size < min { - (parent_size, new_unconstrained_size) - } else { - (cmp::max(min, new_size), new_unconstrained_size) - } - } else { - // If the new unconstrained size is larger than the min size, - // this means we have escaped the grasp of scriptminsize - // and can revert to using the unconstrained size. - // 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) - } - } - - /// This function will also handle scriptminsize and scriptlevel - /// so should not be called when you just want the font sizes to be copied. - /// Hence the different name. - pub fn inherit_font_size_from(&mut self, parent: &Self, - kw_inherited_size: Option<NonNegativeLength>, - device: &Device) { - let (adjusted_size, adjusted_unconstrained_size) - = self.calculate_script_level_size(parent, device); - if adjusted_size.0 != parent.gecko.mSize || - adjusted_unconstrained_size.0 != parent.gecko.mScriptUnconstrainedSize { - // FIXME(Manishearth): This is incorrect. When there is both a - // keyword size being inherited and a scriptlevel change, we must - // handle the keyword size the same way we handle em units. This - // complicates things because we now have to keep track of the - // adjusted and unadjusted ratios in the kw font size. This only - // affects the use case of a generic font being used in MathML. - // - // If we were to fix this I would prefer doing it not doing - // something like the ruletree walk that Gecko used to do in - // nsRuleNode::SetGenericFont and instead using extra bookkeeping in - // the mSize and mScriptUnconstrainedSize values, and reusing those - // instead of font_size_keyword. - - // In the case that MathML has given us an adjusted size, apply it. - // Keep track of the unconstrained adjusted size. - self.gecko.mSize = adjusted_size.0; - - // Technically the MathML constrained size may also be keyword-derived - // but we ignore this since it would be too complicated - // to correctly track and it's mostly unnecessary. - self.gecko.mFontSizeKeyword = structs::NS_STYLE_FONT_SIZE_NO_KEYWORD as u8; - self.gecko.mFontSizeFactor = 1.; - self.gecko.mFontSizeOffset = 0; - - self.gecko.mScriptUnconstrainedSize = adjusted_unconstrained_size.0; - } else if let Some(size) = kw_inherited_size { - // Parent element was a keyword-derived size. - self.gecko.mSize = size.0.to_i32_au(); - // Copy keyword info over. - self.gecko.mFontSizeFactor = parent.gecko.mFontSizeFactor; - self.gecko.mFontSizeOffset = parent.gecko.mFontSizeOffset; - self.gecko.mFontSizeKeyword = parent.gecko.mFontSizeKeyword; - // MathML constraints didn't apply here, so we can ignore this. - self.gecko.mScriptUnconstrainedSize = size.0.to_i32_au(); - } else { - // MathML isn't affecting us, and our parent element does not - // have a keyword-derived size. Set things normally. - self.gecko.mSize = parent.gecko.mSize; - // copy keyword info over - self.gecko.mFontSizeKeyword = structs::NS_STYLE_FONT_SIZE_NO_KEYWORD as u8; - self.gecko.mFontSizeFactor = 1.; - self.gecko.mFontSizeOffset = 0; - self.gecko.mScriptUnconstrainedSize = parent.gecko.mScriptUnconstrainedSize; - } - self.fixup_font_min_size(device); - } - pub fn clone_font_size(&self) -> FontSize { use crate::values::generics::font::{KeywordInfo, KeywordSize}; let size = Au(self.gecko.mSize).into(); @@ -2270,16 +2113,16 @@ fn static_assert() { structs::NS_STYLE_FONT_SIZE_XXXLARGE => KeywordSize::XXXLarge, structs::NS_STYLE_FONT_SIZE_NO_KEYWORD => { return FontSize { - size: size, + size, keyword_info: None, } } _ => unreachable!("mFontSizeKeyword should be an absolute keyword or NO_KEYWORD") }; FontSize { - size: size, + size, keyword_info: Some(KeywordInfo { - kw: kw, + kw, factor: self.gecko.mFontSizeFactor, offset: Au(self.gecko.mFontSizeOffset).into() }) @@ -2751,7 +2594,7 @@ fn static_assert() { transform-style rotate scroll-snap-points-x scroll-snap-points-y scroll-snap-coordinate -moz-binding will-change - offset-path shape-outside contain touch-action + offset-path shape-outside translate scale""" %> <%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}"> #[inline] @@ -3109,10 +2952,10 @@ fn static_assert() { pub fn set_will_change(&mut self, v: longhands::will_change::computed_value::T) { use crate::gecko_bindings::bindings::{Gecko_AppendWillChange, Gecko_ClearWillChange}; - use crate::properties::longhands::will_change::computed_value::T; + use crate::values::specified::box_::{WillChangeBits, WillChange}; match v { - T::AnimateableFeatures { features, bits } => { + WillChange::AnimateableFeatures { features, bits } => { unsafe { Gecko_ClearWillChange(&mut self.gecko, features.len()); } @@ -3123,13 +2966,13 @@ fn static_assert() { } } - self.gecko.mWillChangeBitField = bits.bits(); + self.gecko.mWillChangeBitField = bits; }, - T::Auto => { + WillChange::Auto => { unsafe { Gecko_ClearWillChange(&mut self.gecko, 0); } - self.gecko.mWillChangeBitField = 0; + self.gecko.mWillChangeBitField = WillChangeBits::empty(); }, }; } @@ -3139,7 +2982,7 @@ fn static_assert() { self.gecko.mWillChangeBitField = other.gecko.mWillChangeBitField; unsafe { - Gecko_CopyWillChangeFrom(&mut self.gecko, &other.gecko as *const _ as *mut _); + Gecko_CopyWillChangeFrom(&mut self.gecko, &other.gecko); } } @@ -3148,116 +2991,27 @@ fn static_assert() { } pub fn clone_will_change(&self) -> longhands::will_change::computed_value::T { - use crate::properties::longhands::will_change::computed_value::T; - use crate::gecko_bindings::structs::nsAtom; use crate::values::CustomIdent; - use crate::values::specified::box_::WillChangeBits; + use crate::values::specified::box_::WillChange; if self.gecko.mWillChange.len() == 0 { - return T::Auto + return WillChange::Auto } let custom_idents: Vec<CustomIdent> = self.gecko.mWillChange.iter().map(|gecko_atom| { unsafe { - CustomIdent(Atom::from_raw(gecko_atom.mRawPtr as *mut nsAtom)) + CustomIdent(Atom::from_raw(gecko_atom.mRawPtr)) } }).collect(); - T::AnimateableFeatures { + WillChange::AnimateableFeatures { features: custom_idents.into_boxed_slice(), - bits: WillChangeBits::from_bits_truncate(self.gecko.mWillChangeBitField), + bits: self.gecko.mWillChangeBitField, } } <% impl_shape_source("shape_outside", "mShapeOutside") %> - pub fn set_contain(&mut self, v: longhands::contain::computed_value::T) { - use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_NONE; - use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_STRICT; - use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT; - use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_SIZE; - use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_LAYOUT; - use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_PAINT; - use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_ALL_BITS; - use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT_BITS; - use crate::properties::longhands::contain::SpecifiedValue; - - if v.is_empty() { - self.gecko.mContain = NS_STYLE_CONTAIN_NONE as u8; - return; - } - - if v.contains(SpecifiedValue::STRICT) { - self.gecko.mContain = (NS_STYLE_CONTAIN_STRICT | NS_STYLE_CONTAIN_ALL_BITS) as u8; - return; - } - if v.contains(SpecifiedValue::CONTENT) { - self.gecko.mContain = (NS_STYLE_CONTAIN_CONTENT | NS_STYLE_CONTAIN_CONTENT_BITS) as u8; - return; - } - - let mut bitfield = 0; - if v.contains(SpecifiedValue::LAYOUT) { - bitfield |= NS_STYLE_CONTAIN_LAYOUT; - } - if v.contains(SpecifiedValue::PAINT) { - bitfield |= NS_STYLE_CONTAIN_PAINT; - } - if v.contains(SpecifiedValue::SIZE) { - bitfield |= NS_STYLE_CONTAIN_SIZE; - } - - self.gecko.mContain = bitfield as u8; - } - - pub fn clone_contain(&self) -> longhands::contain::computed_value::T { - use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_STRICT; - use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT; - use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_SIZE; - use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_LAYOUT; - use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_PAINT; - use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_ALL_BITS; - use crate::gecko_bindings::structs::NS_STYLE_CONTAIN_CONTENT_BITS; - use crate::properties::longhands::contain::{self, SpecifiedValue}; - - let mut servo_flags = contain::computed_value::T::empty(); - let gecko_flags = self.gecko.mContain; - - if gecko_flags & (NS_STYLE_CONTAIN_STRICT as u8) != 0 { - debug_assert_eq!( - gecko_flags & (NS_STYLE_CONTAIN_ALL_BITS as u8), - NS_STYLE_CONTAIN_ALL_BITS as u8, - "When strict is specified, ALL_BITS should be specified as well" - ); - servo_flags.insert(SpecifiedValue::STRICT | SpecifiedValue::STRICT_BITS); - return servo_flags; - } - if gecko_flags & (NS_STYLE_CONTAIN_CONTENT as u8) != 0 { - debug_assert_eq!( - gecko_flags & (NS_STYLE_CONTAIN_CONTENT_BITS as u8), - NS_STYLE_CONTAIN_CONTENT_BITS as u8, - "When content is specified, CONTENT_BITS should be specified as well" - ); - servo_flags.insert(SpecifiedValue::CONTENT | SpecifiedValue::CONTENT_BITS); - return servo_flags; - } - if gecko_flags & (NS_STYLE_CONTAIN_LAYOUT as u8) != 0 { - servo_flags.insert(SpecifiedValue::LAYOUT); - } - if gecko_flags & (NS_STYLE_CONTAIN_PAINT as u8) != 0 { - servo_flags.insert(SpecifiedValue::PAINT); - } - if gecko_flags & (NS_STYLE_CONTAIN_SIZE as u8) != 0 { - servo_flags.insert(SpecifiedValue::SIZE); - } - - return servo_flags; - } - - ${impl_simple_copy("contain", "mContain")} - - ${impl_simple_type_with_conversion("touch_action")} - pub fn set_offset_path(&mut self, v: longhands::offset_path::computed_value::T) { use crate::gecko_bindings::bindings::{Gecko_NewStyleMotion, Gecko_SetStyleMotion}; use crate::gecko_bindings::structs::StyleShapeSourceType; @@ -4250,9 +4004,7 @@ fn static_assert() { </%self:impl_trait> <%self:impl_trait style_struct_name="Text" - skip_longhands="text-decoration-line text-overflow initial-letter"> - - ${impl_simple_type_with_conversion("text_decoration_line")} + skip_longhands="text-overflow initial-letter"> fn clear_overflow_sides_if_string(&mut self) { use crate::gecko_bindings::structs::nsStyleTextOverflowSide; @@ -4366,21 +4118,6 @@ fn static_assert() { InitialLetter::Specified(self.gecko.mInitialLetterSize, Some(self.gecko.mInitialLetterSink)) } } - - #[inline] - pub fn has_underline(&self) -> bool { - (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE as u8)) != 0 - } - - #[inline] - pub fn has_overline(&self) -> bool { - (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_OVERLINE as u8)) != 0 - } - - #[inline] - pub fn has_line_through(&self) -> bool { - (self.gecko.mTextDecorationLine & (structs::NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH as u8)) != 0 - } </%self:impl_trait> // Set SVGPathData to StyleShapeSource. @@ -4823,7 +4560,7 @@ clip-path </%self:impl_trait> <%self:impl_trait style_struct_name="Counters" - skip_longhands="content counter-increment counter-reset"> + skip_longhands="content counter-increment counter-reset counter-set"> pub fn ineffective_content_property(&self) -> bool { self.gecko.mContents.is_empty() } @@ -5052,7 +4789,7 @@ clip-path ) } - % for counter_property in ["Increment", "Reset"]: + % for counter_property in ["Increment", "Reset", "Set"]: pub fn set_counter_${counter_property.lower()}( &mut self, v: longhands::counter_${counter_property.lower()}::computed_value::T diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index 81931bc713b..b48f0afbf00 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -333,11 +333,7 @@ % if not property.style_struct.inherited: debug_assert!(false, "Should be handled in apply_properties"); % else: - % if property.name == "font-size": - computed::FontSize::cascade_initial_font_size(context); - % else: context.builder.reset_${property.ident}(); - % endif % endif }, % if property.style_struct.inherited: @@ -394,15 +390,7 @@ % else: let computed = specified_value.to_computed_value(context); % endif - % if property.ident == "font_size": - specified::FontSize::cascade_specified_font_size( - context, - &specified_value, - computed, - ); - % else: - context.builder.set_${property.ident}(computed) - % endif + context.builder.set_${property.ident}(computed) % endif } diff --git a/components/style/properties/longhands/box.mako.rs b/components/style/properties/longhands/box.mako.rs index 1a8abfb3611..dcfcb343040 100644 --- a/components/style/properties/longhands/box.mako.rs +++ b/components/style/properties/longhands/box.mako.rs @@ -365,7 +365,7 @@ ${helpers.predefined_type( "generics::transform::Rotate::None", animation_value_type="ComputedValue", boxed=True, - flags="CREATES_STACKING_CONTEXT FIXPOS_CB", + flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR", gecko_pref="layout.css.individual-transform.enabled", spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms", servo_restyle_damage = "reflow_out_of_flow", @@ -377,7 +377,7 @@ ${helpers.predefined_type( "generics::transform::Scale::None", animation_value_type="ComputedValue", boxed=True, - flags="CREATES_STACKING_CONTEXT FIXPOS_CB", + flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR", gecko_pref="layout.css.individual-transform.enabled", spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms", servo_restyle_damage = "reflow_out_of_flow", @@ -389,7 +389,7 @@ ${helpers.predefined_type( "generics::transform::Translate::None", animation_value_type="ComputedValue", boxed=True, - flags="CREATES_STACKING_CONTEXT FIXPOS_CB", + flags="CREATES_STACKING_CONTEXT FIXPOS_CB CAN_ANIMATE_ON_COMPOSITOR", gecko_pref="layout.css.individual-transform.enabled", spec="https://drafts.csswg.org/css-transforms-2/#individual-transforms", servo_restyle_damage="reflow_out_of_flow", diff --git a/components/style/properties/longhands/counters.mako.rs b/components/style/properties/longhands/counters.mako.rs index b690eeb5c45..f8971315603 100644 --- a/components/style/properties/longhands/counters.mako.rs +++ b/components/style/properties/longhands/counters.mako.rs @@ -27,9 +27,19 @@ ${helpers.predefined_type( ${helpers.predefined_type( "counter-reset", - "CounterReset", + "CounterSetOrReset", initial_value="Default::default()", animation_value_type="discrete", spec="https://drafts.csswg.org/css-lists-3/#propdef-counter-reset", servo_restyle_damage="rebuild_and_reflow", )} + +${helpers.predefined_type( + "counter-set", + "CounterSetOrReset", + initial_value="Default::default()", + animation_value_type="discrete", + spec="https://drafts.csswg.org/css-lists-3/#propdef-counter-set", + servo_restyle_damage="rebuild_and_reflow", + products="gecko", +)} diff --git a/components/style/properties/longhands/font.mako.rs b/components/style/properties/longhands/font.mako.rs index 047ec90c480..88369ab9f21 100644 --- a/components/style/properties/longhands/font.mako.rs +++ b/components/style/properties/longhands/font.mako.rs @@ -379,7 +379,7 @@ ${helpers.predefined_type( use crate::gecko_bindings::structs::{LookAndFeel_FontID, nsFont}; use std::mem; use crate::values::computed::Percentage; - use crate::values::computed::font::{FontSize, FontStretch, FontStyle, FontFamilyList}; + use crate::values::computed::font::{FontFamily, FontSize, FontStretch, FontStyle, FontFamilyList}; use crate::values::generics::NonNegative; let id = match *self { @@ -405,11 +405,12 @@ ${helpers.predefined_type( }))); let font_style = FontStyle::from_gecko(system.style); let ret = ComputedSystemFont { - font_family: longhands::font_family::computed_value::T( - FontFamilyList( - unsafe { system.fontlist.mFontlist.mBasePtr.to_safe() } - ) - ), + font_family: FontFamily { + families: FontFamilyList(unsafe { + system.fontlist.mFontlist.mBasePtr.to_safe() + }), + is_system_font: true, + }, font_size: FontSize { size: Au(system.size).into(), keyword_info: None diff --git a/components/style/properties/longhands/list.mako.rs b/components/style/properties/longhands/list.mako.rs index 4b368ea2ba5..6d79d3ad9ee 100644 --- a/components/style/properties/longhands/list.mako.rs +++ b/components/style/properties/longhands/list.mako.rs @@ -68,3 +68,15 @@ ${helpers.predefined_type( boxed=True, spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-image-region)", )} + +${helpers.predefined_type( + "-moz-list-reversed", + "MozListReversed", + "computed::MozListReversed::False", + animation_value_type="discrete", + products="gecko", + enabled_in="ua", + needs_context=False, + spec="Internal implementation detail for <ol reversed>", + servo_restyle_damage="rebuild_and_reflow", +)} diff --git a/components/style/properties/longhands/text.mako.rs b/components/style/properties/longhands/text.mako.rs index 308de1b7575..723e26ae22b 100644 --- a/components/style/properties/longhands/text.mako.rs +++ b/components/style/properties/longhands/text.mako.rs @@ -5,16 +5,7 @@ <%namespace name="helpers" file="/helpers.mako.rs" /> <% from data import Method %> -<% data.new_style_struct( - "Text", - inherited=False, - gecko_name="TextReset", - additional_methods=[ - Method("has_underline", "bool"), - Method("has_overline", "bool"), - Method("has_line_through", "bool"), - ] -) %> +<% data.new_style_struct("Text", inherited=False, gecko_name="TextReset") %> ${helpers.predefined_type( "text-overflow", diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 7e538c4a662..c9bc2bd8e2f 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -45,6 +45,7 @@ use crate::rule_tree::StrongRuleNode; use crate::Zero; use self::computed_value_flags::*; use crate::str::{CssString, CssStringBorrow, CssStringWriter}; +use std::cell::Cell; pub use self::declaration_block::*; pub use self::cascade::*; @@ -1257,8 +1258,12 @@ impl LonghandId { LonghandId::MozScriptLevel | % endif - // Needed to compute font-relative lengths correctly. + // Needed to compute the first available font, in order to + // compute font-relative units correctly. LonghandId::FontSize | + LonghandId::FontWeight | + LonghandId::FontStretch | + LonghandId::FontStyle | LonghandId::FontFamily | // Needed to resolve currentcolor at computed value time properly. @@ -2634,24 +2639,6 @@ pub mod style_structs { use crate::Zero; !self.outline_width.is_zero() } - % elif style_struct.name == "Text": - /// Whether the text decoration has an underline. - #[inline] - pub fn has_underline(&self) -> bool { - self.text_decoration_line.contains(longhands::text_decoration_line::SpecifiedValue::UNDERLINE) - } - - /// Whether the text decoration has an overline. - #[inline] - pub fn has_overline(&self) -> bool { - self.text_decoration_line.contains(longhands::text_decoration_line::SpecifiedValue::OVERLINE) - } - - /// Whether the text decoration has a line through. - #[inline] - pub fn has_line_through(&self) -> bool { - self.text_decoration_line.contains(longhands::text_decoration_line::SpecifiedValue::LINE_THROUGH) - } % elif style_struct.name == "Box": /// Sets the display property, but without touching original_display, /// except when the adjustment comes from root or item display fixups. @@ -3336,8 +3323,10 @@ pub struct StyleBuilder<'a> { /// /// TODO(emilio): Make private. pub writing_mode: WritingMode, + /// Flags for the computed value. - pub flags: ComputedValueFlags, + pub flags: Cell<ComputedValueFlags>, + /// The element's style if visited, only computed if there's a relevant link /// for this element. A element's "relevant link" is the element being /// matched if it is a link or the nearest ancestor link. @@ -3379,7 +3368,7 @@ impl<'a> StyleBuilder<'a> { modified_reset: false, custom_properties, writing_mode: inherited_style.writing_mode, - flags, + flags: Cell::new(flags), visited_style: None, % for style_struct in data.active_style_structs(): % if style_struct.inherited: @@ -3418,7 +3407,7 @@ impl<'a> StyleBuilder<'a> { rules: None, custom_properties: style_to_derive_from.custom_properties().cloned(), writing_mode: style_to_derive_from.writing_mode, - flags: style_to_derive_from.flags, + flags: Cell::new(style_to_derive_from.flags), visited_style: None, % for style_struct in data.active_style_structs(): ${style_struct.ident}: StyleStructRef::Borrowed( @@ -3448,14 +3437,14 @@ impl<'a> StyleBuilder<'a> { .get_${property.style_struct.name_lower}(); self.modified_reset = true; - self.flags.insert(ComputedValueFlags::INHERITS_RESET_STYLE); + self.add_flags(ComputedValueFlags::INHERITS_RESET_STYLE); % if property.ident == "content": - self.flags.insert(ComputedValueFlags::INHERITS_CONTENT); + self.add_flags(ComputedValueFlags::INHERITS_CONTENT); % endif % if property.ident == "display": - self.flags.insert(ComputedValueFlags::INHERITS_DISPLAY); + self.add_flags(ComputedValueFlags::INHERITS_DISPLAY); % endif if self.${property.style_struct.ident}.ptr_eq(inherited_struct) { @@ -3470,7 +3459,7 @@ impl<'a> StyleBuilder<'a> { % endif ); } - % elif property.name != "font-size": + % else: /// Reset `${property.ident}` to the initial value. #[allow(non_snake_case)] pub fn reset_${property.ident}(&mut self) { @@ -3642,13 +3631,33 @@ impl<'a> StyleBuilder<'a> { self.modified_reset } + /// Return the current flags. + #[inline] + pub fn flags(&self) -> ComputedValueFlags { + self.flags.get() + } + + /// Add a flag to the current builder. + #[inline] + pub fn add_flags(&self, flag: ComputedValueFlags) { + let flags = self.flags() | flag; + self.flags.set(flags); + } + + /// Removes a flag to the current builder. + #[inline] + pub fn remove_flags(&self, flag: ComputedValueFlags) { + let flags = self.flags() & !flag; + self.flags.set(flags); + } + /// Turns this `StyleBuilder` into a proper `ComputedValues` instance. pub fn build(self) -> Arc<ComputedValues> { ComputedValues::new( self.pseudo, self.custom_properties, self.writing_mode, - self.flags, + self.flags.get(), self.rules, self.visited_style, % for style_struct in data.active_style_structs(): @@ -3710,8 +3719,8 @@ mod lazy_static_module { use super::{ComputedValues, ComputedValuesInner, longhands, style_structs}; use super::computed_value_flags::ComputedValueFlags; - /// The initial values for all style structs as defined by the specification. lazy_static! { + /// The initial values for all style structs as defined by the specification. pub static ref INITIAL_SERVO_VALUES: ComputedValues = ComputedValues { inner: ComputedValuesInner { % for style_struct in data.active_style_structs(): diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs index 9abfe976919..229f7dccdd2 100644 --- a/components/style/servo/selector_parser.rs +++ b/components/style/servo/selector_parser.rs @@ -142,6 +142,12 @@ impl PseudoElement { false } + /// Whether this pseudo-element is the ::marker pseudo. + #[inline] + pub fn is_marker(&self) -> bool { + false + } + /// Whether this pseudo-element is the ::before pseudo. #[inline] pub fn is_before(&self) -> bool { diff --git a/components/style/style_adjuster.rs b/components/style/style_adjuster.rs index 248d0ad0661..070a2299969 100644 --- a/components/style/style_adjuster.rs +++ b/components/style/style_adjuster.rs @@ -174,10 +174,14 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { /// Apply the blockification rules based on the table in CSS 2.2 section 9.7. /// <https://drafts.csswg.org/css2/visuren.html#dis-pos-flo> + /// A ::marker pseudo-element with 'list-style-position:outside' needs to + /// have its 'display' blockified. fn blockify_if_necessary<E>(&mut self, layout_parent_style: &ComputedValues, element: Option<E>) where E: TElement, { + use crate::computed_values::list_style_position::T as ListStylePosition; + let mut blockify = false; macro_rules! blockify_if { ($if_what:expr) => { @@ -200,6 +204,11 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { blockify_if!(self.style.floated()); blockify_if!(self.style.out_of_flow_positioned()); + blockify_if!( + self.style.pseudo.map_or(false, |p| p.is_marker()) && + self.style.get_parent_list().clone_list_style_position() == + ListStylePosition::Outside + ); if !blockify { return; @@ -226,22 +235,18 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { .is_empty() { self.style - .flags - .insert(ComputedValueFlags::HAS_TEXT_DECORATION_LINES); + .add_flags(ComputedValueFlags::HAS_TEXT_DECORATION_LINES); } if self.style.is_pseudo_element() { self.style - .flags - .insert(ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE); + .add_flags(ComputedValueFlags::IS_IN_PSEUDO_ELEMENT_SUBTREE); } #[cfg(feature = "servo")] { if self.style.get_parent_column().is_multicol() { - self.style - .flags - .insert(ComputedValueFlags::CAN_BE_FRAGMENTED); + self.style.add_flags(ComputedValueFlags::CAN_BE_FRAGMENTED); } } } @@ -280,9 +285,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { if writing_mode != WritingMode::HorizontalTb && text_combine_upright == TextCombineUpright::All { - self.style - .flags - .insert(ComputedValueFlags::IS_TEXT_COMBINED); + self.style.add_flags(ComputedValueFlags::IS_TEXT_COMBINED); self.style .mutate_inherited_box() .set_writing_mode(WritingMode::HorizontalTb); @@ -304,8 +307,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { .contains(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK) { self.style - .flags - .insert(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK); + .add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK); } } @@ -592,8 +594,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { // Check whether line break should be suppressed for this element. if self.should_suppress_linebreak(layout_parent_style) { self.style - .flags - .insert(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK); + .add_flags(ComputedValueFlags::SHOULD_SUPPRESS_LINEBREAK); // Inlinify the display type if allowed. if !self.skip_item_display_fixup(element) { let inline_display = self_display.inlinify(); @@ -652,13 +653,11 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { if element.unwrap().is_visited_link() { self.style - .flags - .insert(ComputedValueFlags::IS_RELEVANT_LINK_VISITED); + .add_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED); } else { // Need to remove to handle unvisited link inside visited. self.style - .flags - .remove(ComputedValueFlags::IS_RELEVANT_LINK_VISITED); + .remove_flags(ComputedValueFlags::IS_RELEVANT_LINK_VISITED); } } @@ -724,6 +723,52 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { } } + /// For HTML elements with 'display:list-item' we add a default 'counter-increment:list-item' + /// unless 'counter-increment' already has a value for 'list-item'. + /// + /// https://drafts.csswg.org/css-lists-3/#declaring-a-list-item + #[cfg(feature = "gecko")] + fn adjust_for_list_item<E>(&mut self, element: Option<E>) + where + E: TElement, + { + use crate::properties::longhands::counter_increment::computed_value::T as ComputedIncrement; + use crate::values::generics::counters::CounterPair; + use crate::values::specified::list::MozListReversed; + use crate::values::CustomIdent; + + if self.style.get_box().clone_display() != Display::ListItem { + return; + } + if self.style.pseudo.is_some() { + return; + } + if !element.map_or(false, |e| e.is_html_element()) { + return; + } + // Note that we map <li value=INTEGER> to 'counter-set: list-item INTEGER; + // counter-increment: list-item 0;' so we'll return here unless the author + // explicitly specified something else. + let increments = self.style.get_counters().clone_counter_increment(); + if increments.iter().any(|i| i.name.0 == atom!("list-item")) { + return; + } + + let reversed = self.style.get_list().clone__moz_list_reversed() == MozListReversed::True; + let increment = if reversed { -1 } else { 1 }; + let list_increment = CounterPair { + name: CustomIdent(atom!("list-item")), + value: increment, + }; + let increments = increments + .iter() + .cloned() + .chain(std::iter::once(list_increment)); + self.style + .mutate_counters() + .set_counter_increment(ComputedIncrement::new(increments.collect())); + } + /// Adjusts the style to account for various fixups that don't fit naturally /// into the cascade. /// @@ -788,6 +833,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { #[cfg(feature = "gecko")] { self.adjust_for_appearance(element); + self.adjust_for_list_item(element); } self.set_bits(); } diff --git a/components/style/stylesheets/rule_parser.rs b/components/style/stylesheets/rule_parser.rs index 44943259b38..a780ebce250 100644 --- a/components/style/stylesheets/rule_parser.rs +++ b/components/style/stylesheets/rule_parser.rs @@ -189,6 +189,13 @@ impl<'a, 'i> AtRuleParser<'i> for TopLevelRuleParser<'a> { return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule)) } + // FIXME(emilio): We should always be able to have a loader + // around! See bug 1533783. + if self.loader.is_none() { + error!("Saw @import rule, but no way to trigger the load"); + return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedImportRule)) + } + let url_string = input.expect_url_or_string()?.as_ref().to_owned(); let url = CssUrl::parse_from_string(url_string, &self.context); diff --git a/components/style/stylesheets/viewport_rule.rs b/components/style/stylesheets/viewport_rule.rs index 3d7b29f19bf..02be35261db 100644 --- a/components/style/stylesheets/viewport_rule.rs +++ b/components/style/stylesheets/viewport_rule.rs @@ -27,7 +27,6 @@ use cssparser::CowRcStr; use cssparser::{parse_important, AtRuleParser, DeclarationListParser, DeclarationParser, Parser}; use euclid::TypedSize2D; use selectors::parser::SelectorParseErrorKind; -use servo_config::pref; use std::borrow::Cow; use std::cell::RefCell; use std::fmt::{self, Write}; @@ -39,6 +38,7 @@ use style_traits::{CssWriter, ParseError, PinchZoomFactor, StyleParseErrorKind, /// Whether parsing and processing of `@viewport` rules is enabled. #[cfg(feature = "servo")] pub fn enabled() -> bool { + use servo_config::pref; pref!(layout.viewport.enabled) } diff --git a/components/style/values/computed/counters.rs b/components/style/values/computed/counters.rs index aaf83afe485..3a083632eb9 100644 --- a/components/style/values/computed/counters.rs +++ b/components/style/values/computed/counters.rs @@ -7,13 +7,13 @@ use crate::values::computed::url::ComputedImageUrl; use crate::values::generics::counters as generics; use crate::values::generics::counters::CounterIncrement as GenericCounterIncrement; -use crate::values::generics::counters::CounterReset as GenericCounterReset; +use crate::values::generics::counters::CounterSetOrReset as GenericCounterSetOrReset; /// A computed value for the `counter-increment` property. pub type CounterIncrement = GenericCounterIncrement<i32>; -/// A computed value for the `counter-increment` property. -pub type CounterReset = GenericCounterReset<i32>; +/// A computed value for the `counter-set` and `counter-reset` properties. +pub type CounterSetOrReset = GenericCounterSetOrReset<i32>; /// A computed value for the `content` property. pub type Content = generics::Content<ComputedImageUrl>; diff --git a/components/style/values/computed/font.rs b/components/style/values/computed/font.rs index 765efd9a0d8..c78eb9df2e3 100644 --- a/components/style/values/computed/font.rs +++ b/components/style/values/computed/font.rs @@ -152,49 +152,6 @@ impl FontSize { keyword_info: Some(KeywordInfo::medium()), } } - - /// FIXME(emilio): This is very complex. Also, it should move to - /// StyleBuilder. - pub fn cascade_inherit_font_size(context: &mut Context) { - // If inheriting, we must recompute font-size in case of language - // changes using the font_size_keyword. We also need to do this to - // handle mathml scriptlevel changes - let kw_inherited_size = context - .builder - .get_parent_font() - .clone_font_size() - .keyword_info - .map(|info| { - specified::FontSize::Keyword(info) - .to_computed_value(context) - .size - }); - let mut font = context.builder.take_font(); - font.inherit_font_size_from( - context.builder.get_parent_font(), - kw_inherited_size, - context.builder.device, - ); - context.builder.put_font(font); - } - - /// Cascade the initial value for the `font-size` property. - /// - /// FIXME(emilio): This is the only function that is outside of the - /// `StyleBuilder`, and should really move inside! - /// - /// Can we move the font stuff there? - pub fn cascade_initial_font_size(context: &mut Context) { - // font-size's default ("medium") does not always - // compute to the same value and depends on the font - let computed = specified::FontSize::medium().to_computed_value(context); - context.builder.mutate_font().set_font_size(computed); - #[cfg(feature = "gecko")] - { - let device = context.builder.device; - context.builder.mutate_font().fixup_font_min_size(device); - } - } } /// XXXManishearth it might be better to @@ -221,15 +178,21 @@ impl ToAnimatedValue for FontSize { #[derive(Clone, Debug, Eq, Hash, PartialEq)] #[cfg_attr(feature = "servo", derive(MallocSizeOf))] /// Specifies a prioritized list of font family names or generic family names. -pub struct FontFamily(pub FontFamilyList); +pub struct FontFamily { + /// The actual list of family names. + pub families: FontFamilyList, + /// Whether this font-family came from a specified system-font. + pub is_system_font: bool, +} impl FontFamily { #[inline] /// Get default font family as `serif` which is a generic font-family pub fn serif() -> Self { - FontFamily(FontFamilyList::new(Box::new([SingleFontFamily::Generic( - atom!("serif"), - )]))) + FontFamily { + families: FontFamilyList::new(Box::new([SingleFontFamily::Generic(atom!("serif"))])), + is_system_font: false, + } } } @@ -239,7 +202,9 @@ impl MallocSizeOf for FontFamily { // SharedFontList objects are generally shared from the pointer // stored in the specified value. So only count this if the // SharedFontList is unshared. - unsafe { bindings::Gecko_SharedFontList_SizeOfIncludingThisIfUnshared((self.0).0.get()) } + unsafe { + bindings::Gecko_SharedFontList_SizeOfIncludingThisIfUnshared(self.families.0.get()) + } } } @@ -248,7 +213,7 @@ impl ToCss for FontFamily { where W: fmt::Write, { - let mut iter = self.0.iter(); + let mut iter = self.families.iter(); iter.next().unwrap().to_css(dest)?; for family in iter { dest.write_str(", ")?; diff --git a/components/style/values/computed/list.rs b/components/style/values/computed/list.rs index 622c021554f..2bde35e3b6b 100644 --- a/components/style/values/computed/list.rs +++ b/components/style/values/computed/list.rs @@ -6,6 +6,7 @@ #[cfg(feature = "gecko")] pub use crate::values::specified::list::ListStyleType; +pub use crate::values::specified::list::MozListReversed; pub use crate::values::specified::list::{QuotePair, Quotes}; use servo_arc::Arc; diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 1f7d1e3eddc..4c92218e69b 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -45,7 +45,7 @@ pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize}; pub use self::box_::{ScrollSnapAlign, ScrollSnapType, TouchAction, VerticalAlign, WillChange}; pub use self::color::{Color, ColorOrAuto, ColorPropertyValue, RGBAColor}; pub use self::column::ColumnCount; -pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset}; +pub use self::counters::{Content, ContentItem, CounterIncrement, CounterSetOrReset}; pub use self::easing::TimingFunction; pub use self::effects::{BoxShadow, Filter, SimpleShadow}; pub use self::flex::FlexBasis; @@ -64,6 +64,7 @@ pub use self::length::{LengthOrAuto, LengthPercentageOrAuto, MaxSize, Size}; pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto}; #[cfg(feature = "gecko")] pub use self::list::ListStyleType; +pub use self::list::MozListReversed; pub use self::list::{QuotePair, Quotes}; pub use self::motion::OffsetPath; pub use self::outline::OutlineStyle; diff --git a/components/style/values/computed/text.rs b/components/style/values/computed/text.rs index 911891bf33b..db56a16a82d 100644 --- a/components/style/values/computed/text.rs +++ b/components/style/values/computed/text.rs @@ -19,8 +19,8 @@ use std::fmt::{self, Write}; use style_traits::{CssWriter, ToCss}; pub use crate::values::specified::TextAlignKeyword as TextAlign; -pub use crate::values::specified::TextEmphasisPosition; pub use crate::values::specified::{OverflowWrap, WordBreak}; +pub use crate::values::specified::{TextDecorationLine, TextEmphasisPosition}; /// A computed value for the `initial-letter` property. pub type InitialLetter = GenericInitialLetter<CSSFloat, CSSInteger>; @@ -182,11 +182,11 @@ impl TextDecorationsInEffect { .clone(), }; - let text_style = style.get_text(); + let line = style.get_text().clone_text_decoration_line(); - result.underline |= text_style.has_underline(); - result.overline |= text_style.has_overline(); - result.line_through |= text_style.has_line_through(); + result.underline |= line.contains(TextDecorationLine::UNDERLINE); + result.overline |= line.contains(TextDecorationLine::OVERLINE); + result.line_through |= line.contains(TextDecorationLine::LINE_THROUGH); result } diff --git a/components/style/values/generics/counters.rs b/components/style/values/generics/counters.rs index 0f5d6bbb939..7bab3c4bbc5 100644 --- a/components/style/values/generics/counters.rs +++ b/components/style/values/generics/counters.rs @@ -45,21 +45,21 @@ impl<I> Deref for CounterIncrement<I> { } } -/// A generic value for the `counter-reset` property. +/// A generic value for the `counter-set` and `counter-reset` properties. #[derive( Clone, Debug, Default, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss, )] -pub struct CounterReset<I>(Counters<I>); +pub struct CounterSetOrReset<I>(Counters<I>); -impl<I> CounterReset<I> { - /// Returns a new value for `counter-reset`. +impl<I> CounterSetOrReset<I> { + /// Returns a new value for `counter-set` / `counter-reset`. #[inline] pub fn new(counters: Vec<CounterPair<I>>) -> Self { - CounterReset(Counters(counters.into_boxed_slice())) + CounterSetOrReset(Counters(counters.into_boxed_slice())) } } -impl<I> Deref for CounterReset<I> { +impl<I> Deref for CounterSetOrReset<I> { type Target = [CounterPair<I>]; #[inline] diff --git a/components/style/values/specified/box.rs b/components/style/values/specified/box.rs index d4b66e42ab9..ab81d4c3450 100644 --- a/components/style/values/specified/box.rs +++ b/components/style/values/specified/box.rs @@ -555,9 +555,8 @@ impl WillChange { bitflags! { /// The change bits that we care about. - /// - /// These need to be in sync with NS_STYLE_WILL_CHANGE_*. #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue)] + #[repr(C)] pub struct WillChangeBits: u8 { /// Whether the stacking context will change. const STACKING_CONTEXT = 1 << 0; @@ -616,7 +615,7 @@ impl Parse for WillChange { fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, - ) -> Result<WillChange, ParseError<'i>> { + ) -> Result<Self, ParseError<'i>> { if input .try(|input| input.expect_ident_matching("auto")) .is_ok() @@ -650,21 +649,22 @@ impl Parse for WillChange { } bitflags! { + /// Values for the `touch-action` property. #[cfg_attr(feature = "gecko", derive(MallocSizeOf))] #[derive(SpecifiedValueInfo, ToComputedValue)] - /// These constants match Gecko's `NS_STYLE_TOUCH_ACTION_*` constants. #[value_info(other_values = "auto,none,manipulation,pan-x,pan-y")] + #[repr(C)] pub struct TouchAction: u8 { /// `none` variant - const TOUCH_ACTION_NONE = 1 << 0; + const NONE = 1 << 0; /// `auto` variant - const TOUCH_ACTION_AUTO = 1 << 1; + const AUTO = 1 << 1; /// `pan-x` variant - const TOUCH_ACTION_PAN_X = 1 << 2; + const PAN_X = 1 << 2; /// `pan-y` variant - const TOUCH_ACTION_PAN_Y = 1 << 3; + const PAN_Y = 1 << 3; /// `manipulation` variant - const TOUCH_ACTION_MANIPULATION = 1 << 4; + const MANIPULATION = 1 << 4; } } @@ -672,7 +672,7 @@ impl TouchAction { #[inline] /// Get default `touch-action` as `auto` pub fn auto() -> TouchAction { - TouchAction::TOUCH_ACTION_AUTO + TouchAction::AUTO } } @@ -682,16 +682,14 @@ impl ToCss for TouchAction { W: Write, { match *self { - TouchAction::TOUCH_ACTION_NONE => dest.write_str("none"), - TouchAction::TOUCH_ACTION_AUTO => dest.write_str("auto"), - TouchAction::TOUCH_ACTION_MANIPULATION => dest.write_str("manipulation"), - _ if self - .contains(TouchAction::TOUCH_ACTION_PAN_X | TouchAction::TOUCH_ACTION_PAN_Y) => - { + TouchAction::NONE => dest.write_str("none"), + TouchAction::AUTO => dest.write_str("auto"), + TouchAction::MANIPULATION => dest.write_str("manipulation"), + _ if self.contains(TouchAction::PAN_X | TouchAction::PAN_Y) => { dest.write_str("pan-x pan-y") }, - _ if self.contains(TouchAction::TOUCH_ACTION_PAN_X) => dest.write_str("pan-x"), - _ if self.contains(TouchAction::TOUCH_ACTION_PAN_Y) => dest.write_str("pan-y"), + _ if self.contains(TouchAction::PAN_X) => dest.write_str("pan-x"), + _ if self.contains(TouchAction::PAN_Y) => dest.write_str("pan-y"), _ => panic!("invalid touch-action value"), } } @@ -703,68 +701,45 @@ impl Parse for TouchAction { input: &mut Parser<'i, 't>, ) -> Result<TouchAction, ParseError<'i>> { try_match_ident_ignore_ascii_case! { input, - "auto" => Ok(TouchAction::TOUCH_ACTION_AUTO), - "none" => Ok(TouchAction::TOUCH_ACTION_NONE), - "manipulation" => Ok(TouchAction::TOUCH_ACTION_MANIPULATION), + "auto" => Ok(TouchAction::AUTO), + "none" => Ok(TouchAction::NONE), + "manipulation" => Ok(TouchAction::MANIPULATION), "pan-x" => { if input.try(|i| i.expect_ident_matching("pan-y")).is_ok() { - Ok(TouchAction::TOUCH_ACTION_PAN_X | TouchAction::TOUCH_ACTION_PAN_Y) + Ok(TouchAction::PAN_X | TouchAction::PAN_Y) } else { - Ok(TouchAction::TOUCH_ACTION_PAN_X) + Ok(TouchAction::PAN_X) } }, "pan-y" => { if input.try(|i| i.expect_ident_matching("pan-x")).is_ok() { - Ok(TouchAction::TOUCH_ACTION_PAN_X | TouchAction::TOUCH_ACTION_PAN_Y) + Ok(TouchAction::PAN_X | TouchAction::PAN_Y) } else { - Ok(TouchAction::TOUCH_ACTION_PAN_Y) + Ok(TouchAction::PAN_Y) } }, } } } -#[cfg(feature = "gecko")] -impl_bitflags_conversions!(TouchAction); - -/// Asserts that all touch-action matches its NS_STYLE_TOUCH_ACTION_* value. -#[cfg(feature = "gecko")] -#[inline] -pub fn assert_touch_action_matches() { - use crate::gecko_bindings::structs; - - macro_rules! check_touch_action { - ( $( $a:ident => $b:path),*, ) => { - $( - debug_assert_eq!(structs::$a as u8, $b.bits()); - )* - } - } - - check_touch_action! { - NS_STYLE_TOUCH_ACTION_NONE => TouchAction::TOUCH_ACTION_NONE, - NS_STYLE_TOUCH_ACTION_AUTO => TouchAction::TOUCH_ACTION_AUTO, - NS_STYLE_TOUCH_ACTION_PAN_X => TouchAction::TOUCH_ACTION_PAN_X, - NS_STYLE_TOUCH_ACTION_PAN_Y => TouchAction::TOUCH_ACTION_PAN_Y, - NS_STYLE_TOUCH_ACTION_MANIPULATION => TouchAction::TOUCH_ACTION_MANIPULATION, - } -} - bitflags! { #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue)] #[value_info(other_values = "none,strict,content,size,layout,paint")] + #[repr(C)] /// Constants for contain: https://drafts.csswg.org/css-contain/#contain-property pub struct Contain: u8 { + /// `none` variant, just for convenience. + const NONE = 0; /// 'size' variant, turns on size containment - const SIZE = 0x01; + const SIZE = 1 << 0; /// `layout` variant, turns on layout containment - const LAYOUT = 0x02; + const LAYOUT = 1 << 1; /// `paint` variant, turns on paint containment - const PAINT = 0x04; + const PAINT = 1 << 2; /// `strict` variant, turns on all types of containment - const STRICT = 0x08; + const STRICT = 1 << 3; /// 'content' variant, turns on layout and paint containment - const CONTENT = 0x10; + const CONTENT = 1 << 4; /// variant with all the bits that contain: strict turns on const STRICT_BITS = Contain::LAYOUT.bits | Contain::PAINT.bits | Contain::SIZE.bits; /// variant with all the bits that contain: content turns on diff --git a/components/style/values/specified/counters.rs b/components/style/values/specified/counters.rs index 6ee6575fc94..262e7765a4a 100644 --- a/components/style/values/specified/counters.rs +++ b/components/style/values/specified/counters.rs @@ -10,7 +10,7 @@ use crate::parser::{Parse, ParserContext}; use crate::values::generics::counters as generics; use crate::values::generics::counters::CounterIncrement as GenericCounterIncrement; use crate::values::generics::counters::CounterPair; -use crate::values::generics::counters::CounterReset as GenericCounterReset; +use crate::values::generics::counters::CounterSetOrReset as GenericCounterSetOrReset; #[cfg(feature = "gecko")] use crate::values::generics::CounterStyleOrNone; use crate::values::specified::url::SpecifiedImageUrl; @@ -34,10 +34,10 @@ impl Parse for CounterIncrement { } } -/// A specified value for the `counter-increment` property. -pub type CounterReset = GenericCounterReset<Integer>; +/// A specified value for the `counter-set` and `counter-reset` properties. +pub type CounterSetOrReset = GenericCounterSetOrReset<Integer>; -impl Parse for CounterReset { +impl Parse for CounterSetOrReset { fn parse<'i, 't>( context: &ParserContext, input: &mut Parser<'i, 't>, diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs index 0344af275ce..6c92acbf1b9 100644 --- a/components/style/values/specified/font.rs +++ b/components/style/values/specified/font.rs @@ -548,13 +548,16 @@ impl ToComputedValue for FontFamily { fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { match *self { - FontFamily::Values(ref v) => computed::FontFamily(v.clone()), + FontFamily::Values(ref v) => computed::FontFamily { + families: v.clone(), + is_system_font: false, + }, FontFamily::System(_) => self.compute_system(context), } } fn from_computed_value(other: &computed::FontFamily) -> Self { - FontFamily::Values(other.0.clone()) + FontFamily::Values(other.families.clone()) } } @@ -958,48 +961,6 @@ impl FontSize { "larger" => Ok(FontSize::Larger), } } - - #[allow(unused_mut)] - /// Cascade `font-size` with specified value - pub fn cascade_specified_font_size( - context: &mut Context, - specified_value: &FontSize, - mut computed: computed::FontSize, - ) { - // we could use clone_language and clone_font_family() here but that's - // expensive. Do it only in gecko mode for now. - #[cfg(feature = "gecko")] - { - // if the language or generic changed, we need to recalculate - // the font size from the stored font-size origin information. - if context.builder.get_font().gecko().mLanguage.mRawPtr != - context.builder.get_parent_font().gecko().mLanguage.mRawPtr || - context.builder.get_font().gecko().mGenericID != - context.builder.get_parent_font().gecko().mGenericID - { - if let Some(info) = computed.keyword_info { - computed.size = info.to_computed_value(context); - } - } - } - - let device = context.builder.device; - let mut font = context.builder.take_font(); - let parent_unconstrained = { - let parent_font = context.builder.get_parent_font(); - font.apply_font_size(computed, parent_font, device) - }; - context.builder.put_font(font); - - if let Some(parent) = parent_unconstrained { - let new_unconstrained = specified_value - .to_computed_value_against(context, FontBaseSize::Custom(Au::from(parent))); - context - .builder - .mutate_font() - .apply_unconstrained_font_size(new_unconstrained.size); - } - } } impl Parse for FontSize { diff --git a/components/style/values/specified/length.rs b/components/style/values/specified/length.rs index acee43f4b38..631de5c87ec 100644 --- a/components/style/values/specified/length.rs +++ b/components/style/values/specified/length.rs @@ -7,8 +7,9 @@ //! [length]: https://drafts.csswg.org/css-values/#lengths use super::{AllowQuirks, Number, Percentage, ToComputedValue}; -use crate::font_metrics::FontMetricsQueryResult; +use crate::font_metrics::{FontMetrics, FontMetricsOrientation}; use crate::parser::{Parse, ParserContext}; +use crate::properties::computed_value_flags::ComputedValueFlags; use crate::values::computed::{self, CSSPixelLength, Context}; use crate::values::generics::length as generics; use crate::values::generics::length::{ @@ -82,17 +83,12 @@ pub enum FontBaseSize { /// /// FIXME(emilio): This is very complex, and should go away. InheritedStyleButStripEmUnits, - /// Use a custom base size. - /// - /// FIXME(emilio): This is very dubious, and only used for MathML. - Custom(Au), } impl FontBaseSize { /// Calculate the actual size for a given context pub fn resolve(&self, context: &Context) -> Au { match *self { - FontBaseSize::Custom(size) => size, FontBaseSize::CurrentStyle => context.style().get_font().clone_font_size().size(), FontBaseSize::InheritedStyleButStripEmUnits | FontBaseSize::InheritedStyle => { context.style().get_parent_font().clone_font_size().size() @@ -136,15 +132,12 @@ impl FontRelativeLength { ) -> (Au, CSSFloat) { fn query_font_metrics( context: &Context, - reference_font_size: Au, - ) -> FontMetricsQueryResult { - context.font_metrics_provider.query( - context.style().get_font(), - reference_font_size, - context.style().writing_mode, - context.in_media_query, - context.device(), - ) + base_size: FontBaseSize, + orientation: FontMetricsOrientation, + ) -> FontMetrics { + context + .font_metrics_provider + .query(context, base_size, orientation) } let reference_font_size = base_size.resolve(context); @@ -169,24 +162,40 @@ impl FontRelativeLength { if context.for_non_inherited_property.is_some() { context.rule_cache_conditions.borrow_mut().set_uncacheable(); } - let reference_size = match query_font_metrics(context, reference_font_size) { - FontMetricsQueryResult::Available(metrics) => metrics.x_height, + context + .builder + .add_flags(ComputedValueFlags::DEPENDS_ON_FONT_METRICS); + // The x-height is an intrinsically horizontal metric. + let metrics = + query_font_metrics(context, base_size, FontMetricsOrientation::Horizontal); + let reference_size = metrics.x_height.unwrap_or_else(|| { // https://drafts.csswg.org/css-values/#ex // // In the cases where it is impossible or impractical to // determine the x-height, a value of 0.5em must be // assumed. // - FontMetricsQueryResult::NotAvailable => reference_font_size.scale_by(0.5), - }; + reference_font_size.scale_by(0.5) + }); (reference_size, length) }, FontRelativeLength::Ch(length) => { if context.for_non_inherited_property.is_some() { context.rule_cache_conditions.borrow_mut().set_uncacheable(); } - let reference_size = match query_font_metrics(context, reference_font_size) { - FontMetricsQueryResult::Available(metrics) => metrics.zero_advance_measure, + context + .builder + .add_flags(ComputedValueFlags::DEPENDS_ON_FONT_METRICS); + // https://drafts.csswg.org/css-values/#ch: + // + // Equal to the used advance measure of the “0” (ZERO, + // U+0030) glyph in the font used to render it. (The advance + // measure of a glyph is its advance width or height, + // whichever is in the inline axis of the element.) + // + let metrics = + query_font_metrics(context, base_size, FontMetricsOrientation::MatchContext); + let reference_size = metrics.zero_advance_measure.unwrap_or_else(|| { // https://drafts.csswg.org/css-values/#ch // // In the cases where it is impossible or impractical to @@ -197,14 +206,13 @@ impl FontRelativeLength { // writing-mode is vertical-rl or vertical-lr and // text-orientation is upright). // - FontMetricsQueryResult::NotAvailable => { - if context.style().writing_mode.is_vertical() { - reference_font_size - } else { - reference_font_size.scale_by(0.5) - } - }, - }; + let wm = context.style().writing_mode; + if wm.is_vertical() && wm.is_upright() { + reference_font_size + } else { + reference_font_size.scale_by(0.5) + } + }); (reference_size, length) }, FontRelativeLength::Rem(length) => { diff --git a/components/style/values/specified/list.rs b/components/style/values/specified/list.rs index b37ade47081..dffce9a3001 100644 --- a/components/style/values/specified/list.rs +++ b/components/style/values/specified/list.rs @@ -123,3 +123,25 @@ impl Parse for Quotes { } } } + +/// Specified and computed `-moz-list-reversed` property (for UA sheets only). +#[derive( + Clone, + Copy, + Debug, + Eq, + Hash, + MallocSizeOf, + Parse, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToCss, +)] +#[repr(u8)] +pub enum MozListReversed { + /// the initial value + False, + /// exclusively used for <ol reversed> in our html.css UA sheet + True, +} diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 7295f060697..919cb0cb341 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -44,7 +44,7 @@ pub use self::box_::{ScrollSnapAlign, ScrollSnapType}; pub use self::box_::{TouchAction, TransitionProperty, VerticalAlign, WillChange}; pub use self::color::{Color, ColorOrAuto, ColorPropertyValue, RGBAColor}; pub use self::column::ColumnCount; -pub use self::counters::{Content, ContentItem, CounterIncrement, CounterReset}; +pub use self::counters::{Content, ContentItem, CounterIncrement, CounterSetOrReset}; pub use self::easing::TimingFunction; pub use self::effects::{BoxShadow, Filter, SimpleShadow}; pub use self::flex::FlexBasis; @@ -66,6 +66,7 @@ pub use self::length::{NoCalcLength, ViewportPercentageLength}; pub use self::length::{NonNegativeLengthPercentage, NonNegativeLengthPercentageOrAuto}; #[cfg(feature = "gecko")] pub use self::list::ListStyleType; +pub use self::list::MozListReversed; pub use self::list::{QuotePair, Quotes}; pub use self::motion::OffsetPath; pub use self::outline::OutlineStyle; diff --git a/components/style/values/specified/text.rs b/components/style/values/specified/text.rs index 883084e649a..33bda52aa9d 100644 --- a/components/style/values/specified/text.rs +++ b/components/style/values/specified/text.rs @@ -22,8 +22,7 @@ use cssparser::{Parser, Token}; use selectors::parser::SelectorParseErrorKind; use std::fmt::{self, Write}; use style_traits::values::SequenceWriter; -use style_traits::{CssWriter, KeywordsCollectFn, ParseError}; -use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss}; +use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss}; use unicode_segmentation::UnicodeSegmentation; /// A specified type for the `initial-letter` property. @@ -255,119 +254,121 @@ impl ToComputedValue for TextOverflow { } } -macro_rules! impl_text_decoration_line { - { - $( - $(#[$($meta:tt)+])* - $ident:ident / $css:expr => $value:expr, - )+ - } => { - bitflags! { - #[derive(MallocSizeOf, ToComputedValue)] - /// Specified keyword values for the text-decoration-line property. - pub struct TextDecorationLine: u8 { - /// No text decoration line is specified - const NONE = 0; - $( - $(#[$($meta)+])* - const $ident = $value; - )+ - #[cfg(feature = "gecko")] - /// Only set by presentation attributes - /// - /// Setting this will mean that text-decorations use the color - /// specified by `color` in quirks mode. - /// - /// For example, this gives <a href=foo><font color="red">text</font></a> - /// a red text decoration - const COLOR_OVERRIDE = 0x10; - } - } +bitflags! { + #[derive(MallocSizeOf, SpecifiedValueInfo, ToComputedValue)] + #[value_info(other_values = "none,underline,overline,line-through,blink")] + #[repr(C)] + /// Specified keyword values for the text-decoration-line property. + pub struct TextDecorationLine: u8 { + /// No text decoration line is specified. + const NONE = 0; + /// underline + const UNDERLINE = 1 << 0; + /// overline + const OVERLINE = 1 << 1; + /// line-through + const LINE_THROUGH = 1 << 2; + /// blink + const BLINK = 1 << 3; + /// Only set by presentation attributes + /// + /// Setting this will mean that text-decorations use the color + /// specified by `color` in quirks mode. + /// + /// For example, this gives <a href=foo><font color="red">text</font></a> + /// a red text decoration + #[cfg(feature = "gecko")] + const COLOR_OVERRIDE = 0x10; + } +} - impl Parse for TextDecorationLine { - /// none | [ underline || overline || line-through || blink ] - fn parse<'i, 't>( - _context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result<TextDecorationLine, ParseError<'i>> { - let mut result = TextDecorationLine::NONE; - if input - .try(|input| input.expect_ident_matching("none")) - .is_ok() - { - return Ok(result); - } +impl Parse for TextDecorationLine { + /// none | [ underline || overline || line-through || blink ] + fn parse<'i, 't>( + _context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result<Self, ParseError<'i>> { + let mut result = TextDecorationLine::empty(); + + // NOTE(emilio): this loop has this weird structure because we run this + // code to parse the text-decoration shorthand as well, so we need to + // ensure we don't return an error if we don't consume the whole thing + // because we find an invalid identifier or other kind of token. + loop { + let flag: Result<_, ParseError<'i>> = input.try(|input| { + let flag = try_match_ident_ignore_ascii_case! { input, + "none" if result.is_empty() => TextDecorationLine::NONE, + "underline" => TextDecorationLine::UNDERLINE, + "overline" => TextDecorationLine::OVERLINE, + "line-through" => TextDecorationLine::LINE_THROUGH, + "blink" => TextDecorationLine::BLINK, + }; - loop { - let result = input.try(|input| { - let ident = input.expect_ident().map_err(|_| ())?; - match_ignore_ascii_case! { ident, - $( - $css => { - if result.contains(TextDecorationLine::$ident) { - Err(()) - } else { - result.insert(TextDecorationLine::$ident); - Ok(()) - } - } - )+ - _ => Err(()), - } - }); - if result.is_err() { - break; - } - } + Ok(flag) + }); - if !result.is_empty() { - Ok(result) - } else { - Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) - } - } - } + let flag = match flag { + Ok(flag) => flag, + Err(..) => break, + }; - impl ToCss for TextDecorationLine { - fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result - where - W: Write, - { - if self.is_empty() { - return dest.write_str("none"); - } + if flag.is_empty() { + return Ok(TextDecorationLine::NONE); + } - let mut writer = SequenceWriter::new(dest, " "); - $( - if self.contains(TextDecorationLine::$ident) { - writer.raw_item($css)?; - } - )+ - Ok(()) + if result.contains(flag) { + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); } + + result.insert(flag) } - impl SpecifiedValueInfo for TextDecorationLine { - fn collect_completion_keywords(f: KeywordsCollectFn) { - f(&["none", $($css,)+]); - } + if !result.is_empty() { + Ok(result) + } else { + Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) } } } -impl_text_decoration_line! { - /// Underline - UNDERLINE / "underline" => 1 << 0, - /// Overline - OVERLINE / "overline" => 1 << 1, - /// Line through - LINE_THROUGH / "line-through" => 1 << 2, - /// Blink - BLINK / "blink" => 1 << 3, -} +impl ToCss for TextDecorationLine { + fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result + where + W: Write, + { + if self.is_empty() { + return dest.write_str("none"); + } -#[cfg(feature = "gecko")] -impl_bitflags_conversions!(TextDecorationLine); + #[cfg(feature = "gecko")] + { + if *self == TextDecorationLine::COLOR_OVERRIDE { + return Ok(()); + } + } + + let mut writer = SequenceWriter::new(dest, " "); + let mut any = false; + + macro_rules! maybe_write { + ($ident:ident => $str:expr) => { + if self.contains(TextDecorationLine::$ident) { + any = true; + writer.raw_item($str)?; + } + }; + } + + maybe_write!(UNDERLINE => "underline"); + maybe_write!(OVERLINE => "overline"); + maybe_write!(LINE_THROUGH => "line-through"); + maybe_write!(BLINK => "blink"); + + debug_assert!(any); + + Ok(()) + } +} impl TextDecorationLine { #[inline] diff --git a/components/style_traits/specified_value_info.rs b/components/style_traits/specified_value_info.rs index 3cb59e796c4..c978727b47a 100644 --- a/components/style_traits/specified_value_info.rs +++ b/components/style_traits/specified_value_info.rs @@ -10,7 +10,7 @@ use std::sync::Arc as StdArc; /// Type of value that a property supports. This is used by Gecko's /// devtools to make sense about value it parses, and types listed -/// here should match TYPE_* constants in InspectorUtils.webidl. +/// here should match InspectorPropertyType in InspectorUtils.webidl. /// /// XXX This should really be a bitflags rather than a namespace mod, /// but currently we cannot use bitflags in const. |