diff --git a/components/atoms/static_atoms.txt b/components/atoms/static_atoms.txt index bdb16e30e78..8409639055f 100644 --- a/components/atoms/static_atoms.txt +++ b/components/atoms/static_atoms.txt @@ -127,6 +127,7 @@ stroke-opacity storage submit suspend +system-ui tel text time diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index 31c74f1e7a4..ac00083d44e 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -1387,6 +1387,8 @@ fn to_font_kit_family(font_family: &font::SingleFontFamily) -> FamilyName { font::GenericFontFamily::Monospace => FamilyName::Monospace, font::GenericFontFamily::Fantasy => FamilyName::Fantasy, font::GenericFontFamily::Cursive => FamilyName::Cursive, + // TODO: There is no FontFamily::SystemUi. + font::GenericFontFamily::SystemUi => unreachable!("system-ui should be disabled"), font::GenericFontFamily::None => unreachable!("Shouldn't appear in computed values"), }, } diff --git a/components/gfx/font.rs b/components/gfx/font.rs index d53f6761b66..05ffb897a4e 100644 --- a/components/gfx/font.rs +++ b/components/gfx/font.rs @@ -569,6 +569,7 @@ impl<'a> From<&'a SingleFontFamily> for FontFamilyName { GenericFontFamily::Monospace => atom!("monospace"), GenericFontFamily::Cursive => atom!("cursive"), GenericFontFamily::Fantasy => atom!("fantasy"), + GenericFontFamily::SystemUi => atom!("system-ui"), }), } } diff --git a/components/gfx/tests/font_context.rs b/components/gfx/tests/font_context.rs index 4752a5fb3b3..0cda196f12d 100644 --- a/components/gfx/tests/font_context.rs +++ b/components/gfx/tests/font_context.rs @@ -120,7 +120,9 @@ fn font_family(names: Vec<&str>) -> FontFamily { .collect(); FontFamily { - families: FontFamilyList::new(names.into_boxed_slice()), + families: FontFamilyList { + list: names.into_boxed_slice(), + }, is_system_font: false, } } diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index d8b80df754a..1ceec50281d 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -733,9 +733,9 @@ impl<'dom> LayoutElementHelpers<'dom> for LayoutDom<'dom, Element> { hints.push(from_declaration( shared_lock, PropertyDeclaration::FontFamily(font_family::SpecifiedValue::Values( - computed::font::FontFamilyList::new(Box::new([ - computed::font::SingleFontFamily::from_atom(font_family), - ])), + computed::font::FontFamilyList { + list: Box::new([computed::font::SingleFontFamily::from_atom(font_family)]), + }, )), )); } diff --git a/components/script/dom/htmlmetaelement.rs b/components/script/dom/htmlmetaelement.rs index f91e4339f1d..168c29cdb0b 100644 --- a/components/script/dom/htmlmetaelement.rs +++ b/components/script/dom/htmlmetaelement.rs @@ -21,7 +21,6 @@ use crate::dom::node::{ use crate::dom::virtualmethods::VirtualMethods; use dom_struct::dom_struct; use html5ever::{LocalName, Prefix}; -use parking_lot::RwLock; use servo_arc::Arc; use servo_config::pref; use std::sync::atomic::AtomicBool; @@ -112,15 +111,12 @@ impl HTMLMetaElement { let shared_lock = document.style_shared_lock(); let rule = CssRule::Viewport(Arc::new(shared_lock.wrap(translated_rule))); let sheet = Arc::new(Stylesheet { - contents: StylesheetContents { - rules: CssRules::new(vec![rule], shared_lock), - origin: Origin::Author, - namespaces: Default::default(), - quirks_mode: document.quirks_mode(), - url_data: RwLock::new(window_from_node(self).get_url()), - source_map_url: RwLock::new(None), - source_url: RwLock::new(None), - }, + contents: StylesheetContents::from_shared_data( + CssRules::new(vec![rule], shared_lock), + Origin::Author, + window_from_node(self).get_url(), + document.quirks_mode(), + ), media: Arc::new(shared_lock.wrap(MediaList::empty())), shared_lock: shared_lock.clone(), disabled: AtomicBool::new(false), diff --git a/components/script/stylesheet_loader.rs b/components/script/stylesheet_loader.rs index 20fa6872e49..48e3016458e 100644 --- a/components/script/stylesheet_loader.rs +++ b/components/script/stylesheet_loader.rs @@ -29,7 +29,6 @@ use net_traits::{ FetchMetadata, FetchResponseListener, FilteredMetadata, Metadata, NetworkError, ReferrerPolicy, }; use net_traits::{ResourceFetchTiming, ResourceTimingType}; -use parking_lot::RwLock; use servo_arc::Arc; use servo_url::ImmutableOrigin; use servo_url::ServoUrl; @@ -41,9 +40,7 @@ use style::parser::ParserContext; use style::shared_lock::{Locked, SharedRwLock}; use style::stylesheets::import_rule::ImportSheet; use style::stylesheets::StylesheetLoader as StyleStylesheetLoader; -use style::stylesheets::{ - CssRules, ImportRule, Namespaces, Origin, Stylesheet, StylesheetContents, -}; +use style::stylesheets::{CssRules, ImportRule, Origin, Stylesheet, StylesheetContents}; use style::values::CssUrl; pub trait StylesheetOwner { @@ -363,15 +360,12 @@ impl<'a> StyleStylesheetLoader for StylesheetLoader<'a> { media: Arc>, ) -> Arc> { let sheet = Arc::new(Stylesheet { - contents: StylesheetContents { - rules: CssRules::new(Vec::new(), lock), - origin: context.stylesheet_origin, - url_data: RwLock::new(context.url_data.clone()), - quirks_mode: context.quirks_mode, - namespaces: RwLock::new(Namespaces::default()), - source_map_url: RwLock::new(None), - source_url: RwLock::new(None), - }, + contents: StylesheetContents::from_shared_data( + CssRules::new(Vec::new(), lock), + context.stylesheet_origin, + context.url_data.clone(), + context.quirks_mode, + ), media: media, shared_lock: lock.clone(), disabled: AtomicBool::new(false), diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index 0b8d7d2331e..c6650102616 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -256,11 +256,6 @@ pub trait Parser<'i> { false } - /// The error recovery that selector lists inside :is() and :where() have. - fn is_and_where_error_recovery(&self) -> ParseErrorRecovery { - ParseErrorRecovery::IgnoreInvalidSelector - } - /// Whether the given function name is an alias for the `:is()` function. fn is_is_alias(&self, _name: &str) -> bool { false @@ -344,7 +339,7 @@ pub struct SelectorList( ); /// How to treat invalid selectors in a selector list. -pub enum ParseErrorRecovery { +enum ParseErrorRecovery { /// Discard the entire selector list, this is the default behavior for /// almost all of CSS. DiscardList, @@ -2277,7 +2272,7 @@ where state | SelectorParsingState::SKIP_DEFAULT_NAMESPACE | SelectorParsingState::DISALLOW_PSEUDOS, - parser.is_and_where_error_recovery(), + ParseErrorRecovery::IgnoreInvalidSelector, )?; Ok(component(inner.0.into_vec().into_boxed_slice())) } @@ -2686,10 +2681,6 @@ pub mod tests { true } - fn is_and_where_error_recovery(&self) -> ParseErrorRecovery { - ParseErrorRecovery::DiscardList - } - fn parse_part(&self) -> bool { true } @@ -3294,9 +3285,9 @@ pub mod tests { assert!(parse("::slotted(div)::before").is_ok()); assert!(parse("slot::slotted(div,foo)").is_err()); - assert!(parse("foo:where()").is_err()); + assert!(parse("foo:where()").is_ok()); assert!(parse("foo:where(div, foo, .bar baz)").is_ok()); - assert!(parse("foo:where(::before)").is_err()); + assert!(parse_expected("foo:where(::before)", Some("foo:where()")).is_ok()); } #[test] diff --git a/components/servo_arc/lib.rs b/components/servo_arc/lib.rs index cff93a5d524..fccbde99c6e 100644 --- a/components/servo_arc/lib.rs +++ b/components/servo_arc/lib.rs @@ -248,6 +248,14 @@ impl Arc { } } + /// Like from_raw, but returns an addrefed arc instead. + #[inline] + pub unsafe fn from_raw_addrefed(ptr: *const T) -> Self { + let arc = Self::from_raw(ptr); + mem::forget(arc.clone()); + arc + } + /// Create a new static Arc (one that won't reference count the object) /// and place it in the allocation provided by the specified `alloc` /// function. diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml index 70219416452..b4eb6dc55c5 100644 --- a/components/style/Cargo.toml +++ b/components/style/Cargo.toml @@ -27,7 +27,6 @@ servo-layout-2013 = [] servo-layout-2020 = [] gecko_debug = [] gecko_refcount_logging = [] -gecko_profiler = [] [dependencies] app_units = "0.7" diff --git a/components/style/build_gecko.rs b/components/style/build_gecko.rs index 094b44a7087..ddf4a8b71ef 100644 --- a/components/style/build_gecko.rs +++ b/components/style/build_gecko.rs @@ -46,11 +46,15 @@ lazy_static! { .join("layout/style/ServoBindings.toml"); read_config(&path) }; - static ref BUILD_CONFIG: Table = { + static ref BINDGEN_FLAGS: Vec = { // Load build-specific config overrides. let path = PathBuf::from(env::var_os("MOZ_TOPOBJDIR").unwrap()) - .join("layout/style/bindgen.toml"); - read_config(&path) + .join("layout/style/extra-bindgen-flags"); + println!("cargo:rerun-if-changed={}", path.to_str().unwrap()); + fs::read_to_string(path).expect("Failed to read extra-bindgen-flags file") + .split_whitespace() + .map(std::borrow::ToOwned::to_owned) + .collect() }; static ref INCLUDE_RE: Regex = Regex::new(r#"#include\s*"(.+?)""#).unwrap(); static ref DISTDIR_PATH: PathBuf = { @@ -159,12 +163,8 @@ impl BuilderExt for Builder { builder = builder.clang_arg("-DDEBUG=1").clang_arg("-DJS_DEBUG=1"); } - let build_config = BUILD_CONFIG["build"] - .as_table() - .expect("Malformed config file"); - let extra_bindgen_flags = build_config["args"].as_array().unwrap().as_slice(); - for item in extra_bindgen_flags.iter() { - builder = builder.clang_arg(item.as_str().expect("Expect string in list")); + for item in &*BINDGEN_FLAGS { + builder = builder.clang_arg(item); } builder diff --git a/components/style/counter_style/mod.rs b/components/style/counter_style/mod.rs index 1688189d3e0..e470e53ae1f 100644 --- a/components/style/counter_style/mod.rs +++ b/components/style/counter_style/mod.rs @@ -55,7 +55,12 @@ pub fn parse_counter_style_name<'i, 't>( } fn is_valid_name_definition(ident: &CustomIdent) -> bool { - ident.0 != atom!("decimal") && ident.0 != atom!("disc") + ident.0 != atom!("decimal") + && ident.0 != atom!("disc") + && ident.0 != atom!("circle") + && ident.0 != atom!("square") + && ident.0 != atom!("disclosure-closed") + && ident.0 != atom!("disclosure-open") } /// Parse the prelude of an @counter-style rule diff --git a/components/style/custom_properties.rs b/components/style/custom_properties.rs index b5461e1cbae..df3f9660dd4 100644 --- a/components/style/custom_properties.rs +++ b/components/style/custom_properties.rs @@ -88,7 +88,7 @@ pub type Name = Atom; /// /// pub fn parse_name(s: &str) -> Result<&str, ()> { - if s.starts_with("--") { + if s.starts_with("--") && s.len() > 2 { Ok(&s[2..]) } else { Err(()) @@ -318,11 +318,6 @@ fn parse_declaration_value<'i, 't>( missing_closing_characters: &mut String, ) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> { input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| { - // Need at least one token - let start = input.state(); - input.next_including_whitespace()?; - input.reset(&start); - parse_declaration_value_block(input, references, missing_closing_characters) }) } @@ -334,6 +329,7 @@ fn parse_declaration_value_block<'i, 't>( mut references: Option<&mut VarOrEnvReferences>, missing_closing_characters: &mut String, ) -> Result<(TokenSerializationType, TokenSerializationType), ParseError<'i>> { + input.skip_whitespace(); let mut token_start = input.position(); let mut token = match input.next_including_whitespace_and_comments() { Ok(token) => token, @@ -477,10 +473,8 @@ fn parse_fallback<'i, 't>(input: &mut Parser<'i, 't>) -> Result<(), ParseError<' // Exclude `!` and `;` at the top level // https://drafts.csswg.org/css-syntax/#typedef-declaration-value input.parse_until_before(Delimiter::Bang | Delimiter::Semicolon, |input| { - // At least one non-comment token. - input.next_including_whitespace()?; // Skip until the end. - while let Ok(_) = input.next_including_whitespace_and_comments() {} + while input.next_including_whitespace_and_comments().is_ok() {} Ok(()) }) } @@ -996,12 +990,12 @@ fn substitute_block<'i>( while input.next().is_ok() {} } else { input.expect_comma()?; + input.skip_whitespace(); let after_comma = input.state(); let first_token_type = input .next_including_whitespace_and_comments() - // parse_var_function() ensures that .unwrap() will not fail. - .unwrap() - .serialization_type(); + .ok() + .map_or_else(TokenSerializationType::nothing, |t| t.serialization_type()); input.reset(&after_comma); let mut position = (after_comma.position(), first_token_type); last_token_type = substitute_block( diff --git a/components/style/data.rs b/components/style/data.rs index 76d64956f19..f486b24dc58 100644 --- a/components/style/data.rs +++ b/components/style/data.rs @@ -175,6 +175,25 @@ impl ElementStyles { self.primary().get_box().clone_display().is_none() } + /// Whether this element uses viewport units. + pub fn uses_viewport_units(&self) -> bool { + use crate::computed_value_flags::ComputedValueFlags; + + if self.primary().flags.intersects(ComputedValueFlags::USES_VIEWPORT_UNITS) { + return true; + } + + for pseudo_style in self.pseudos.as_array() { + if let Some(ref pseudo_style) = pseudo_style { + if pseudo_style.flags.intersects(ComputedValueFlags::USES_VIEWPORT_UNITS) { + return true; + } + } + } + + false + } + #[cfg(feature = "gecko")] fn size_of_excluding_cvs(&self, _ops: &mut MallocSizeOfOps) -> usize { // As the method name suggests, we don't measures the ComputedValues diff --git a/components/style/dom.rs b/components/style/dom.rs index 3d088956289..c85ff1edaff 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -151,7 +151,7 @@ pub trait TNode: Sized + Copy + Clone + Debug + NodeInfo + PartialEq { /// Get this node's first child. fn first_child(&self) -> Option; - /// Get this node's first child. + /// Get this node's last child. fn last_child(&self) -> Option; /// Get this node's previous sibling. @@ -881,7 +881,7 @@ pub trait TElement: } } // TODO: Could be more granular. - if !shadow.host().exports_any_part() { + if !inner_shadow_host.exports_any_part() { break; } inner_shadow = shadow; diff --git a/components/style/driver.rs b/components/style/driver.rs index 475d04269c5..82ed2a66386 100644 --- a/components/style/driver.rs +++ b/components/style/driver.rs @@ -140,7 +140,8 @@ where // ensures that we process all the elements at a given depth before // proceeding to the next depth, which is important for style sharing. rayon::scope_fifo(|scope| { - profiler_label!(Style); + #[cfg(feature = "gecko")] + gecko_profiler_label!(Layout, StyleComputation); parallel::traverse_nodes( drain, DispatchMode::TailCall, diff --git a/components/style/gecko/data.rs b/components/style/gecko/data.rs index 0689aa6c0c4..39f7d7cc8d8 100644 --- a/components/style/gecko/data.rs +++ b/components/style/gecko/data.rs @@ -56,10 +56,17 @@ impl GeckoStyleSheet { /// already holds a strong reference. #[inline] pub unsafe fn from_addrefed(s: *const DomStyleSheet) -> Self { - debug_assert!(!s.is_null()); + assert!(!s.is_null()); GeckoStyleSheet(s) } + /// HACK(emilio): This is so that we can avoid crashing release due to + /// bug 1719963 and can hopefully get a useful report from fuzzers. + #[inline] + pub fn hack_is_null(&self) -> bool { + self.0.is_null() + } + /// Get the raw `StyleSheet` that we're wrapping. pub fn raw(&self) -> &DomStyleSheet { unsafe { &*self.0 } diff --git a/components/style/gecko/media_features.rs b/components/style/gecko/media_features.rs index 4c23620dd15..664a12f4108 100644 --- a/components/style/gecko/media_features.rs +++ b/components/style/gecko/media_features.rs @@ -590,6 +590,23 @@ fn eval_moz_os_version( query_value.as_ptr() == os_version } +fn eval_moz_windows_non_native_menus( + device: &Device, + query_value: Option, + _: Option, +) -> bool { + let use_non_native_menus = match static_prefs::pref!("browser.display.windows.non_native_menus") { + 0 => false, + 1 => true, + _ => { + eval_moz_os_version(device, Some(atom!("windows-win10")), None) && + get_lnf_int_as_bool(bindings::LookAndFeel_IntID::WindowsDefaultTheme as i32) + }, + }; + + query_value.map_or(use_non_native_menus, |v| v == use_non_native_menus) +} + fn get_lnf_int(int_id: i32) -> i32 { unsafe { bindings::Gecko_GetLookAndFeelInt(int_id) } } @@ -663,7 +680,7 @@ macro_rules! bool_pref_feature { /// to support new types in these entries and (2) ensuring that either /// nsPresContext::MediaFeatureValuesChanged is called when the value that /// would be returned by the evaluator function could change. -pub static MEDIA_FEATURES: [MediaFeatureDescription; 62] = [ +pub static MEDIA_FEATURES: [MediaFeatureDescription; 60] = [ feature!( atom!("width"), AllowsRanges::Yes, @@ -879,6 +896,12 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 62] = [ keyword_evaluator!(eval_toolbar_prefers_color_scheme, ToolbarPrefersColorScheme), ParsingRequirements::CHROME_AND_UA_ONLY, ), + feature!( + atom!("-moz-windows-non-native-menus"), + AllowsRanges::No, + Evaluator::BoolInteger(eval_moz_windows_non_native_menus), + ParsingRequirements::CHROME_AND_UA_ONLY, + ), lnf_int_feature!(atom!("-moz-scrollbar-start-backward"), ScrollArrowStyle, get_scrollbar_start_backward), lnf_int_feature!(atom!("-moz-scrollbar-start-forward"), ScrollArrowStyle, get_scrollbar_start_forward), @@ -903,10 +926,6 @@ pub static MEDIA_FEATURES: [MediaFeatureDescription; 62] = [ lnf_int_feature!(atom!("-moz-gtk-csd-close-button"), GTKCSDCloseButton), lnf_int_feature!(atom!("-moz-gtk-csd-reversed-placement"), GTKCSDReversedPlacement), lnf_int_feature!(atom!("-moz-system-dark-theme"), SystemUsesDarkTheme), - bool_pref_feature!(atom!("-moz-proton"), "browser.proton.enabled"), - bool_pref_feature!(atom!("-moz-proton-modals"), "browser.proton.modals.enabled"), - bool_pref_feature!(atom!("-moz-proton-contextmenus"), "browser.proton.contextmenus.enabled"), - bool_pref_feature!(atom!("-moz-proton-doorhangers"), "browser.proton.doorhangers.enabled"), bool_pref_feature!(atom!("-moz-proton-places-tooltip"), "browser.proton.places-tooltip.enabled"), ]; diff --git a/components/style/gecko/media_queries.rs b/components/style/gecko/media_queries.rs index 5d960a8e9e1..2f5a9673d4c 100644 --- a/components/style/gecko/media_queries.rs +++ b/components/style/gecko/media_queries.rs @@ -93,7 +93,7 @@ impl Device { document, default_values: ComputedValues::default_values(doc), root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()), - body_text_color: AtomicUsize::new(prefs.mDefaultColor as usize), + body_text_color: AtomicUsize::new(prefs.mColors.mDefault as usize), used_root_font_size: AtomicBool::new(false), used_font_metrics: AtomicBool::new(false), used_viewport_size: AtomicBool::new(false), @@ -387,12 +387,12 @@ impl Device { /// Returns the default background color. pub fn default_background_color(&self) -> RGBA { - convert_nscolor_to_rgba(self.pref_sheet_prefs().mDefaultBackgroundColor) + convert_nscolor_to_rgba(self.pref_sheet_prefs().mColors.mDefaultBackground) } /// Returns the default foreground color. pub fn default_color(&self) -> RGBA { - convert_nscolor_to_rgba(self.pref_sheet_prefs().mDefaultColor) + convert_nscolor_to_rgba(self.pref_sheet_prefs().mColors.mDefault) } /// Returns the current effective text zoom. diff --git a/components/style/gecko/mod.rs b/components/style/gecko/mod.rs index 3ff2cfcf140..7b19810fcf2 100644 --- a/components/style/gecko/mod.rs +++ b/components/style/gecko/mod.rs @@ -13,8 +13,6 @@ pub mod conversions; pub mod data; pub mod media_features; pub mod media_queries; -#[cfg(feature = "gecko_profiler")] -pub mod profiler; pub mod pseudo_element; pub mod restyle_damage; pub mod selector_parser; diff --git a/components/style/gecko/profiler.rs b/components/style/gecko/profiler.rs deleted file mode 100644 index db269b497d1..00000000000 --- a/components/style/gecko/profiler.rs +++ /dev/null @@ -1,75 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -//! Gecko profiler support. -//! -//! Use the `profiler_label!` macro from macros.rs. - -use crate::gecko_bindings::structs; - -/// A label describing a category of work that style threads can perform. -pub enum ProfilerLabel { - /// Style computation. - Style, - /// Style sheet parsing. - Parse, -} - -/// RAII object that constructs and destroys a C++ AutoProfilerLabel object -/// pointed to be the specified reference. -#[cfg(feature = "gecko_profiler")] -pub struct AutoProfilerLabel<'a>(&'a mut structs::AutoProfilerLabel); - -#[cfg(feature = "gecko_profiler")] -impl<'a> AutoProfilerLabel<'a> { - /// Creates a new AutoProfilerLabel with the specified label type. - /// - /// unsafe since the caller must ensure that `label` is allocated on the - /// stack. - #[inline] - pub unsafe fn new( - label: &mut std::mem::MaybeUninit, - label_type: ProfilerLabel, - ) -> AutoProfilerLabel { - let category_pair = match label_type { - ProfilerLabel::Style => structs::JS::ProfilingCategoryPair_LAYOUT_StyleComputation, - ProfilerLabel::Parse => structs::JS::ProfilingCategoryPair_LAYOUT_CSSParsing, - }; - structs::Gecko_Construct_AutoProfilerLabel(label.as_mut_ptr(), category_pair); - AutoProfilerLabel(&mut *label.as_mut_ptr()) - } -} - -#[cfg(feature = "gecko_profiler")] -impl<'a> Drop for AutoProfilerLabel<'a> { - #[inline] - fn drop(&mut self) { - unsafe { - structs::Gecko_Destroy_AutoProfilerLabel(self.0); - } - } -} - -/// Whether the Gecko profiler is currently active. -/// -/// This implementation must be kept in sync with -/// `mozilla::profiler::detail::RacyFeatures::IsActive`. -#[cfg(feature = "gecko_profiler")] -#[inline] -pub fn profiler_is_active() -> bool { - use self::structs::profiler::detail; - use std::mem; - use std::sync::atomic::{AtomicU32, Ordering}; - - let active_and_features: &AtomicU32 = - unsafe { mem::transmute(&detail::RacyFeatures_sActiveAndFeatures) }; - (active_and_features.load(Ordering::Relaxed) & detail::RacyFeatures_Active) != 0 -} - -/// Always false when the Gecko profiler is disabled. -#[cfg(not(feature = "gecko_profiler"))] -#[inline] -pub fn profiler_is_active() -> bool { - false -} diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index b2fe46a9a2c..5379454daa0 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -14,7 +14,7 @@ use crate::string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; use crate::values::{AtomIdent, AtomString}; use cssparser::{BasicParseError, BasicParseErrorKind, Parser}; use cssparser::{CowRcStr, SourceLocation, ToCss, Token}; -use selectors::parser::{ParseErrorRecovery, SelectorParseErrorKind}; +use selectors::parser::SelectorParseErrorKind; use selectors::SelectorList; use std::fmt; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_}; @@ -311,15 +311,6 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { true } - #[inline] - fn is_and_where_error_recovery(&self) -> ParseErrorRecovery { - if static_prefs::pref!("layout.css.is-and-where-better-error-recovery.enabled") { - ParseErrorRecovery::IgnoreInvalidSelector - } else { - ParseErrorRecovery::DiscardList - } - } - #[inline] fn parse_part(&self) -> bool { true diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 3f23981a823..476e710795d 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -409,9 +409,11 @@ impl<'ln> TNode for GeckoNode<'ln> { #[inline] fn prev_sibling(&self) -> Option { unsafe { - bindings::Gecko_GetPreviousSibling(self.0) - .as_ref() - .map(GeckoNode) + let prev_or_last = GeckoNode::from_content(self.0.mPreviousOrLastSibling.as_ref()?); + if prev_or_last.0.mNextSibling.raw::().is_null() { + return None; + } + Some(prev_or_last) } } diff --git a/components/style/gecko_bindings/sugar/refptr.rs b/components/style/gecko_bindings/sugar/refptr.rs index a141aef4e8b..d8c0cea0f2b 100644 --- a/components/style/gecko_bindings/sugar/refptr.rs +++ b/components/style/gecko_bindings/sugar/refptr.rs @@ -308,11 +308,6 @@ impl_threadsafe_refcount!( bindings::Gecko_AddRefnsIURIArbitraryThread, bindings::Gecko_ReleasensIURIArbitraryThread ); -impl_threadsafe_refcount!( - structs::SharedFontList, - bindings::Gecko_AddRefSharedFontListArbitraryThread, - bindings::Gecko_ReleaseSharedFontListArbitraryThread -); impl_threadsafe_refcount!( structs::SheetLoadDataHolder, bindings::Gecko_AddRefSheetLoadDataHolderArbitraryThread, diff --git a/components/style/global_style_data.rs b/components/style/global_style_data.rs index 4e3d11b94b7..6a22c5a11e9 100644 --- a/components/style/global_style_data.rs +++ b/components/style/global_style_data.rs @@ -10,6 +10,8 @@ use crate::gecko_bindings::bindings; use crate::parallel::STYLE_THREAD_STACK_SIZE_KB; use crate::shared_lock::SharedRwLock; use crate::thread_state; +#[cfg(feature = "gecko")] +use gecko_profiler; use parking_lot::{RwLock, RwLockReadGuard}; use rayon; use std::env; @@ -49,20 +51,16 @@ fn thread_startup(_index: usize) { thread_state::initialize_layout_worker_thread(); #[cfg(feature = "gecko")] unsafe { - use std::ffi::CString; - bindings::Gecko_SetJemallocThreadLocalArena(true); let name = thread_name(_index); - let name = CString::new(name).unwrap(); - // Gecko_RegisterProfilerThread copies the passed name here. - bindings::Gecko_RegisterProfilerThread(name.as_ptr()); + gecko_profiler::register_thread(&name); } } fn thread_shutdown(_: usize) { #[cfg(feature = "gecko")] unsafe { - bindings::Gecko_UnregisterProfilerThread(); + gecko_profiler::unregister_thread(); bindings::Gecko_SetJemallocThreadLocalArena(false); } ALIVE_WORKER_THREADS.fetch_sub(1, Ordering::Relaxed); diff --git a/components/style/invalidation/element/restyle_hints.rs b/components/style/invalidation/element/restyle_hints.rs index c0bf2424d52..c423d853905 100644 --- a/components/style/invalidation/element/restyle_hints.rs +++ b/components/style/invalidation/element/restyle_hints.rs @@ -60,7 +60,12 @@ impl RestyleHint { /// Returns whether this hint invalidates the element and all its /// descendants. pub fn contains_subtree(&self) -> bool { - self.contains(RestyleHint::RESTYLE_SELF | RestyleHint::RESTYLE_DESCENDANTS) + self.contains(Self::restyle_subtree()) + } + + /// Returns whether we'll recascade all of the descendants. + pub fn will_recascade_subtree(&self) -> bool { + self.contains_subtree() || self.contains(Self::recascade_subtree()) } /// Returns whether we need to restyle this element. diff --git a/components/style/invalidation/mod.rs b/components/style/invalidation/mod.rs index a59d9474b0a..12b0d06853b 100644 --- a/components/style/invalidation/mod.rs +++ b/components/style/invalidation/mod.rs @@ -7,3 +7,4 @@ pub mod element; pub mod media_queries; pub mod stylesheets; +pub mod viewport_units; diff --git a/components/style/invalidation/viewport_units.rs b/components/style/invalidation/viewport_units.rs new file mode 100644 index 00000000000..acf8b095f9f --- /dev/null +++ b/components/style/invalidation/viewport_units.rs @@ -0,0 +1,57 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +//! Invalidates style of all elements that depend on viewport units. + +use crate::dom::{TElement, TNode}; +use crate::invalidation::element::restyle_hints::RestyleHint; + +/// Invalidates style of all elements that depend on viewport units. +/// +/// Returns whether any element was invalidated. +pub fn invalidate(root: E) -> bool +where + E: TElement, +{ + debug!("invalidation::viewport_units::invalidate({:?})", root); + invalidate_recursively(root) +} + +fn invalidate_recursively(element: E) -> bool +where + E: TElement, +{ + let mut data = match element.mutate_data() { + Some(data) => data, + None => return false, + }; + + if data.hint.will_recascade_subtree() { + debug!("invalidate_recursively: {:?} was already invalid", element); + return false; + } + + let uses_viewport_units = data.styles.uses_viewport_units(); + if uses_viewport_units { + debug!("invalidate_recursively: {:?} uses viewport units", element); + data.hint.insert(RestyleHint::RECASCADE_SELF); + } + + let mut any_children_invalid = false; + for child in element.traversal_children() { + if let Some(child) = child.as_element() { + any_children_invalid |= invalidate_recursively(child); + } + } + + if any_children_invalid { + debug!( + "invalidate_recursively: Children of {:?} changed, setting dirty descendants", + element + ); + unsafe { element.set_dirty_descendants() } + } + + uses_viewport_units || any_children_invalid +} diff --git a/components/style/lib.rs b/components/style/lib.rs index a9853e4c823..b721ea0d952 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -33,6 +33,9 @@ extern crate cssparser; extern crate debug_unreachable; #[macro_use] extern crate derive_more; +#[macro_use] +#[cfg(feature = "gecko")] +extern crate gecko_profiler; #[cfg(feature = "gecko")] #[macro_use] pub mod gecko_string_cache; diff --git a/components/style/macros.rs b/components/style/macros.rs index f5937d8b469..e27a554acf4 100644 --- a/components/style/macros.rs +++ b/components/style/macros.rs @@ -105,35 +105,6 @@ macro_rules! define_keyword_type { }; } -/// Place a Gecko profiler label on the stack. -/// -/// The `label_type` argument must be the name of a variant of `ProfilerLabel`. -#[cfg(feature = "gecko_profiler")] -#[macro_export] -macro_rules! profiler_label { - ($label_type:ident) => { - let mut _profiler_label = - ::std::mem::MaybeUninit::<$crate::gecko_bindings::structs::AutoProfilerLabel>::uninit(); - let _profiler_label = if $crate::gecko::profiler::profiler_is_active() { - unsafe { - Some($crate::gecko::profiler::AutoProfilerLabel::new( - &mut _profiler_label, - $crate::gecko::profiler::ProfilerLabel::$label_type, - )) - } - } else { - None - }; - }; -} - -/// No-op when the Gecko profiler is not available. -#[cfg(not(feature = "gecko_profiler"))] -#[macro_export] -macro_rules! profiler_label { - ($label_type:ident) => {}; -} - #[cfg(feature = "servo")] macro_rules! local_name { ($s:tt) => { diff --git a/components/style/parallel.rs b/components/style/parallel.rs index f03bf64fe61..82f003cff71 100644 --- a/components/style/parallel.rs +++ b/components/style/parallel.rs @@ -274,7 +274,8 @@ pub fn traverse_nodes<'a, 'scope, E, D, I>( top_down_dom(&work, root, traversal_data, scope, pool, traversal, tls); } else { scope.spawn_fifo(move |scope| { - profiler_label!(Style); + #[cfg(feature = "gecko")] + gecko_profiler_label!(Layout, StyleComputation); let work = work; top_down_dom(&work, root, traversal_data, scope, pool, traversal, tls); }); @@ -284,7 +285,8 @@ pub fn traverse_nodes<'a, 'scope, E, D, I>( let nodes: WorkUnit = chunk.collect(); let traversal_data_copy = traversal_data.clone(); scope.spawn_fifo(move |scope| { - profiler_label!(Style); + #[cfg(feature = "gecko")] + gecko_profiler_label!(Layout, StyleComputation); let n = nodes; top_down_dom(&*n, root, traversal_data_copy, scope, pool, traversal, tls) }); diff --git a/components/style/properties/cascade.rs b/components/style/properties/cascade.rs index 9c3e6ebd85e..e3f919cc56e 100644 --- a/components/style/properties/cascade.rs +++ b/components/style/properties/cascade.rs @@ -464,7 +464,22 @@ fn tweak_when_ignoring_colors( } } }, - _ => {}, + _ => { + // We honor transparent and system colors more generally for all + // colors. + // + // NOTE(emilio): This doesn't handle caret-color and + // accent-color because those use a slightly different syntax + // ( | auto for example). That's probably fine though, as + // using a system color for caret-color doesn't make sense (using + // currentColor is fine), and we ignore accent-color in + // high-contrast-mode anyways. + if let Some(color) = declaration.color_value() { + if color.is_system() || alpha_channel(color, context) == 0 { + return; + } + } + }, } *declaration.to_mut() = @@ -786,6 +801,35 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_PADDING); } + if self + .author_specified + .contains(LonghandId::FontFamily) + { + builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY); + } + + if self + .author_specified + .contains(LonghandId::LetterSpacing) + { + builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING); + } + + if self + .author_specified + .contains(LonghandId::WordSpacing) + { + builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING); + } + + #[cfg(feature = "gecko")] + if self + .author_specified + .contains(LonghandId::FontSynthesis) + { + builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS); + } + #[cfg(feature = "servo")] { if let Some(font) = builder.get_font_if_mutated() { @@ -849,18 +893,19 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { // System fonts are all right, and should have the default font type // set to none already, so bail out early. - if font.mFont.systemFont { + if font.mFont.family.is_system_font { debug_assert_eq!( - font.mFont.fontlist.mDefaultFontType, + font.mFont.family.families.fallback, GenericFontFamily::None ); return; } + let generic = font.mFont.family.families.single_generic().unwrap_or(GenericFontFamily::None); let default_font_type = unsafe { bindings::Gecko_nsStyleFont_ComputeDefaultFontType( builder.device.document(), - font.mGenericID, + generic, font.mLanguage.mRawPtr, ) }; @@ -870,15 +915,15 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { // 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 && + default_font_type != GenericFontFamily::None && matches!( - font.mGenericID, + generic, GenericFontFamily::None | - GenericFontFamily::Fantasy | - GenericFontFamily::Cursive - ) && - default_font_type != GenericFontFamily::None; + GenericFontFamily::Fantasy | + GenericFontFamily::Cursive + ); - if !prioritize_user_fonts && default_font_type == font.mFont.fontlist.mDefaultFontType { + if !prioritize_user_fonts && default_font_type == font.mFont.family.families.fallback { // Nothing to do. return; } @@ -886,9 +931,9 @@ impl<'a, 'b: 'a> Cascade<'a, 'b> { }; let font = builder.mutate_font().gecko_mut(); - font.mFont.fontlist.mDefaultFontType = default_font_type; + font.mFont.family.families.fallback = default_font_type; if prioritize_user_fonts { - unsafe { bindings::Gecko_nsStyleFont_PrioritizeUserFonts(font, default_font_type) } + font.mFont.family.families.prioritize_first_generic_or_prepend(default_font_type); } } diff --git a/components/style/properties/computed_value_flags.rs b/components/style/properties/computed_value_flags.rs index cc7d9c94fb9..0942bbd0d13 100644 --- a/components/style/properties/computed_value_flags.rs +++ b/components/style/properties/computed_value_flags.rs @@ -12,7 +12,7 @@ bitflags! { /// If we ever want to add some flags that shouldn't inherit for them, /// we might want to add a function to handle this. #[repr(C)] - pub struct ComputedValueFlags: u16 { + pub struct ComputedValueFlags: u32 { /// Whether the style or any of the ancestors has a text-decoration-line /// property that should get propagated to descendants. /// @@ -89,6 +89,21 @@ bitflags! { /// FIXME(emilio): Try to merge this with BORDER_BACKGROUND, see /// https://github.com/w3c/csswg-drafts/issues/4777 const HAS_AUTHOR_SPECIFIED_PADDING = 1 << 15; + + /// Whether there are author-specified rules for `font-family`. + const HAS_AUTHOR_SPECIFIED_FONT_FAMILY = 1 << 16; + + /// Whether there are author-specified rules for `font-synthesis`. + const HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS = 1 << 17; + + /// Whether there are author-specified rules for `letter-spacing`. + const HAS_AUTHOR_SPECIFIED_LETTER_SPACING = 1 << 18; + + /// Whether there are author-specified rules for `word-spacing`. + const HAS_AUTHOR_SPECIFIED_WORD_SPACING = 1 << 19; + + /// Whether the style depends on viewport units. + const USES_VIEWPORT_UNITS = 1 << 20; } } diff --git a/components/style/properties/counted_unknown_properties.py b/components/style/properties/counted_unknown_properties.py index 577547d099d..6a562a9abab 100644 --- a/components/style/properties/counted_unknown_properties.py +++ b/components/style/properties/counted_unknown_properties.py @@ -25,7 +25,6 @@ COUNTED_UNKNOWN_PROPERTIES = [ "-webkit-user-modify", "-webkit-margin-before", "-webkit-margin-after", - "tab-size", "-webkit-margin-start", "-webkit-column-break-inside", "-webkit-padding-start", @@ -63,7 +62,6 @@ COUNTED_UNKNOWN_PROPERTIES = [ "-webkit-text-combine", "-webkit-text-emphasis-style", "-webkit-text-emphasis", - "d", "-webkit-mask-box-image-width", "-webkit-mask-box-image-source", "-webkit-mask-box-image-outset", @@ -123,4 +121,5 @@ COUNTED_UNKNOWN_PROPERTIES = [ "-webkit-columns", "-webkit-column-rule-color", "-webkit-shape-margin", + "content-visibility", ] diff --git a/components/style/properties/data.py b/components/style/properties/data.py index 8a523ccb66f..b2ab520ad3d 100644 --- a/components/style/properties/data.py +++ b/components/style/properties/data.py @@ -340,13 +340,14 @@ class Longhand(Property): assert ( has_effect_on_gecko_scrollbars in [None, False, True] and not style_struct.inherited - or (gecko_pref is None) == (has_effect_on_gecko_scrollbars is None) + or (gecko_pref is None and enabled_in != "") + == (has_effect_on_gecko_scrollbars is None) ), ( "Property " + name + ": has_effect_on_gecko_scrollbars must be " + "specified, and must have a value of True or False, iff a " - + "property is inherited and is behind a Gecko pref" + + "property is inherited and is behind a Gecko pref or internal" ) self.need_index = need_index self.gecko_ffi_name = gecko_ffi_name or "m" + self.camel_case @@ -759,7 +760,7 @@ def _add_logical_props(data, props): # These are probably Gecko bugs and should be supported per spec. def _remove_common_first_line_and_first_letter_properties(props, engine): if engine == "gecko": - props.remove("-moz-tab-size") + props.remove("tab-size") props.remove("hyphens") props.remove("line-break") props.remove("text-align-last") diff --git a/components/style/properties/declaration_block.rs b/components/style/properties/declaration_block.rs index 3639ae24f6e..980d3de11ff 100644 --- a/components/style/properties/declaration_block.rs +++ b/components/style/properties/declaration_block.rs @@ -385,7 +385,7 @@ impl PropertyDeclarationBlock { // Step 1.2.3 // We don't print !important when serializing individual properties, // so we treat this as a normal-importance property - match shorthand.get_shorthand_appendable_value(list.iter().cloned()) { + match shorthand.get_shorthand_appendable_value(&list) { Some(appendable_value) => append_declaration_value(dest, appendable_value), None => return Ok(()), } @@ -911,9 +911,6 @@ impl PropertyDeclarationBlock { /// /// https://drafts.csswg.org/cssom/#serialize-a-css-declaration-block pub fn to_css(&self, dest: &mut CssStringWriter) -> fmt::Result { - use std::iter::Cloned; - use std::slice; - let mut is_first_serialization = true; // trailing serializations should have a prepended space // Step 1 -> dest = result list @@ -938,7 +935,7 @@ impl PropertyDeclarationBlock { // properties in a declaration block, and that custom // properties can't be part of a shorthand, we can just care // about them here. - append_serialization::>, _>( + append_serialization( dest, &property, AppendableValue::Declaration(declaration), @@ -992,7 +989,7 @@ impl PropertyDeclarationBlock { // Step 3.4.3: // Let current longhands be an empty array. - let mut current_longhands = SmallVec::<[_; 10]>::new(); + let mut current_longhands = SmallVec::<[&_; 10]>::new(); let mut logical_groups = LogicalGroupSet::new(); let mut saw_one = false; let mut logical_mismatch = false; @@ -1066,7 +1063,7 @@ impl PropertyDeclarationBlock { // Let value be the result of invoking serialize a CSS value // of current longhands. let appendable_value = match shorthand - .get_shorthand_appendable_value(current_longhands.iter().cloned()) + .get_shorthand_appendable_value(¤t_longhands) { None => continue, Some(appendable_value) => appendable_value, @@ -1076,15 +1073,9 @@ impl PropertyDeclarationBlock { // AppendableValue::Css. let mut v = CssString::new(); let value = match appendable_value { - AppendableValue::Css { - css, - with_variables, - } => { + AppendableValue::Css(css) => { debug_assert!(!css.is_empty()); - AppendableValue::Css { - css, - with_variables, - } + appendable_value }, other => { append_declaration_value(&mut v, other)?; @@ -1096,14 +1087,13 @@ impl PropertyDeclarationBlock { continue; } - AppendableValue::Css { + AppendableValue::Css({ // Safety: serialization only generates valid utf-8. #[cfg(feature = "gecko")] - css: unsafe { v.as_str_unchecked() }, + unsafe { v.as_str_unchecked() } #[cfg(feature = "servo")] - css: &v, - with_variables: false, - } + &v + }) }, }; @@ -1116,7 +1106,7 @@ impl PropertyDeclarationBlock { // // 3.4.10: // Append serialized declaration to list. - append_serialization::>, _>( + append_serialization( dest, &shorthand, value, @@ -1152,7 +1142,7 @@ impl PropertyDeclarationBlock { // its important flag set. // // Append serialized declaration to list. - append_serialization::>, _>( + append_serialization( dest, &property, AppendableValue::Declaration(declaration), @@ -1172,25 +1162,17 @@ impl PropertyDeclarationBlock { /// A convenient enum to represent different kinds of stuff that can represent a /// _value_ in the serialization of a property declaration. -pub enum AppendableValue<'a, I> -where - I: Iterator, -{ +pub enum AppendableValue<'a, 'b: 'a> { /// A given declaration, of which we'll serialize just the value. Declaration(&'a PropertyDeclaration), /// A set of declarations for a given shorthand. /// /// FIXME: This needs more docs, where are the shorthands expanded? We print /// the property name before-hand, don't we? - DeclarationsForShorthand(ShorthandId, I), + DeclarationsForShorthand(ShorthandId, &'a [&'b PropertyDeclaration]), /// A raw CSS string, coming for example from a property with CSS variables, /// or when storing a serialized shorthand value before appending directly. - Css { - /// The raw CSS string. - css: &'a str, - /// Whether the original serialization contained variables or not. - with_variables: bool, - }, + Css(&'a str), } /// Potentially appends whitespace after the first (property: value;) pair. @@ -1207,56 +1189,34 @@ where } /// Append a given kind of appendable value to a serialization. -pub fn append_declaration_value<'a, I>( +pub fn append_declaration_value<'a, 'b: 'a>( dest: &mut CssStringWriter, - appendable_value: AppendableValue<'a, I>, -) -> fmt::Result -where - I: Iterator, -{ + appendable_value: AppendableValue<'a, 'b>, +) -> fmt::Result { match appendable_value { - AppendableValue::Css { css, .. } => dest.write_str(css), + AppendableValue::Css(css) => dest.write_str(css), AppendableValue::Declaration(decl) => decl.to_css(dest), AppendableValue::DeclarationsForShorthand(shorthand, decls) => { - shorthand.longhands_to_css(decls, &mut CssWriter::new(dest)) + shorthand.longhands_to_css(decls, dest) }, } } /// Append a given property and value pair to a serialization. -pub fn append_serialization<'a, I, N>( +pub fn append_serialization<'a, 'b: 'a, N>( dest: &mut CssStringWriter, property_name: &N, - appendable_value: AppendableValue<'a, I>, + appendable_value: AppendableValue<'a, 'b>, importance: Importance, is_first_serialization: &mut bool, ) -> fmt::Result where - I: Iterator, N: ToCss, { handle_first_serialization(dest, is_first_serialization)?; property_name.to_css(&mut CssWriter::new(dest))?; - dest.write_char(':')?; - - // for normal parsed values, add a space between key: and value - match appendable_value { - AppendableValue::Declaration(decl) => { - if !decl.value_is_unparsed() { - // For normal parsed values, add a space between key: and value. - dest.write_str(" ")? - } - }, - AppendableValue::Css { with_variables, .. } => { - if !with_variables { - dest.write_str(" ")? - } - }, - // Currently append_serialization is only called with a Css or - // a Declaration AppendableValue. - AppendableValue::DeclarationsForShorthand(..) => unreachable!(), - } + dest.write_str(": ")?; append_declaration_value(dest, appendable_value)?; diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index 7a864c06d27..86cdeefa982 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -22,7 +22,6 @@ use crate::gecko_bindings::bindings::Gecko_CopyConstruct_${style_struct.gecko_ff use crate::gecko_bindings::bindings::Gecko_Destroy_${style_struct.gecko_ffi_name}; % endfor use crate::gecko_bindings::bindings::Gecko_CopyCounterStyle; -use crate::gecko_bindings::bindings::Gecko_CopyFontFamilyFrom; use crate::gecko_bindings::bindings::Gecko_EnsureImageLayersLength; use crate::gecko_bindings::bindings::Gecko_nsStyleFont_SetLang; use crate::gecko_bindings::bindings::Gecko_nsStyleFont_CopyLangFrom; @@ -799,11 +798,8 @@ fn static_assert() { ${impl_simple_type_with_conversion("masonry_auto_flow", "mMasonryAutoFlow")} -<% skip_outline_longhands = " ".join("outline-style outline-width".split() + - ["-moz-outline-radius-{0}".format(x.replace("_", "")) - for x in CORNERS]) %> <%self:impl_trait style_struct_name="Outline" - skip_longhands="${skip_outline_longhands}"> + skip_longhands="outline-style outline-width"> pub fn set_outline_style(&mut self, v: longhands::outline_style::computed_value::T) { self.gecko.mOutlineStyle = v; @@ -831,12 +827,6 @@ fn static_assert() { inherit_from="mOutlineWidth", round_to_pixels=True) %> - % for corner in CORNERS: - <% impl_corner_style_coord("_moz_outline_radius_%s" % corner.replace("_", ""), - "mOutlineRadius", - corner) %> - % endfor - pub fn outline_has_nonzero_width(&self) -> bool { self.gecko.mActualOutlineWidth != 0 } @@ -857,52 +847,6 @@ 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 set_font_family(&mut self, v: longhands::font_family::computed_value::T) { - use crate::values::computed::font::GenericFontFamily; - - let is_system_font = v.is_system_font; - self.gecko.mFont.systemFont = is_system_font; - self.gecko.mGenericID = if is_system_font { - GenericFontFamily::None - } else { - v.families.single_generic().unwrap_or(GenericFontFamily::None) - }; - self.gecko.mFont.fontlist.mFontlist.mBasePtr.set_move( - v.families.shared_font_list().clone() - ); - // Fixed-up if needed in Cascade::fixup_font_stuff. - self.gecko.mFont.fontlist.mDefaultFontType = GenericFontFamily::None; - } - - pub fn copy_font_family_from(&mut self, other: &Self) { - unsafe { Gecko_CopyFontFamilyFrom(&mut self.gecko.mFont, &other.gecko.mFont); } - self.gecko.mGenericID = other.gecko.mGenericID; - self.gecko.mFont.systemFont = other.gecko.mFont.systemFont; - } - - pub fn reset_font_family(&mut self, other: &Self) { - self.copy_font_family_from(other) - } - - pub fn clone_font_family(&self) -> longhands::font_family::computed_value::T { - use crate::values::computed::font::{FontFamily, SingleFontFamily, FontFamilyList}; - - let fontlist = &self.gecko.mFont.fontlist; - let shared_fontlist = unsafe { fontlist.mFontlist.mBasePtr.to_safe() }; - - let families = if shared_fontlist.mNames.is_empty() { - let default = SingleFontFamily::Generic(fontlist.mDefaultFontType); - FontFamilyList::new(Box::new([default])) - } else { - FontFamilyList::SharedFontList(shared_fontlist) - }; - - FontFamily { - families, - is_system_font: self.gecko.mFont.systemFont, - } - } - pub fn unzoom_fonts(&mut self, device: &Device) { use crate::values::generics::NonNegative; self.gecko.mSize = NonNegative(device.unzoom_text(self.gecko.mSize.0)); @@ -1008,26 +952,9 @@ fn static_assert() { ${impl_simple("font_variant_alternates", "mFont.variantAlternates")} - pub fn set_font_size_adjust(&mut self, v: longhands::font_size_adjust::computed_value::T) { - use crate::properties::longhands::font_size_adjust::computed_value::T; - match v { - T::None => self.gecko.mFont.sizeAdjust = -1.0 as f32, - T::Number(n) => self.gecko.mFont.sizeAdjust = n, - } - } + ${impl_simple("font_size_adjust", "mFont.sizeAdjust")} - pub fn copy_font_size_adjust_from(&mut self, other: &Self) { - self.gecko.mFont.sizeAdjust = other.gecko.mFont.sizeAdjust; - } - - pub fn reset_font_size_adjust(&mut self, other: &Self) { - self.copy_font_size_adjust_from(other) - } - - pub fn clone_font_size_adjust(&self) -> longhands::font_size_adjust::computed_value::T { - use crate::properties::longhands::font_size_adjust::computed_value::T; - T::from_gecko_adjust(self.gecko.mFont.sizeAdjust) - } + ${impl_simple("font_family", "mFont.family")} #[allow(non_snake_case)] pub fn set__x_lang(&mut self, v: longhands::_x_lang::computed_value::T) { diff --git a/components/style/properties/helpers.mako.rs b/components/style/properties/helpers.mako.rs index ecd714cebfc..e46024feba1 100644 --- a/components/style/properties/helpers.mako.rs +++ b/components/style/properties/helpers.mako.rs @@ -845,10 +845,7 @@ impl<'a> LonghandsToSerialize<'a> { /// Tries to get a serializable set of longhands given a set of /// property declarations. - pub fn from_iter(iter: I) -> Result - where - I: Iterator, - { + pub fn from_iter(iter: impl Iterator) -> Result { // Define all of the expected variables that correspond to the shorthand % for sub_property in shorthand.sub_properties: let mut ${sub_property.ident} = @@ -856,8 +853,8 @@ % endfor // Attempt to assign the incoming declarations to the expected variables - for longhand in iter { - match *longhand { + for declaration in iter { + match *declaration { % for sub_property in shorthand.sub_properties: PropertyDeclaration::${sub_property.camel_case}(ref value) => { ${sub_property.ident} = Some(value) @@ -918,6 +915,14 @@ }) } + /// Try to serialize a given shorthand to a string. + pub fn to_css(declarations: &[&PropertyDeclaration], dest: &mut crate::str::CssStringWriter) -> fmt::Result { + match LonghandsToSerialize::from_iter(declarations.iter().cloned()) { + Ok(longhands) => longhands.to_css(&mut CssWriter::new(dest)), + Err(_) => Ok(()) + } + } + ${caller.body()} } % endif diff --git a/components/style/properties/longhands/box.mako.rs b/components/style/properties/longhands/box.mako.rs index f2d0234a918..c8942fdc6ff 100644 --- a/components/style/properties/longhands/box.mako.rs +++ b/components/style/properties/longhands/box.mako.rs @@ -503,7 +503,6 @@ ${helpers.predefined_type( "BreakWithin", "computed::BreakWithin::Auto", engines="gecko", - aliases="page-break-inside", spec="https://drafts.csswg.org/css-break/#propdef-break-inside", animation_value_type="discrete", )} diff --git a/components/style/properties/longhands/column.mako.rs b/components/style/properties/longhands/column.mako.rs index 2d9a116d30b..747a1c2db0a 100644 --- a/components/style/properties/longhands/column.mako.rs +++ b/components/style/properties/longhands/column.mako.rs @@ -13,7 +13,6 @@ ${helpers.predefined_type( engines="gecko servo-2013 servo-2020", servo_2020_pref="layout.2020.unimplemented", initial_specified_value="specified::length::NonNegativeLengthOrAuto::auto()", - extra_prefixes="moz:layout.css.prefixes.columns", animation_value_type="NonNegativeLengthOrAuto", servo_2013_pref="layout.columns.enabled", spec="https://drafts.csswg.org/css-multicol/#propdef-column-width", @@ -29,7 +28,6 @@ ${helpers.predefined_type( initial_specified_value="specified::ColumnCount::auto()", servo_2013_pref="layout.columns.enabled", animation_value_type="AnimatedColumnCount", - extra_prefixes="moz:layout.css.prefixes.columns", spec="https://drafts.csswg.org/css-multicol/#propdef-column-count", servo_restyle_damage="rebuild_and_reflow", )} @@ -38,7 +36,6 @@ ${helpers.single_keyword( "column-fill", "balance auto", engines="gecko", - extra_prefixes="moz:layout.css.prefixes.columns", animation_value_type="discrete", gecko_enum_prefix="StyleColumnFill", spec="https://drafts.csswg.org/css-multicol/#propdef-column-fill", @@ -53,7 +50,6 @@ ${helpers.predefined_type( computed_type="crate::values::computed::NonNegativeLength", spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-width", animation_value_type="NonNegativeLength", - extra_prefixes="moz:layout.css.prefixes.columns", )} // https://drafts.csswg.org/css-multicol-1/#crc @@ -64,7 +60,6 @@ ${helpers.predefined_type( engines="gecko", initial_specified_value="specified::Color::currentcolor()", animation_value_type="AnimatedColor", - extra_prefixes="moz:layout.css.prefixes.columns", ignored_when_colors_disabled=True, spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-color", )} @@ -76,7 +71,6 @@ ${helpers.single_keyword( animation_value_type="discrete", gecko_enum_prefix="StyleColumnSpan", spec="https://drafts.csswg.org/css-multicol/#propdef-column-span", - extra_prefixes="moz:layout.css.prefixes.columns", )} ${helpers.predefined_type( @@ -85,7 +79,6 @@ ${helpers.predefined_type( "computed::BorderStyle::None", engines="gecko", initial_specified_value="specified::BorderStyle::None", - extra_prefixes="moz:layout.css.prefixes.columns", animation_value_type="discrete", spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule-style", )} diff --git a/components/style/properties/longhands/font.mako.rs b/components/style/properties/longhands/font.mako.rs index e84a5574d1a..12191509622 100644 --- a/components/style/properties/longhands/font.mako.rs +++ b/components/style/properties/longhands/font.mako.rs @@ -213,6 +213,7 @@ ${helpers.predefined_type( initial_value="computed::XLang::get_initial_value()", animation_value_type="none", enabled_in="", + has_effect_on_gecko_scrollbars=False, spec="Internal (not web-exposed)", )} @@ -224,6 +225,7 @@ ${helpers.predefined_type( animation_value_type="none", gecko_ffi_name="mScriptSizeMultiplier", enabled_in="", + has_effect_on_gecko_scrollbars=False, spec="Internal (not web-exposed)", )} @@ -263,6 +265,7 @@ ${helpers.single_keyword( spec="Internal (not web-exposed)", animation_value_type="none", enabled_in="", + has_effect_on_gecko_scrollbars=False, needs_conversion=True, )} @@ -273,6 +276,7 @@ ${helpers.predefined_type( engines="gecko", animation_value_type="none", enabled_in="", + has_effect_on_gecko_scrollbars=False, gecko_ffi_name="mScriptMinSize", spec="Internal (not web-exposed)", )} @@ -284,6 +288,7 @@ ${helpers.predefined_type( engines="gecko", animation_value_type="none", enabled_in="", + has_effect_on_gecko_scrollbars=False, spec="Internal (not web-exposed)", )} @@ -309,7 +314,6 @@ pub mod system_font { //! variable reference. We may want to improve this behavior at some //! point. See also https://github.com/w3c/csswg-drafts/issues/1586. - use crate::values::computed::font::GenericFontFamily; use crate::properties::longhands; use std::hash::{Hash, Hasher}; use crate::values::computed::{ToComputedValue, Context}; @@ -351,7 +355,7 @@ pub mod system_font { use std::mem; use crate::values::computed::Percentage; use crate::values::specified::font::KeywordInfo; - use crate::values::computed::font::{FontFamily, FontSize, FontStretch, FontStyle, FontFamilyList}; + use crate::values::computed::font::{FontSize, FontStretch, FontStyle}; use crate::values::generics::NonNegative; let mut system = mem::MaybeUninit::::uninit(); @@ -370,12 +374,7 @@ pub mod system_font { }))); let font_style = FontStyle::from_gecko(system.style); let ret = ComputedSystemFont { - font_family: FontFamily { - families: FontFamilyList::SharedFontList( - unsafe { system.fontlist.mFontlist.mBasePtr.to_safe() } - ), - is_system_font: true, - }, + font_family: system.family.clone(), font_size: FontSize { size: NonNegative(cx.maybe_zoom_text(system.size.0)), keyword_info: KeywordInfo::none() @@ -383,8 +382,7 @@ pub mod system_font { font_weight, font_stretch, font_style, - font_size_adjust: longhands::font_size_adjust::computed_value - ::T::from_gecko_adjust(system.sizeAdjust), + font_size_adjust: system.sizeAdjust, % for kwprop in kw_font_props: ${kwprop}: longhands::${kwprop}::computed_value::T::from_gecko_keyword( system.${to_camel_case_lower(kwprop.replace('font_', ''))} @@ -399,7 +397,6 @@ pub mod system_font { font_variation_settings: longhands::font_variation_settings::get_initial_value(), font_variant_alternates: longhands::font_variant_alternates::get_initial_value(), system_font: *self, - default_font_type: system.fontlist.mDefaultFontType, }; unsafe { bindings::Gecko_nsFont_Destroy(system); } ret @@ -431,7 +428,6 @@ pub mod system_font { pub ${name}: longhands::${name}::computed_value::T, % endfor pub system_font: SystemFont, - pub default_font_type: GenericFontFamily, } } diff --git a/components/style/properties/longhands/inherited_svg.mako.rs b/components/style/properties/longhands/inherited_svg.mako.rs index 820b2d25d04..b9d55c01b20 100644 --- a/components/style/properties/longhands/inherited_svg.mako.rs +++ b/components/style/properties/longhands/inherited_svg.mako.rs @@ -209,6 +209,9 @@ ${helpers.predefined_type( "MozContextProperties", "computed::MozContextProperties::default()", engines="gecko", + enabled_in="chrome", + gecko_pref="svg.context-properties.content.enabled", + has_effect_on_gecko_scrollbars=False, animation_value_type="none", spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-context-properties)", )} diff --git a/components/style/properties/longhands/inherited_text.mako.rs b/components/style/properties/longhands/inherited_text.mako.rs index 246e7ff36a6..3a66eee9a3f 100644 --- a/components/style/properties/longhands/inherited_text.mako.rs +++ b/components/style/properties/longhands/inherited_text.mako.rs @@ -238,12 +238,13 @@ ${helpers.predefined_type( )} ${helpers.predefined_type( - "-moz-tab-size", + "tab-size", "NonNegativeLengthOrNumber", "generics::length::LengthOrNumber::Number(From::from(8.0))", engines="gecko", animation_value_type="LengthOrNumber", spec="https://drafts.csswg.org/css-text-3/#tab-size-property", + aliases="-moz-tab-size", )} ${helpers.predefined_type( @@ -336,6 +337,9 @@ ${helpers.predefined_type( "text::MozControlCharacterVisibility", "Default::default()", engines="gecko", + enabled_in="chrome", + gecko_pref="layout.css.moz-control-character-visibility.enabled", + has_effect_on_gecko_scrollbars=False, animation_value_type="none", spec="Nonstandard" )} diff --git a/components/style/properties/longhands/inherited_ui.mako.rs b/components/style/properties/longhands/inherited_ui.mako.rs index f08abbc6235..badcc3f2012 100644 --- a/components/style/properties/longhands/inherited_ui.mako.rs +++ b/components/style/properties/longhands/inherited_ui.mako.rs @@ -95,6 +95,17 @@ ${helpers.predefined_type( has_effect_on_gecko_scrollbars=False, )} +${helpers.predefined_type( + "color-scheme", + "ColorScheme", + "specified::color::ColorScheme::normal()", + engines="gecko", + spec="https://drafts.csswg.org/css-color-adjust/#color-scheme-prop", + gecko_pref="layout.css.color-scheme.enabled", + animation_value_type="discrete", + has_effect_on_gecko_scrollbars=False, +)} + ${helpers.predefined_type( "scrollbar-color", "ui::ScrollbarColor", diff --git a/components/style/properties/longhands/outline.mako.rs b/components/style/properties/longhands/outline.mako.rs index e5cd09fbe82..c744f5f523f 100644 --- a/components/style/properties/longhands/outline.mako.rs +++ b/components/style/properties/longhands/outline.mako.rs @@ -42,20 +42,6 @@ ${helpers.predefined_type( spec="https://drafts.csswg.org/css-ui/#propdef-outline-width", )} -// The -moz-outline-radius-* properties are non-standard and not on a standards track. -% for corner in ["topleft", "topright", "bottomright", "bottomleft"]: - ${helpers.predefined_type( - "-moz-outline-radius-" + corner, - "BorderCornerRadius", - "computed::BorderCornerRadius::zero()", - engines="gecko", - boxed=True, - animation_value_type="BorderCornerRadius", - gecko_pref="layout.css.moz-outline-radius.enabled", - spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-outline-radius)", - )} -% endfor - ${helpers.predefined_type( "outline-offset", "Length", diff --git a/components/style/properties/longhands/position.mako.rs b/components/style/properties/longhands/position.mako.rs index 1795bde7e5f..7ca7772b9c7 100644 --- a/components/style/properties/longhands/position.mako.rs +++ b/components/style/properties/longhands/position.mako.rs @@ -434,7 +434,6 @@ ${helpers.predefined_type( "computed::length::NonNegativeLengthPercentageOrNormal::normal()", engines="gecko servo-2013", aliases="grid-column-gap" if engine == "gecko" else "", - extra_prefixes="moz:layout.css.prefixes.columns", servo_2013_pref="layout.columns.enabled", spec="https://drafts.csswg.org/css-align-3/#propdef-column-gap", animation_value_type="NonNegativeLengthPercentageOrNormal", diff --git a/components/style/properties/longhands/svg.mako.rs b/components/style/properties/longhands/svg.mako.rs index f6128693582..3d711462ff3 100644 --- a/components/style/properties/longhands/svg.mako.rs +++ b/components/style/properties/longhands/svg.mako.rs @@ -246,3 +246,13 @@ ${helpers.predefined_type( animation_value_type="LengthPercentage", spec="https://svgwg.org/svg2-draft/geometry.html#R", )} + +${helpers.predefined_type( + "d", + "DProperty", + "specified::DProperty::none()", + engines="gecko", + animation_value_type="ComputedValue", + gecko_pref="layout.css.d-property.enabled", + spec="https://svgwg.org/svg2-draft/paths.html#TheDProperty", +)} diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 99d283da46c..67183062e2f 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -232,7 +232,7 @@ pub mod shorthands { // which don't exist in `LonghandId`. <% - extra = [ + extra_variants = [ { "name": "CSSWideKeyword", "type": "WideKeywordDeclaration", @@ -252,7 +252,7 @@ pub mod shorthands { "copy": False, }, ] - for v in extra: + for v in extra_variants: variants.append(v) groups[v["type"]] = [v] %> @@ -394,6 +394,17 @@ impl MallocSizeOf for PropertyDeclaration { impl PropertyDeclaration { + /// Returns whether this is a variant of the Longhand(Value) type, rather + /// than one of the special variants in extra_variants. + fn is_longhand_value(&self) -> bool { + match *self { + % for v in extra_variants: + PropertyDeclaration::${v["name"]}(..) => false, + % endfor + _ => true, + } + } + /// Like the method on ToCss, but without the type parameter to avoid /// accidentally monomorphizing this large function multiple times for /// different writers. @@ -409,6 +420,24 @@ impl PropertyDeclaration { % endfor } } + + /// Returns the color value of a given property, for high-contrast-mode + /// tweaks. + pub(crate) fn color_value(&self) -> Option<<&crate::values::specified::Color> { + ${static_longhand_id_set("COLOR_PROPERTIES", lambda p: p.predefined_type == "Color")} + <% + # sanity check + assert data.longhands_by_name["background-color"].predefined_type == "Color" + + color_specified_type = data.longhands_by_name["background-color"].specified_type() + %> + let id = self.id().as_longhand()?; + if !COLOR_PROPERTIES.contains(id) || !self.is_longhand_value() { + return None; + } + let repr = self as *const _ as *const PropertyDeclarationVariantRepr<${color_specified_type}>; + Some(unsafe { &(*repr).value }) + } } /// A module with all the code related to animated properties. @@ -1366,6 +1395,9 @@ impl LonghandId { // font-size depends on math-depth's computed value. LonghandId::MathDepth | + + // color-scheme affects how system colors resolve. + LonghandId::ColorScheme | % endif // Needed to compute the first available font, in order to @@ -1471,74 +1503,66 @@ impl ShorthandId { /// /// Returns an error if writing to the stream fails, or if the declarations /// do not map to a shorthand. - pub fn longhands_to_css<'a, W, I>( + pub fn longhands_to_css( &self, - declarations: I, - dest: &mut CssWriter, - ) -> fmt::Result - where - W: Write, - I: Iterator, - { - match *self { - ShorthandId::All => { - // No need to try to serialize the declarations as the 'all' - // shorthand, since it only accepts CSS-wide keywords (and - // variable references), which will be handled in - // get_shorthand_appendable_value. - Err(fmt::Error) - } - % for property in data.shorthands_except_all(): - ShorthandId::${property.camel_case} => { - match shorthands::${property.ident}::LonghandsToSerialize::from_iter(declarations) { - Ok(longhands) => longhands.to_css(dest), - Err(_) => Err(fmt::Error) - } - }, - % endfor + declarations: &[&PropertyDeclaration], + dest: &mut CssStringWriter, + ) -> fmt::Result { + type LonghandsToCssFn = for<'a, 'b> fn(&'a [&'b PropertyDeclaration], &mut CssStringWriter) -> fmt::Result; + fn all_to_css(_: &[&PropertyDeclaration], _: &mut CssStringWriter) -> fmt::Result { + // No need to try to serialize the declarations as the 'all' + // shorthand, since it only accepts CSS-wide keywords (and variable + // references), which will be handled in + // get_shorthand_appendable_value. + Ok(()) } + + static LONGHANDS_TO_CSS: [LonghandsToCssFn; ${len(data.shorthands)}] = [ + % for shorthand in data.shorthands: + % if shorthand.ident == "all": + all_to_css, + % else: + shorthands::${shorthand.ident}::to_css, + % endif + % endfor + ]; + + LONGHANDS_TO_CSS[*self as usize](declarations, dest) } /// Finds and returns an appendable value for the given declarations. /// /// Returns the optional appendable value. - pub fn get_shorthand_appendable_value<'a, I>( + pub fn get_shorthand_appendable_value<'a, 'b: 'a>( self, - declarations: I, - ) -> Option> - where - I: IntoIterator, - I::IntoIter: Clone, - { - let declarations = declarations.into_iter(); - - // Only cloning iterators (a few pointers each) not declarations. - let mut declarations2 = declarations.clone(); - let mut declarations3 = declarations.clone(); - - let first_declaration = declarations2.next()?; + declarations: &'a [&'b PropertyDeclaration], + ) -> Option> { + let first_declaration = declarations.get(0)?; + let rest = || declarations.iter().skip(1); // https://drafts.csswg.org/css-variables/#variables-in-shorthands if let Some(css) = first_declaration.with_variables_from_shorthand(self) { - if declarations2.all(|d| d.with_variables_from_shorthand(self) == Some(css)) { - return Some(AppendableValue::Css { css, with_variables: true }); + if rest().all(|d| d.with_variables_from_shorthand(self) == Some(css)) { + return Some(AppendableValue::Css(css)); } return None; } // Check whether they are all the same CSS-wide keyword. if let Some(keyword) = first_declaration.get_css_wide_keyword() { - if declarations2.all(|d| d.get_css_wide_keyword() == Some(keyword)) { - return Some(AppendableValue::Css { - css: keyword.to_str(), - with_variables: false, - }); + if rest().all(|d| d.get_css_wide_keyword() == Some(keyword)) { + return Some(AppendableValue::Css(keyword.to_str())) } return None; } + if self == ShorthandId::All { + // 'all' only supports variables and CSS wide keywords. + return None; + } + // Check whether all declarations can be serialized as part of shorthand. - if declarations3.all(|d| d.may_serialize_as_part_of_shorthand()) { + if declarations.iter().all(|d| d.may_serialize_as_part_of_shorthand()) { return Some(AppendableValue::DeclarationsForShorthand(self, declarations)); } @@ -1596,23 +1620,20 @@ impl ShorthandId { input: &mut Parser<'i, 't>, ) -> Result<(), ParseError<'i>>; - fn unreachable<'i, 't>( + fn parse_all<'i, 't>( _: &mut SourcePropertyDeclaration, _: &ParserContext, - _: &mut Parser<'i, 't> + input: &mut Parser<'i, 't> ) -> Result<(), ParseError<'i>> { - unreachable!() + // 'all' accepts no value other than CSS-wide keywords + Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) } - // 'all' accepts no value other than CSS-wide keywords - if *self == ShorthandId::All { - return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); - } static PARSE_INTO: [ParseIntoFn; ${len(data.shorthands)}] = [ % for shorthand in data.shorthands: % if shorthand.ident == "all": - unreachable, + parse_all, % else: shorthands::${shorthand.ident}::parse_into, % endif @@ -1720,7 +1741,8 @@ impl UnparsedValue { let mut input = ParserInput::new(&css); let mut input = Parser::new(&mut input); - input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less. + input.skip_whitespace(); + if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) { return Cow::Owned(PropertyDeclaration::css_wide_keyword(longhand_id, keyword)); } @@ -2436,12 +2458,11 @@ impl PropertyDeclaration { debug_assert!(id.allowed_in(context), "{:?}", id); let non_custom_id = id.non_custom_id(); + input.skip_whitespace(); + let start = input.state(); match id { PropertyId::Custom(property_name) => { - // FIXME: fully implement https://github.com/w3c/csswg-drafts/issues/774 - // before adding skip_whitespace here. - // This probably affects some test results. let value = match input.try_parse(CSSWideKeyword::parse) { Ok(keyword) => CustomDeclarationValue::CSSWideKeyword(keyword), Err(()) => CustomDeclarationValue::Value( @@ -2456,7 +2477,6 @@ impl PropertyDeclaration { } PropertyId::LonghandAlias(id, _) | PropertyId::Longhand(id) => { - input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less. input.try_parse(CSSWideKeyword::parse).map(|keyword| { PropertyDeclaration::css_wide_keyword(id, keyword) }).or_else(|()| { @@ -2486,7 +2506,6 @@ impl PropertyDeclaration { } PropertyId::ShorthandAlias(id, _) | PropertyId::Shorthand(id) => { - input.skip_whitespace(); // Unnecessary for correctness, but may help try() rewind less. if let Ok(keyword) = input.try_parse(CSSWideKeyword::parse) { if id == ShorthandId::All { declarations.all_shorthand = AllShorthand::CSSWideKeyword(keyword) diff --git a/components/style/properties/shorthands/box.mako.rs b/components/style/properties/shorthands/box.mako.rs index 851232545c9..4beef9a0d95 100644 --- a/components/style/properties/shorthands/box.mako.rs +++ b/components/style/properties/shorthands/box.mako.rs @@ -316,15 +316,15 @@ ${helpers.two_properties_shorthand( name="page-break-before" flags="SHORTHAND_IN_GETCS IS_LEGACY_SHORTHAND" sub_properties="break-before" - spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-before" + spec="https://drafts.csswg.org/css-break-3/#page-break-properties" > pub fn parse_value<'i>( - _: &ParserContext, + context: &ParserContext, input: &mut Parser<'i, '_>, ) -> Result> { use crate::values::specified::box_::BreakBetween; Ok(expanded! { - break_before: BreakBetween::parse_legacy(input)?, + break_before: BreakBetween::parse_legacy(context, input)?, }) } @@ -340,15 +340,15 @@ ${helpers.two_properties_shorthand( name="page-break-after" flags="SHORTHAND_IN_GETCS IS_LEGACY_SHORTHAND" sub_properties="break-after" - spec="https://drafts.csswg.org/css2/page.html#propdef-page-break-after" + spec="https://drafts.csswg.org/css-break-3/#page-break-properties" > pub fn parse_value<'i>( - _: &ParserContext, + context: &ParserContext, input: &mut Parser<'i, '_>, ) -> Result> { use crate::values::specified::box_::BreakBetween; Ok(expanded! { - break_after: BreakBetween::parse_legacy(input)?, + break_after: BreakBetween::parse_legacy(context, input)?, }) } @@ -359,6 +359,30 @@ ${helpers.two_properties_shorthand( } +<%helpers:shorthand + engines="gecko" + name="page-break-inside" + flags="SHORTHAND_IN_GETCS IS_LEGACY_SHORTHAND" + sub_properties="break-inside" + spec="https://drafts.csswg.org/css-break-3/#page-break-properties" +> + pub fn parse_value<'i>( + context: &ParserContext, + input: &mut Parser<'i, '_>, + ) -> Result> { + use crate::values::specified::box_::BreakWithin; + Ok(expanded! { + break_inside: BreakWithin::parse_legacy(context, input)?, + }) + } + + impl<'a> ToCss for LonghandsToSerialize<'a> { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write { + self.break_inside.to_css_legacy(dest) + } + } + + <%helpers:shorthand name="offset" engines="gecko" sub_properties="offset-path offset-distance offset-rotate offset-anchor" diff --git a/components/style/properties/shorthands/column.mako.rs b/components/style/properties/shorthands/column.mako.rs index e7c775dff04..2fc8b72fae9 100644 --- a/components/style/properties/shorthands/column.mako.rs +++ b/components/style/properties/shorthands/column.mako.rs @@ -9,7 +9,6 @@ sub_properties="column-width column-count" servo_2013_pref="layout.columns.enabled", derive_serialize="True" - extra_prefixes="moz:layout.css.prefixes.columns" spec="https://drafts.csswg.org/css-multicol/#propdef-columns"> use crate::properties::longhands::{column_count, column_width}; @@ -60,7 +59,6 @@ <%helpers:shorthand name="column-rule" engines="gecko" - extra_prefixes="moz:layout.css.prefixes.columns" sub_properties="column-rule-width column-rule-style column-rule-color" derive_serialize="True" spec="https://drafts.csswg.org/css-multicol/#propdef-column-rule" diff --git a/components/style/properties/shorthands/font.mako.rs b/components/style/properties/shorthands/font.mako.rs index 26205c2ea9c..b24711b4aed 100644 --- a/components/style/properties/shorthands/font.mako.rs +++ b/components/style/properties/shorthands/font.mako.rs @@ -137,7 +137,7 @@ return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) } - let family = FontFamily::parse_specified(input)?; + let family = FontFamily::parse(context, input)?; Ok(expanded! { % for name in "style weight stretch variant_caps".split(): font_${name}: unwrap_or_initial!(font_${name}, ${name}), diff --git a/components/style/properties/shorthands/outline.mako.rs b/components/style/properties/shorthands/outline.mako.rs index db397498919..b90f8d966f3 100644 --- a/components/style/properties/shorthands/outline.mako.rs +++ b/components/style/properties/shorthands/outline.mako.rs @@ -78,50 +78,3 @@ } } - -// The -moz-outline-radius shorthand is non-standard and not on a standards track. -<%helpers:shorthand - name="-moz-outline-radius" - engines="gecko" - gecko_pref="layout.css.moz-outline-radius.enabled" - sub_properties="${' '.join( - '-moz-outline-radius-%s' % corner - for corner in ['topleft', 'topright', 'bottomright', 'bottomleft'] - )}" - spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-outline-radius)" -> - use crate::values::generics::rect::Rect; - use crate::values::specified::border::BorderRadius; - use crate::parser::Parse; - - pub fn parse_value<'i, 't>( - context: &ParserContext, - input: &mut Parser<'i, 't>, - ) -> Result> { - let radii = BorderRadius::parse(context, input)?; - Ok(expanded! { - _moz_outline_radius_topleft: radii.top_left, - _moz_outline_radius_topright: radii.top_right, - _moz_outline_radius_bottomright: radii.bottom_right, - _moz_outline_radius_bottomleft: radii.bottom_left, - }) - } - - impl<'a> ToCss for LonghandsToSerialize<'a> { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: fmt::Write { - use crate::values::generics::border::BorderCornerRadius; - - let LonghandsToSerialize { - _moz_outline_radius_topleft: &BorderCornerRadius(ref tl), - _moz_outline_radius_topright: &BorderCornerRadius(ref tr), - _moz_outline_radius_bottomright: &BorderCornerRadius(ref br), - _moz_outline_radius_bottomleft: &BorderCornerRadius(ref bl), - } = *self; - - let widths = Rect::new(tl.width(), tr.width(), br.width(), bl.width()); - let heights = Rect::new(tl.height(), tr.height(), br.height(), bl.height()); - - BorderRadius::serialize_rects(widths, heights, dest) - } - } - diff --git a/components/style/style_adjuster.rs b/components/style/style_adjuster.rs index 158be6d5dfb..5e66e91e894 100644 --- a/components/style/style_adjuster.rs +++ b/components/style/style_adjuster.rs @@ -232,9 +232,6 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { where E: TElement, { - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] - use crate::computed_values::list_style_position::T as ListStylePosition; - let mut blockify = false; macro_rules! blockify_if { ($if_what:expr) => { @@ -254,16 +251,6 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { blockify_if!(self.style.is_floating()); blockify_if!(self.style.is_absolutely_positioned()); - #[cfg(any(feature = "servo-layout-2013", feature = "gecko"))] - blockify_if!( - self.style.pseudo.map_or(false, |p| p.is_marker()) && - self.style.get_parent_list().clone_list_style_position() == - ListStylePosition::Outside && - !layout_parent_style - .get_box() - .clone_display() - .is_inline_flow() - ); if !blockify { return; @@ -818,6 +805,46 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { } } + /// A legacy ::marker (i.e. no 'content') without an author-specified 'font-family' + /// and 'list-style-type:disc|circle|square|disclosure-closed|disclosure-open' + /// is assigned 'font-family:-moz-bullet-font'. (This is for
  • etc.) + /// We don't want synthesized italic/bold for this font, so turn that off too. + /// Likewise for 'letter/word-spacing' -- unless the author specified it then reset + /// them to their initial value because traditionally we never added such spacing + /// between a legacy bullet and the list item's content, so we keep that behavior + /// for web-compat reasons. + /// We intentionally don't check 'list-style-image' below since we want it to use + /// the same font as its fallback ('list-style-type') in case it fails to load. + #[cfg(feature = "gecko")] + fn adjust_for_marker_pseudo(&mut self) { + use crate::values::computed::font::{FontFamily, FontSynthesis}; + use crate::values::computed::text::{LetterSpacing, WordSpacing}; + use crate::values::computed::counters::{Content}; + + let is_legacy_marker = self.style.pseudo.map_or(false, |p| p.is_marker()) && + self.style.get_list().clone_list_style_type().is_bullet() && + self.style.get_counters().clone_content() == Content::Normal; + if !is_legacy_marker { + return; + } + if !self.style.flags.get().contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_FAMILY) { + self.style.mutate_font().set_font_family(FontFamily::moz_bullet().clone()); + + // FIXME(mats): We can remove this if support for font-synthesis is added to @font-face rules. + // Then we can add it to the @font-face rule in html.css instead. + // https://github.com/w3c/csswg-drafts/issues/6081 + if !self.style.flags.get().contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS) { + self.style.mutate_font().set_font_synthesis(FontSynthesis::none()); + } + } + if !self.style.flags.get().contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_LETTER_SPACING) { + self.style.mutate_inherited_text().set_letter_spacing(LetterSpacing::normal()); + } + if !self.style.flags.get().contains(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING) { + self.style.mutate_inherited_text().set_word_spacing(WordSpacing::normal()); + } + } + /// Adjusts the style to account for various fixups that don't fit naturally /// into the cascade. /// @@ -883,6 +910,7 @@ impl<'a, 'b: 'a> StyleAdjuster<'a, 'b> { { self.adjust_for_appearance(element); self.adjust_for_inert(); + self.adjust_for_marker_pseudo(); } self.set_bits(); } diff --git a/components/style/stylesheets/import_rule.rs b/components/style/stylesheets/import_rule.rs index 396be242024..8f518f29bac 100644 --- a/components/style/stylesheets/import_rule.rs +++ b/components/style/stylesheets/import_rule.rs @@ -56,7 +56,13 @@ impl ImportSheet { /// exists. pub fn as_sheet(&self) -> Option<&crate::gecko::data::GeckoStyleSheet> { match *self { - ImportSheet::Sheet(ref s) => Some(s), + ImportSheet::Sheet(ref s) => { + debug_assert!(!s.hack_is_null()); + if s.hack_is_null() { + return None; + } + Some(s) + }, ImportSheet::Pending(_) => None, } } diff --git a/components/style/stylesheets/stylesheet.rs b/components/style/stylesheets/stylesheet.rs index 2f7706a6979..b8e7f246c19 100644 --- a/components/style/stylesheets/stylesheet.rs +++ b/components/style/stylesheets/stylesheet.rs @@ -65,6 +65,10 @@ pub struct StylesheetContents { pub source_map_url: RwLock>, /// This stylesheet's source URL. pub source_url: RwLock>, + + /// We don't want to allow construction outside of this file, to guarantee + /// that all contents are created with Arc<>. + _forbid_construction: (), } impl StylesheetContents { @@ -82,7 +86,7 @@ impl StylesheetContents { use_counters: Option<&UseCounters>, allow_import_rules: AllowImportRules, sanitization_data: Option<&mut SanitizationData>, - ) -> Self { + ) -> Arc { let namespaces = RwLock::new(Namespaces::default()); let (rules, source_map_url, source_url) = Stylesheet::parse_rules( css, @@ -99,7 +103,7 @@ impl StylesheetContents { sanitization_data, ); - Self { + Arc::new(Self { rules: CssRules::new(rules, &shared_lock), origin, url_data: RwLock::new(url_data), @@ -107,7 +111,8 @@ impl StylesheetContents { quirks_mode, source_map_url: RwLock::new(source_map_url), source_url: RwLock::new(source_url), - } + _forbid_construction: (), + }) } /// Creates a new StylesheetContents with the specified pre-parsed rules, @@ -126,9 +131,9 @@ impl StylesheetContents { origin: Origin, url_data: UrlExtraData, quirks_mode: QuirksMode, - ) -> Self { + ) -> Arc { debug_assert!(rules.is_static()); - Self { + Arc::new(Self { rules, origin, url_data: RwLock::new(url_data), @@ -136,7 +141,8 @@ impl StylesheetContents { quirks_mode, source_map_url: RwLock::new(None), source_url: RwLock::new(None), - } + _forbid_construction: (), + }) } /// Returns a reference to the list of rules. @@ -178,6 +184,7 @@ impl DeepCloneWithLock for StylesheetContents { namespaces: RwLock::new((*self.namespaces.read()).clone()), source_map_url: RwLock::new((*self.source_map_url.read()).clone()), source_url: RwLock::new((*self.source_url.read()).clone()), + _forbid_construction: (), } } } @@ -186,7 +193,7 @@ impl DeepCloneWithLock for StylesheetContents { #[derive(Debug)] pub struct Stylesheet { /// The contents of this stylesheet. - pub contents: StylesheetContents, + pub contents: Arc, /// The lock used for objects inside this stylesheet pub shared_lock: SharedRwLock, /// List of media associated with the Stylesheet. @@ -587,9 +594,9 @@ impl Clone for Stylesheet { // Make a deep clone of the media, using the new lock. let media = self.media.read_with(&guard).clone(); let media = Arc::new(lock.wrap(media)); - let contents = self + let contents = Arc::new(self .contents - .deep_clone_with_lock(&lock, &guard, &DeepCloneParams); + .deep_clone_with_lock(&lock, &guard, &DeepCloneParams)); Stylesheet { contents, diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 74704e707df..fb7334d3600 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -11,7 +11,7 @@ use crate::element_state::{DocumentState, ElementState}; #[cfg(feature = "gecko")] use crate::gecko_bindings::structs::{ServoStyleSetSizes, StyleRuleInclusion}; use crate::invalidation::element::invalidation_map::InvalidationMap; -use crate::invalidation::media_queries::EffectiveMediaQueryResults; +use crate::invalidation::media_queries::{EffectiveMediaQueryResults, MediaListKey, ToMediaListKey}; use crate::invalidation::stylesheets::RuleChangeKind; use crate::media_queries::Device; use crate::properties::{self, CascadeMode, ComputedValues}; @@ -26,8 +26,7 @@ use crate::stylesheet_set::{DataValidity, DocumentStylesheetSet, SheetRebuildKin use crate::stylesheet_set::{DocumentStylesheetFlusher, SheetCollectionFlusher}; use crate::stylesheets::keyframes_rule::KeyframesAnimation; use crate::stylesheets::viewport_rule::{self, MaybeNew, ViewportRule}; -use crate::stylesheets::StyleRule; -use crate::stylesheets::StylesheetInDocument; +use crate::stylesheets::{StyleRule, StylesheetInDocument, StylesheetContents}; #[cfg(feature = "gecko")] use crate::stylesheets::{CounterStyleRule, FontFaceRule, FontFeatureValuesRule, PageRule}; use crate::stylesheets::{CssRule, Origin, OriginSet, PerOrigin, PerOriginIter}; @@ -50,7 +49,9 @@ use smallbitvec::SmallBitVec; use smallvec::SmallVec; use std::sync::Mutex; use std::{mem, ops}; +use std::hash::{Hash, Hasher}; use style_traits::viewport::ViewportConstraints; +use fxhash::FxHashMap; /// The type of the stylesheets that the stylist contains. #[cfg(feature = "servo")] @@ -60,6 +61,37 @@ pub type StylistSheet = crate::stylesheets::DocumentStyleSheet; #[cfg(feature = "gecko")] pub type StylistSheet = crate::gecko::data::GeckoStyleSheet; +#[derive(Debug, Clone)] +struct StylesheetContentsPtr(Arc); + +impl PartialEq for StylesheetContentsPtr { + #[inline] + fn eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.0, &other.0) + } +} + +impl Eq for StylesheetContentsPtr {} + +impl Hash for StylesheetContentsPtr { + fn hash(&self, state: &mut H) { + let contents: &StylesheetContents = &*self.0; + (contents as *const StylesheetContents).hash(state) + } +} + +type StyleSheetContentList = Vec; + +/// A key in the cascade data cache. +#[derive(Debug, Hash, Default, PartialEq, Eq)] +struct CascadeDataCacheKey { + media_query_results: Vec, + contents: StyleSheetContentList, +} + +unsafe impl Send for CascadeDataCacheKey {} +unsafe impl Sync for CascadeDataCacheKey {} + trait CascadeDataCacheEntry : Sized { /// Returns a reference to the cascade data. fn cascade_data(&self) -> &CascadeData; @@ -80,7 +112,7 @@ trait CascadeDataCacheEntry : Sized { } struct CascadeDataCache { - entries: Vec>, + entries: FxHashMap>, } impl CascadeDataCache @@ -88,7 +120,7 @@ where Entry: CascadeDataCacheEntry, { fn new() -> Self { - Self { entries: vec![] } + Self { entries: Default::default() } } fn len(&self) -> usize { @@ -110,51 +142,66 @@ where where S: StylesheetInDocument + PartialEq + 'static, { + use std::collections::hash_map::Entry as HashMapEntry; debug!("StyleSheetCache::lookup({})", self.len()); if !collection.dirty() { return Ok(None); } - let mut key = EffectiveMediaQueryResults::new(); + let mut key = CascadeDataCacheKey::default(); for sheet in collection.sheets() { - CascadeData::collect_applicable_media_query_results_into(device, sheet, guard, &mut key) + CascadeData::collect_applicable_media_query_results_into( + device, + sheet, + guard, + &mut key.media_query_results, + &mut key.contents, + ) } - for entry in &self.entries { - if std::ptr::eq(&**entry, old_entry) { + let new_entry; + match self.entries.entry(key) { + HashMapEntry::Vacant(e) => { + debug!("> Picking the slow path (not in the cache)"); + new_entry = Entry::rebuild( + device, + quirks_mode, + collection, + guard, + old_entry, + )?; + e.insert(new_entry.clone()); + } + HashMapEntry::Occupied(mut e) => { // Avoid reusing our old entry (this can happen if we get // invalidated due to CSSOM mutations and our old stylesheet - // contents were already unique, for example). This old entry - // will be pruned from the cache with take_unused() afterwards. - continue; - } - if entry.cascade_data().effective_media_query_results != key { - continue; - } - if log_enabled!(log::Level::Debug) { - debug!("cache hit for:"); - for sheet in collection.sheets() { - debug!(" > {:?}", sheet); + // contents were already unique, for example). + if !std::ptr::eq(&**e.get(), old_entry) { + if log_enabled!(log::Level::Debug) { + debug!("cache hit for:"); + for sheet in collection.sheets() { + debug!(" > {:?}", sheet); + } + } + // The line below ensures the "committed" bit is updated + // properly. + collection.each(|_, _| true); + return Ok(Some(e.get().clone())); } + + debug!("> Picking the slow path due to same entry as old"); + new_entry = Entry::rebuild( + device, + quirks_mode, + collection, + guard, + old_entry, + )?; + e.insert(new_entry.clone()); } - // The line below ensures the "committed" bit is updated properly - // below. - collection.each(|_, _| true); - return Ok(Some(entry.clone())); } - debug!("> Picking the slow path"); - - let new_entry = Entry::rebuild( - device, - quirks_mode, - collection, - guard, - old_entry, - )?; - - self.entries.push(new_entry.clone()); Ok(Some(new_entry)) } @@ -167,25 +214,27 @@ where /// cache to not deadlock. fn take_unused(&mut self) -> SmallVec<[Arc; 3]> { let mut unused = SmallVec::new(); - for i in (0..self.entries.len()).rev() { + self.entries.retain(|_key, value| { // is_unique() returns false for static references, but we never // have static references to UserAgentCascadeDatas. If we did, it // may not make sense to put them in the cache in the first place. - if self.entries[i].is_unique() { - unused.push(self.entries.remove(i)); + if !value.is_unique() { + return true; } - } + unused.push(value.clone()); + false + }); unused } - fn take_all(&mut self) -> Vec> { - mem::replace(&mut self.entries, Vec::new()) + fn take_all(&mut self) -> FxHashMap> { + mem::take(&mut self.entries) } #[cfg(feature = "gecko")] fn add_size_of(&self, ops: &mut MallocSizeOfOps, sizes: &mut ServoStyleSetSizes) { sizes.mOther += self.entries.shallow_size_of(ops); - for arc in self.entries.iter() { + for (_key, arc) in self.entries.iter() { // These are primary Arc references that can be measured // unconditionally. sizes.mOther += arc.unconditional_shallow_size_of(ops); @@ -2033,7 +2082,8 @@ impl CascadeData { device: &Device, stylesheet: &S, guard: &SharedRwLockReadGuard, - results: &mut EffectiveMediaQueryResults, + results: &mut Vec, + contents_list: &mut StyleSheetContentList, ) where S: StylesheetInDocument + 'static, { @@ -2042,19 +2092,25 @@ impl CascadeData { } debug!(" + {:?}", stylesheet); - results.saw_effective(stylesheet.contents()); + let contents = stylesheet.contents(); + results.push(contents.to_media_list_key()); + + // Safety: StyleSheetContents are reference-counted with Arc. + contents_list.push(StylesheetContentsPtr(unsafe { + Arc::from_raw_addrefed(contents) + })); for rule in stylesheet.effective_rules(device, guard) { match *rule { CssRule::Import(ref lock) => { let import_rule = lock.read_with(guard); debug!(" + {:?}", import_rule.stylesheet.media(guard)); - results.saw_effective(import_rule); + results.push(import_rule.to_media_list_key()); }, CssRule::Media(ref lock) => { let media_rule = lock.read_with(guard); debug!(" + {:?}", media_rule.media_queries.read_with(guard)); - results.saw_effective(media_rule); + results.push(media_rule.to_media_list_key()); }, _ => {}, } diff --git a/components/style/values/animated/color.rs b/components/style/values/animated/color.rs index 773310dd9b6..f99e344a57a 100644 --- a/components/style/values/animated/color.rs +++ b/components/style/values/animated/color.rs @@ -7,6 +7,8 @@ use crate::values::animated::{Animate, Procedure, ToAnimatedZero}; use crate::values::distance::{ComputeSquaredDistance, SquaredDistance}; use crate::values::generics::color::{Color as GenericColor, ComplexColorRatios}; +use crate::values::specified::color::{ColorSpaceKind, HueAdjuster}; +use euclid::default::{Transform3D, Vector3D}; /// An animated RGBA color. /// @@ -41,6 +43,26 @@ impl RGBA { alpha, } } + + /// Returns whether or not the colour is in gamut for sRGB. + pub fn in_gamut(&self) -> bool { + 0. <= self.red && + self.red <= 1. && + 0. <= self.green && + self.green <= 1. && + 0. <= self.blue && + self.blue <= 1. + } + + /// Returns the colour with coordinates clamped to the sRGB range. + pub fn clamp(&self) -> Self { + Self { + red: self.red.max(0.).min(1.), + green: self.green.max(0.).min(1.), + blue: self.blue.max(0.).min(1.), + alpha: self.alpha, + } + } } impl Animate for RGBA { @@ -106,43 +128,69 @@ impl Color { /// Mix two colors into one. pub fn mix( + color_space: ColorSpaceKind, left_color: &Color, left_weight: f32, right_color: &Color, right_weight: f32, + hue_adjuster: HueAdjuster, ) -> Self { - let left_bg = left_color.scaled_rgba(); - let right_bg = right_color.scaled_rgba(); - let alpha = (left_bg.alpha * left_weight + - right_bg.alpha * right_weight) - .min(1.); - - let mut fg = 0.; - let mut red = 0.; - let mut green = 0.; - let mut blue = 0.; - - let colors = [ - (left_color, &left_bg, left_weight), - (right_color, &right_bg, right_weight), - ]; - - for &(color, bg, weight) in &colors { - fg += color.ratios.fg * weight; - - red += bg.red * bg.alpha * weight; - green += bg.green * bg.alpha * weight; - blue += bg.blue * bg.alpha * weight; + match color_space { + ColorSpaceKind::Srgb => Self::mix_in::( + left_color, + left_weight, + right_color, + right_weight, + hue_adjuster, + ), + ColorSpaceKind::Xyz => Self::mix_in::( + left_color, + left_weight, + right_color, + right_weight, + hue_adjuster, + ), + ColorSpaceKind::Lab => Self::mix_in::( + left_color, + left_weight, + right_color, + right_weight, + hue_adjuster, + ), + ColorSpaceKind::Lch => Self::mix_in::( + left_color, + left_weight, + right_color, + right_weight, + hue_adjuster, + ), } + } - let color = if alpha <= 0. { - RGBA::transparent() + fn mix_in( + left_color: &Color, + left_weight: f32, + right_color: &Color, + right_weight: f32, + hue_adjuster: HueAdjuster, + ) -> Self + where + S: ModelledColor, + { + let left_bg = S::from(left_color.scaled_rgba()); + let right_bg = S::from(right_color.scaled_rgba()); + + let color = S::lerp(left_bg, left_weight, right_bg, right_weight, hue_adjuster); + let rgba: RGBA = color.into(); + let rgba = if !rgba.in_gamut() { + // TODO: Better gamut mapping. + rgba.clamp() } else { - let inv = 1. / alpha; - RGBA::new(red * inv, green * inv, blue * inv, alpha) + rgba }; - Self::new(color, ComplexColorRatios { bg: 1., fg }) + let fg = left_color.ratios.fg * left_weight + right_color.ratios.fg * right_weight; + Self::new(rgba, ComplexColorRatios { bg: 1., fg }) } fn scaled_rgba(&self) -> RGBA { @@ -309,3 +357,573 @@ impl ToAnimatedZero for Color { Ok(RGBA::transparent().into()) } } + +/// A color modelled in a specific color space (such as sRGB or CIE XYZ). +/// +/// For now, colors modelled in other spaces need to be convertible to and from +/// `RGBA` because we use sRGB for displaying colors. +trait ModelledColor: Clone + Copy + From + Into { + /// Linearly interpolate between the left and right colors. + /// + /// The HueAdjuster parameter is only for color spaces where the hue is + /// represented as an angle (e.g., CIE LCH). + fn lerp( + left_bg: Self, + left_weight: f32, + right_bg: Self, + right_weight: f32, + hue_adjuster: HueAdjuster, + ) -> Self; +} + +impl ModelledColor for RGBA { + fn lerp( + left_bg: Self, + left_weight: f32, + right_bg: Self, + right_weight: f32, + _: HueAdjuster, + ) -> Self { + // Interpolation with alpha, as per + // https://drafts.csswg.org/css-color/#interpolation-alpha. + let mut red = 0.; + let mut green = 0.; + let mut blue = 0.; + + // sRGB is a rectangular othogonal color space, so all component values + // are multiplied by the alpha value. + for &(bg, weight) in &[(left_bg, left_weight), (right_bg, right_weight)] { + red += bg.red * bg.alpha * weight; + green += bg.green * bg.alpha * weight; + blue += bg.blue * bg.alpha * weight; + } + + let alpha = (left_bg.alpha * left_weight + right_bg.alpha * right_weight).min(1.); + if alpha <= 0. { + RGBA::transparent() + } else { + let inv = 1. / alpha; + RGBA::new(red * inv, green * inv, blue * inv, alpha) + } + } +} + +/// An animated XYZA colour. +#[derive(Clone, Copy, Debug)] +pub struct XYZA { + /// The x component. + pub x: f32, + /// The y component. + pub y: f32, + /// The z component. + pub z: f32, + /// The alpha component. + pub alpha: f32, +} + +impl XYZA { + /// Returns a transparent color. + #[inline] + pub fn transparent() -> Self { + Self { + x: 0., + y: 0., + z: 0., + alpha: 0., + } + } +} + +impl ModelledColor for XYZA { + fn lerp( + left_bg: Self, + left_weight: f32, + right_bg: Self, + right_weight: f32, + _: HueAdjuster, + ) -> Self { + // Interpolation with alpha, as per + // https://drafts.csswg.org/css-color/#interpolation-alpha. + let mut x = 0.; + let mut y = 0.; + let mut z = 0.; + + // CIE XYZ is a rectangular othogonal color space, so all component + // values are multiplied by the alpha value. + for &(bg, weight) in &[(left_bg, left_weight), (right_bg, right_weight)] { + x += bg.x * bg.alpha * weight; + y += bg.y * bg.alpha * weight; + z += bg.z * bg.alpha * weight; + } + + let alpha = (left_bg.alpha * left_weight + right_bg.alpha * right_weight).min(1.); + if alpha <= 0. { + Self::transparent() + } else { + let inv = 1. / alpha; + Self { + x: x * inv, + y: y * inv, + z: z * inv, + alpha, + } + } + } +} + +/// An animated LABA colour. +#[derive(Clone, Copy, Debug)] +pub struct LABA { + /// The lightness component. + pub lightness: f32, + /// The a component. + pub a: f32, + /// The b component. + pub b: f32, + /// The alpha component. + pub alpha: f32, +} + +impl LABA { + /// Returns a transparent color. + #[inline] + pub fn transparent() -> Self { + Self { + lightness: 0., + a: 0., + b: 0., + alpha: 0., + } + } +} + +impl ModelledColor for LABA { + fn lerp( + left_bg: Self, + left_weight: f32, + right_bg: Self, + right_weight: f32, + _: HueAdjuster, + ) -> Self { + // Interpolation with alpha, as per + // https://drafts.csswg.org/css-color/#interpolation-alpha. + let mut lightness = 0.; + let mut a = 0.; + let mut b = 0.; + + // CIE LAB is a rectangular othogonal color space, so all component + // values are multiplied by the alpha value. + for &(bg, weight) in &[(left_bg, left_weight), (right_bg, right_weight)] { + lightness += bg.lightness * bg.alpha * weight; + a += bg.a * bg.alpha * weight; + b += bg.b * bg.alpha * weight; + } + + let alpha = (left_bg.alpha * left_weight + right_bg.alpha * right_weight).min(1.); + if alpha <= 0. { + Self::transparent() + } else { + let inv = 1. / alpha; + Self { + lightness: lightness * inv, + a: a * inv, + b: b * inv, + alpha, + } + } + } +} + +/// An animated LCHA colour. +#[derive(Clone, Copy, Debug)] +pub struct LCHA { + /// The lightness component. + pub lightness: f32, + /// The chroma component. + pub chroma: f32, + /// The hua component. + pub hue: f32, + /// The alpha component. + pub alpha: f32, +} + +impl LCHA { + /// Returns a transparent color. + #[inline] + pub fn transparent() -> Self { + Self { + lightness: 0., + chroma: 0., + hue: 0., + alpha: 0., + } + } +} + +impl LCHA { + fn adjust(left_bg: Self, right_bg: Self, hue_adjuster: HueAdjuster) -> (Self, Self) { + use std::f32::consts::{PI, TAU}; + + let mut left_bg = left_bg; + let mut right_bg = right_bg; + + // Adjust the hue angle as per + // https://drafts.csswg.org/css-color/#hue-interpolation. + // + // If both hue angles are NAN, they should be set to 0. Otherwise, if a + // single hue angle is NAN, it should use the other hue angle. + if left_bg.hue.is_nan() || right_bg.hue.is_nan() { + if left_bg.hue.is_nan() && right_bg.hue.is_nan() { + left_bg.hue = 0.; + right_bg.hue = 0.; + } else if left_bg.hue.is_nan() { + left_bg.hue = right_bg.hue; + } else if right_bg.hue.is_nan() { + right_bg.hue = left_bg.hue; + } + } + + if hue_adjuster != HueAdjuster::Specified { + // Normalize hue into [0, 2 * PI) + while left_bg.hue < 0. { + left_bg.hue += TAU; + } + while left_bg.hue > TAU { + left_bg.hue -= TAU; + } + + while right_bg.hue < 0. { + right_bg.hue += TAU; + } + while right_bg.hue >= TAU { + right_bg.hue -= TAU; + } + } + + match hue_adjuster { + HueAdjuster::Shorter => { + let delta = right_bg.hue - left_bg.hue; + + if delta > PI { + left_bg.hue += PI; + } else if delta < -1. * PI { + right_bg.hue += PI; + } + }, + + HueAdjuster::Longer => { + let delta = right_bg.hue - left_bg.hue; + if 0. < delta && delta < PI { + left_bg.hue += TAU; + } else if -1. * PI < delta && delta < 0. { + right_bg.hue += TAU; + } + }, + + HueAdjuster::Increasing => { + if right_bg.hue < left_bg.hue { + right_bg.hue += TAU; + } + }, + + HueAdjuster::Decreasing => { + if left_bg.hue < right_bg.hue { + left_bg.hue += TAU; + } + }, + + //Angles are not adjusted. They are interpolated like any other + //component. + HueAdjuster::Specified => {}, + } + + (left_bg, right_bg) + } +} + +impl ModelledColor for LCHA { + fn lerp( + left_bg: Self, + left_weight: f32, + right_bg: Self, + right_weight: f32, + hue_adjuster: HueAdjuster, + ) -> Self { + // Interpolation with alpha, as per + // https://drafts.csswg.org/css-color/#interpolation-alpha. + let (left_bg, right_bg) = Self::adjust(left_bg, right_bg, hue_adjuster); + + let mut lightness = 0.; + let mut chroma = 0.; + let mut hue = 0.; + + // CIE LCH is a cylindical polar color space, so all component values + // are multiplied by the alpha value. + for &(bg, weight) in &[(left_bg, left_weight), (right_bg, right_weight)] { + lightness += bg.lightness * bg.alpha * weight; + chroma += bg.chroma * bg.alpha * weight; + // LCHA is a cylindrical color space so the hue coordinate is not + // pre-multipled by the alpha component when interpolating. + hue += bg.hue * weight; + } + + let alpha = (left_bg.alpha * left_weight + right_bg.alpha * right_weight).min(1.); + if alpha <= 0. { + Self::transparent() + } else { + let inv = 1. / alpha; + Self { + lightness: lightness * inv, + chroma: chroma * inv, + hue, + alpha, + } + } + } +} + +impl From for XYZA { + /// Convert an RGBA colour to XYZ as specified in [1]. + /// + /// [1]: https://drafts.csswg.org/css-color/#rgb-to-lab + fn from(rgba: RGBA) -> Self { + fn linearize(value: f32) -> f32 { + let sign = if value < 0. { -1. } else { 1. }; + let abs = value.abs(); + if abs < 0.04045 { + return value / 12.92; + } + + sign * ((abs + 0.055) / 1.055).powf(2.4) + } + + #[cfg_attr(rustfmt, rustfmt_skip)] + const SRGB_TO_XYZ: Transform3D = Transform3D::new( + 0.41239079926595934, 0.21263900587151027, 0.01933081871559182, 0., + 0.357584339383878, 0.715168678767756, 0.11919477979462598, 0., + 0.1804807884018343, 0.07219231536073371, 0.9505321522496607, 0., + 0., 0., 0., 1., + ); + + #[cfg_attr(rustfmt, rustfmt_skip)] + const BRADFORD: Transform3D = Transform3D::new( + 1.0479298208405488, 0.029627815688159344, -0.009243058152591178, 0., + 0.022946793341019088, 0.990434484573249, 0.015055144896577895, 0., + -0.05019222954313557, -0.01707382502938514, 0.7518742899580008, 0., + 0., 0., 0., 1., + ); + + // 1. Convert from sRGB to linear-light sRGB (undo gamma encoding). + let rgb = Vector3D::new( + linearize(rgba.red), + linearize(rgba.green), + linearize(rgba.blue), + ); + + // 2. Convert from linear sRGB to CIE XYZ. + // 3. Convert from a D65 whitepoint (used by sRGB) to the D50 whitepoint used in XYZ + // with the Bradford transform. + let xyz = SRGB_TO_XYZ.then(&BRADFORD).transform_vector3d(rgb); + + XYZA { + x: xyz.x, + y: xyz.y, + z: xyz.z, + alpha: rgba.alpha, + } + } +} + +impl From for LABA { + /// Convert an XYZ colour to LAB as specified in [1] and [2]. + /// + /// [1]: https://drafts.csswg.org/css-color/#rgb-to-lab + /// [2]: https://drafts.csswg.org/css-color/#color-conversion-code + fn from(xyza: XYZA) -> Self { + const WHITE: [f32; 3] = [0.96422, 1., 0.82521]; + + fn compute_f(value: f32) -> f32 { + const EPSILON: f32 = 216. / 24389.; + const KAPPA: f32 = 24389. / 27.; + + if value > EPSILON { + value.cbrt() + } else { + (KAPPA * value + 16.) / 116. + } + } + + // 4. Convert D50-adapted XYZ to Lab. + let f = [ + compute_f(xyza.x / WHITE[0]), + compute_f(xyza.y / WHITE[1]), + compute_f(xyza.z / WHITE[2]), + ]; + + let lightness = 116. * f[1] - 16.; + let a = 500. * (f[0] - f[1]); + let b = 200. * (f[1] - f[2]); + + LABA { + lightness, + a, + b, + alpha: xyza.alpha, + } + } +} + +impl From for LCHA { + /// Convert a LAB color to LCH as specified in [1]. + /// + /// [1]: https://drafts.csswg.org/css-color/#color-conversion-code + fn from(laba: LABA) -> Self { + let hue = laba.b.atan2(laba.a); + let chroma = (laba.a * laba.a + laba.b * laba.b).sqrt(); + LCHA { + lightness: laba.lightness, + chroma, + hue, + alpha: laba.alpha, + } + } +} + +impl From for LABA { + /// Convert a LCH color to LAB as specified in [1]. + /// + /// [1]: https://drafts.csswg.org/css-color/#color-conversion-code + fn from(lcha: LCHA) -> Self { + let a = lcha.chroma * lcha.hue.cos(); + let b = lcha.chroma * lcha.hue.sin(); + LABA { + lightness: lcha.lightness, + a, + b, + alpha: lcha.alpha, + } + } +} + +impl From for XYZA { + /// Convert a CIELAB color to XYZ as specified in [1] and [2]. + /// + /// [1]: https://drafts.csswg.org/css-color/#lab-to-predefined + /// [2]: https://drafts.csswg.org/css-color/#color-conversion-code + fn from(laba: LABA) -> Self { + // 1. Convert LAB to (D50-adapated) XYZ. + const KAPPA: f32 = 24389. / 27.; + const EPSILON: f32 = 216. / 24389.; + const WHITE: [f32; 3] = [0.96422, 1., 0.82521]; + + let f1 = (laba.lightness + 16f32) / 116f32; + let f0 = (laba.a / 500.) + f1; + let f2 = f1 - laba.b / 200.; + + let x = if f0.powf(3.) > EPSILON { + f0.powf(3.) + } else { + (116. * f0 - 16.) / KAPPA + }; + let y = if laba.lightness > KAPPA * EPSILON { + ((laba.lightness + 16.) / 116.).powf(3.) + } else { + laba.lightness / KAPPA + }; + let z = if f2.powf(3.) > EPSILON { + f2.powf(3.) + } else { + (116. * f2 - 16.) / KAPPA + }; + + XYZA { + x: x * WHITE[0], + y: y * WHITE[1], + z: z * WHITE[2], + alpha: laba.alpha, + } + } +} + +impl From for RGBA { + /// Convert an XYZ color to sRGB as specified in [1] and [2]. + /// + /// [1]: https://www.w3.org/TR/css-color-4/#lab-to-predefined + /// [2]: https://www.w3.org/TR/css-color-4/#color-conversion-code + fn from(xyza: XYZA) -> Self { + #[cfg_attr(rustfmt, rustfmt_skip)] + const BRADFORD_INVERSE: Transform3D = Transform3D::new( + 0.9554734527042182, -0.028369706963208136, 0.012314001688319899, 0., + -0.023098536874261423, 1.0099954580058226, -0.020507696433477912, 0., + 0.0632593086610217, 0.021041398966943008, 1.3303659366080753, 0., + 0., 0., 0., 1., + ); + + #[cfg_attr(rustfmt, rustfmt_skip)] + const XYZ_TO_SRGB: Transform3D = Transform3D::new( + 3.2409699419045226, -0.9692436362808796, 0.05563007969699366, 0., + -1.537383177570094, 1.8759675015077202, -0.20397695888897652, 0., + -0.4986107602930034, 0.04155505740717559, 1.0569715142428786, 0., + 0., 0., 0., 1., + ); + + // 2. Convert from a D50 whitepoint (used by Lab) to the D65 whitepoint + // used in sRGB, with the Bradford transform. + // 3. Convert from (D65-adapted) CIE XYZ to linear-light srgb + let xyz = Vector3D::new(xyza.x, xyza.y, xyza.z); + let linear_rgb = BRADFORD_INVERSE.then(&XYZ_TO_SRGB).transform_vector3d(xyz); + + // 4. Convert from linear-light srgb to srgb (do gamma encoding). + fn delinearize(value: f32) -> f32 { + let sign = if value < 0. { -1. } else { 1. }; + let abs = value.abs(); + + if abs > 0.0031308 { + sign * (1.055 * abs.powf(1. / 2.4) - 0.055) + } else { + 12.92 * value + } + } + + let red = delinearize(linear_rgb.x); + let green = delinearize(linear_rgb.y); + let blue = delinearize(linear_rgb.z); + + RGBA { + red, + green, + blue, + alpha: xyza.alpha, + } + } +} + +impl From for LABA { + fn from(rgba: RGBA) -> Self { + let xyza: XYZA = rgba.into(); + xyza.into() + } +} + +impl From for RGBA { + fn from(laba: LABA) -> Self { + let xyza: XYZA = laba.into(); + xyza.into() + } +} + +impl From for LCHA { + fn from(rgba: RGBA) -> Self { + let xyza: XYZA = rgba.into(); + let laba: LABA = xyza.into(); + laba.into() + } +} + +impl From for RGBA { + fn from(lcha: LCHA) -> Self { + let laba: LABA = lcha.into(); + let xyza: XYZA = laba.into(); + xyza.into() + } +} diff --git a/components/style/values/animated/transform.rs b/components/style/values/animated/transform.rs index 621ae60084d..598bf9b59a7 100644 --- a/components/style/values/animated/transform.rs +++ b/components/style/values/animated/transform.rs @@ -1105,21 +1105,27 @@ impl Animate for ComputedTransformOperation { // interpolated as defined in section Interpolation of // Matrices afterwards. // - let from = create_perspective_matrix(fd.px()); - let to = create_perspective_matrix(td.px()); + let from = create_perspective_matrix(fd.infinity_or(|l| l.px())); + let to = create_perspective_matrix(td.infinity_or(|l| l.px())); let interpolated = Matrix3D::from(from).animate(&Matrix3D::from(to), procedure)?; let decomposed = decompose_3d_matrix(interpolated)?; let perspective_z = decomposed.perspective.2; - let used_value = if perspective_z == 0. { - 0. + // Clamp results outside of the -1 to 0 range so that we get perspective + // function values between 1 and infinity. + let used_value = if perspective_z >= 0. { + transform::PerspectiveFunction::None } else { - -1. / perspective_z + transform::PerspectiveFunction::Length(CSSPixelLength::new( + if perspective_z <= -1. { + 1. + } else { + -1. / perspective_z + } + )) }; - Ok(TransformOperation::Perspective(CSSPixelLength::new( - used_value, - ))) + Ok(TransformOperation::Perspective(used_value)) }, _ if self.is_translate() && other.is_translate() => self .to_translate_3d() @@ -1198,14 +1204,18 @@ impl ComputeSquaredDistance for ComputedTransformOperation { ( &TransformOperation::Perspective(ref fd), &TransformOperation::Perspective(ref td), - ) => fd.compute_squared_distance(td), + ) => { + fd.infinity_or(|l| l.px()) + .compute_squared_distance(&td.infinity_or(|l| l.px())) + }, (&TransformOperation::Perspective(ref p), &TransformOperation::Matrix3D(ref m)) | (&TransformOperation::Matrix3D(ref m), &TransformOperation::Perspective(ref p)) => { // FIXME(emilio): Is this right? Why interpolating this with // Perspective but not with anything else? let mut p_matrix = Matrix3D::identity(); - if p.px() > 0. { - p_matrix.m34 = -1. / p.px(); + let p = p.infinity_or(|p| p.px()); + if p >= 0. { + p_matrix.m34 = -1. / p.max(1.); } p_matrix.compute_squared_distance(&m) }, diff --git a/components/style/values/computed/color.rs b/components/style/values/computed/color.rs index 664fed95886..36cd1db4a42 100644 --- a/components/style/values/computed/color.rs +++ b/components/style/values/computed/color.rs @@ -11,6 +11,8 @@ use cssparser::{Color as CSSParserColor, RGBA}; use std::fmt; use style_traits::{CssWriter, ToCss}; +pub use crate::values::specified::color::ColorScheme; + /// The computed value of the `color` property. pub type ColorPropertyValue = RGBA; diff --git a/components/style/values/computed/font.rs b/components/style/values/computed/font.rs index 0bd177bcc85..e2b29f61d5e 100644 --- a/components/style/values/computed/font.rs +++ b/components/style/values/computed/font.rs @@ -4,13 +4,12 @@ //! Computed values for font properties -#[cfg(feature = "gecko")] -use crate::gecko_bindings::sugar::refptr::RefPtr; #[cfg(feature = "gecko")] use crate::gecko_bindings::{bindings, structs}; -use crate::values::animated::{ToAnimatedValue, ToAnimatedZero}; +use crate::parser::{Parse, ParserContext}; +use crate::values::animated::ToAnimatedValue; use crate::values::computed::{ - Angle, Context, Integer, Length, NonNegativeLength, NonNegativePercentage, + Angle, Context, Integer, Length, NonNegativeLength, NonNegativeNumber, NonNegativePercentage, }; use crate::values::computed::{Number, Percentage, ToComputedValue}; use crate::values::generics::font::{FeatureTagValue, FontSettings, VariationValue}; @@ -26,13 +25,7 @@ use cssparser::{serialize_identifier, CssStringWriter, Parser}; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use std::fmt::{self, Write}; use std::hash::{Hash, Hasher}; -#[cfg(feature = "gecko")] -use std::mem::{self, ManuallyDrop}; -#[cfg(feature = "servo")] -use std::slice; use style_traits::{CssWriter, ParseError, ToCss}; -#[cfg(feature = "gecko")] -use to_shmem::{self, SharedMemoryBuilder, ToShmem}; pub use crate::values::computed::Length as MozScriptMinSize; pub use crate::values::specified::font::{FontSynthesis, MozScriptSizeMultiplier}; @@ -183,6 +176,7 @@ impl ToAnimatedValue for FontSize { #[derive(Clone, Debug, Eq, PartialEq, ToComputedValue, ToResolvedValue)] #[cfg_attr(feature = "servo", derive(Hash, MallocSizeOf, Serialize, Deserialize))] /// Specifies a prioritized list of font family names or generic family names. +#[repr(C)] pub struct FontFamily { /// The actual list of family names. pub families: FontFamilyList, @@ -190,27 +184,105 @@ pub struct FontFamily { pub is_system_font: bool, } +macro_rules! static_font_family { + ($ident:ident, $family:expr) => { + lazy_static! { + static ref $ident: FontFamily = FontFamily { + families: FontFamilyList { + #[cfg(feature = "gecko")] + list: crate::ArcSlice::from_iter_leaked(std::iter::once($family)), + #[cfg(feature = "servo")] + list: Box::new([$family]), + #[cfg(feature = "gecko")] + fallback: GenericFontFamily::None, + }, + is_system_font: false, + }; + } + }; +} + + impl FontFamily { #[inline] /// Get default font family as `serif` which is a generic font-family pub fn serif() -> Self { - FontFamily { - families: FontFamilyList::new(Box::new([SingleFontFamily::Generic( - GenericFontFamily::Serif, - )])), - is_system_font: false, + Self::generic(GenericFontFamily::Serif).clone() + } + + /// Returns the font family for `-moz-bullet-font`. + #[cfg(feature = "gecko")] + pub(crate) fn moz_bullet() -> &'static Self { + static_font_family!(MOZ_BULLET, SingleFontFamily::FamilyName(FamilyName { + name: atom!("-moz-bullet-font"), + syntax: FontFamilyNameSyntax::Identifiers, + })); + + &*MOZ_BULLET + } + + /// Returns a font family for a single system font. + #[cfg(feature = "gecko")] + pub fn for_system_font(name: &str) -> Self { + Self { + families: FontFamilyList { + list: crate::ArcSlice::from_iter(std::iter::once(SingleFontFamily::FamilyName(FamilyName { + name: Atom::from(name), + syntax: FontFamilyNameSyntax::Identifiers, + }))), + fallback: GenericFontFamily::None, + }, + is_system_font: true, + } + } + + /// Returns a generic font family. + pub fn generic(generic: GenericFontFamily) -> &'static Self { + macro_rules! generic_font_family { + ($ident:ident, $family:ident) => { + static_font_family!($ident, SingleFontFamily::Generic(GenericFontFamily::$family)) + } + } + + generic_font_family!(SERIF, Serif); + generic_font_family!(SANS_SERIF, SansSerif); + generic_font_family!(MONOSPACE, Monospace); + generic_font_family!(CURSIVE, Cursive); + generic_font_family!(FANTASY, Fantasy); + #[cfg(feature = "gecko")] + generic_font_family!(MOZ_EMOJI, MozEmoji); + generic_font_family!(SYSTEM_UI, SystemUi); + + match generic { + GenericFontFamily::None => { + debug_assert!(false, "Bogus caller!"); + &*SERIF + } + GenericFontFamily::Serif => &*SERIF, + GenericFontFamily::SansSerif => &*SANS_SERIF, + GenericFontFamily::Monospace => &*MONOSPACE, + GenericFontFamily::Cursive => &*CURSIVE, + GenericFontFamily::Fantasy => &*FANTASY, + #[cfg(feature = "gecko")] + GenericFontFamily::MozEmoji => &*MOZ_EMOJI, + GenericFontFamily::SystemUi => &*SYSTEM_UI, } } } #[cfg(feature = "gecko")] impl MallocSizeOf for FontFamily { - fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { - // SharedFontList objects are generally shared from the pointer - // stored in the specified value. So only count this if the - // SharedFontList is unshared. - let shared_font_list = self.families.shared_font_list().get(); - unsafe { bindings::Gecko_SharedFontList_SizeOfIncludingThisIfUnshared(shared_font_list) } + fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { + use malloc_size_of::MallocUnconditionalSizeOf; + // SharedFontList objects are generally measured from the pointer stored + // in the specified value. So only count this if the SharedFontList is + // unshared. + let shared_font_list = &self.families.list; + if shared_font_list.is_unique() { + shared_font_list.unconditional_size_of(ops) + } else { + 0 + } } } @@ -220,7 +292,15 @@ impl ToCss for FontFamily { W: fmt::Write, { let mut iter = self.families.iter(); - iter.next().unwrap().to_css(dest)?; + match iter.next() { + Some(f) => f.to_css(dest)?, + None => { + #[cfg(feature = "gecko")] + return self.families.fallback.to_css(dest); + #[cfg(feature = "servo")] + unreachable!(); + }, + } for family in iter { dest.write_str(", ")?; family.to_css(dest)?; @@ -229,15 +309,16 @@ impl ToCss for FontFamily { } } +/// The name of a font family of choice. #[derive( Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToComputedValue, ToResolvedValue, ToShmem, )] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -/// The name of a font family of choice +#[repr(C)] pub struct FamilyName { - /// Name of the font family + /// Name of the font family. pub name: Atom, - /// Syntax of the font family + /// Syntax of the font family. pub syntax: FontFamilyNameSyntax, } @@ -291,11 +372,13 @@ pub enum FontFamilyNameSyntax { Identifiers, } +/// A set of faces that vary in weight, width or slope. +/// cbindgen:derive-mut-casts=true #[derive( Clone, Debug, Eq, MallocSizeOf, PartialEq, ToCss, ToComputedValue, ToResolvedValue, ToShmem, )] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize, Hash))] -/// A set of faces that vary in weight, width or slope. +#[repr(u8)] pub enum SingleFontFamily { /// The name of a font family of choice. FamilyName(FamilyName), @@ -303,11 +386,22 @@ pub enum SingleFontFamily { Generic(GenericFontFamily), } +fn system_ui_enabled(_: &ParserContext) -> bool { + #[cfg(feature = "gecko")] + return static_prefs::pref!("layout.css.system-ui.enabled"); + #[cfg(feature = "servo")] + return false; +} + /// A generic font-family name. /// /// The order here is important, if you change it make sure that /// `gfxPlatformFontList.h`s ranged array and `gfxFontFamilyList`'s /// sSingleGenerics are updated as well. +/// +/// NOTE(emilio): Should be u8, but it's a u32 because of ABI issues between GCC +/// and LLVM see https://bugs.llvm.org/show_bug.cgi?id=44228 / bug 1600735 / +/// bug 1726515. #[derive( Clone, Copy, @@ -323,7 +417,7 @@ pub enum SingleFontFamily { ToShmem, )] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -#[repr(u8)] +#[repr(u32)] #[allow(missing_docs)] pub enum GenericFontFamily { /// No generic family specified, only for internal usage. @@ -337,15 +431,17 @@ pub enum GenericFontFamily { Monospace, Cursive, Fantasy, + #[parse(condition = "system_ui_enabled")] + SystemUi, /// An internal value for emoji font selection. #[css(skip)] #[cfg(feature = "gecko")] MozEmoji, } -impl SingleFontFamily { +impl Parse for SingleFontFamily { /// Parse a font-family value. - pub fn parse<'i, 't>(input: &mut Parser<'i, 't>) -> Result> { + fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { if let Ok(value) = input.try_parse(|i| i.expect_string_cloned()) { return Ok(SingleFontFamily::FamilyName(FamilyName { name: Atom::from(&*value), @@ -353,11 +449,11 @@ impl SingleFontFamily { })); } - let first_ident = input.expect_ident_cloned()?; - if let Ok(generic) = GenericFontFamily::from_ident(&first_ident) { + if let Ok(generic) = input.try_parse(|i| GenericFontFamily::parse(context, i)) { return Ok(SingleFontFamily::Generic(generic)); } + let first_ident = input.expect_ident_cloned()?; let reserved = match_ignore_ascii_case! { &first_ident, // https://drafts.csswg.org/css-fonts/#propdef-font-family // "Font family names that happen to be the same as a keyword value @@ -400,8 +496,10 @@ impl SingleFontFamily { syntax, })) } +} - #[cfg(feature = "servo")] +#[cfg(feature = "servo")] +impl SingleFontFamily { /// Get the corresponding font-family with Atom pub fn from_atom(input: Atom) -> SingleFontFamily { match input { @@ -410,6 +508,7 @@ impl SingleFontFamily { atom!("cursive") => return SingleFontFamily::Generic(GenericFontFamily::Cursive), atom!("fantasy") => return SingleFontFamily::Generic(GenericFontFamily::Fantasy), atom!("monospace") => return SingleFontFamily::Generic(GenericFontFamily::Monospace), + atom!("system-ui") => return SingleFontFamily::Generic(GenericFontFamily::SystemUi), _ => {}, } @@ -419,6 +518,7 @@ impl SingleFontFamily { "cursive" => return SingleFontFamily::Generic(GenericFontFamily::Cursive), "fantasy" => return SingleFontFamily::Generic(GenericFontFamily::Fantasy), "monospace" => return SingleFontFamily::Generic(GenericFontFamily::Monospace), + "system-ui" => return SingleFontFamily::Generic(GenericFontFamily::SystemUi), _ => {} } @@ -429,22 +529,20 @@ impl SingleFontFamily { syntax: FontFamilyNameSyntax::Quoted, }) } - - #[cfg(feature = "gecko")] - /// Get the corresponding font-family with family name - fn from_font_family_name(family: &structs::FontFamilyName) -> SingleFontFamily { - if family.mName.mRawPtr.is_null() { - debug_assert_ne!(family.mGeneric, GenericFontFamily::None); - return SingleFontFamily::Generic(family.mGeneric); - } - let name = unsafe { Atom::from_raw(family.mName.mRawPtr) }; - SingleFontFamily::FamilyName(FamilyName { - name, - syntax: family.mSyntax, - }) - } } +/// A list of font families. +#[cfg(feature = "gecko")] +#[derive(Clone, Debug, ToComputedValue, ToResolvedValue, ToShmem, PartialEq, Eq)] +#[repr(C)] +pub struct FontFamilyList { + /// The actual list of font families specified. + pub list: crate::ArcSlice, + /// A fallback font type (none, serif, or sans-serif, generally). + pub fallback: GenericFontFamily, +} + +/// A list of font families. #[cfg(feature = "servo")] #[derive( Clone, @@ -459,115 +557,52 @@ impl SingleFontFamily { ToResolvedValue, ToShmem, )] -/// A list of SingleFontFamily -pub struct FontFamilyList(Box<[SingleFontFamily]>); - -#[cfg(feature = "gecko")] -#[derive(Clone, Debug, ToComputedValue, ToResolvedValue)] -/// A list of SingleFontFamily -pub enum FontFamilyList { - /// A strong reference to a Gecko SharedFontList object. - SharedFontList( - #[compute(no_field_bound)] - #[resolve(no_field_bound)] - RefPtr, - ), - /// A font-family generic ID. - Generic(GenericFontFamily), +pub struct FontFamilyList { + /// The actual list of font families specified. + pub list: Box<[SingleFontFamily]>, } -#[cfg(feature = "gecko")] -impl ToShmem for FontFamilyList { - fn to_shmem(&self, _builder: &mut SharedMemoryBuilder) -> to_shmem::Result { - // In practice, the only SharedFontList objects we create from shared - // style sheets are ones with a single generic entry. - Ok(ManuallyDrop::new(match *self { - FontFamilyList::SharedFontList(ref r) => { - if !(r.mNames.len() == 1 && r.mNames[0].mName.mRawPtr.is_null()) { - return Err(String::from( - "ToShmem failed for FontFamilyList: cannot handle non-generic families", - )); - } - FontFamilyList::Generic(r.mNames[0].mGeneric) - }, - FontFamilyList::Generic(t) => FontFamilyList::Generic(t), - })) - } -} - -#[cfg(feature = "gecko")] -impl PartialEq for FontFamilyList { - fn eq(&self, other: &FontFamilyList) -> bool { - let self_list = self.shared_font_list(); - let other_list = other.shared_font_list(); - - if self_list.mNames.len() != other_list.mNames.len() { - return false; - } - for (a, b) in self_list.mNames.iter().zip(other_list.mNames.iter()) { - if a.mSyntax != b.mSyntax || - a.mName.mRawPtr != b.mName.mRawPtr || - a.mGeneric != b.mGeneric - { - return false; - } - } - true - } -} - -#[cfg(feature = "gecko")] -impl Eq for FontFamilyList {} - impl FontFamilyList { - /// Return FontFamilyList with a vector of SingleFontFamily - #[cfg(feature = "servo")] - pub fn new(families: Box<[SingleFontFamily]>) -> FontFamilyList { - FontFamilyList(families) + /// Return iterator of SingleFontFamily + pub fn iter(&self) -> impl Iterator { + self.list.iter() } - /// Return FontFamilyList with a vector of SingleFontFamily + /// Puts the fallback in the list if needed. #[cfg(feature = "gecko")] - pub fn new(families: Box<[SingleFontFamily]>) -> FontFamilyList { - let fontlist; - let names; - unsafe { - fontlist = bindings::Gecko_SharedFontList_Create(); - names = &mut (*fontlist).mNames; - names.ensure_capacity(families.len()); + pub fn normalize(&mut self) { + if self.fallback == GenericFontFamily::None { + return; + } + let mut new_list = self.list.iter().cloned().collect::>(); + new_list.push(SingleFontFamily::Generic(self.fallback)); + self.list = crate::ArcSlice::from_iter(new_list.into_iter()); + } + + /// If there's a generic font family on the list (which isn't cursive or + /// fantasy), then move it to the front of the list. Otherwise, prepend the + /// default generic. + #[cfg(feature = "gecko")] + pub (crate) fn prioritize_first_generic_or_prepend(&mut self, generic: GenericFontFamily) { + let index_of_first_generic = self.iter().position(|f| { + match *f { + SingleFontFamily::Generic(f) => f != GenericFontFamily::Cursive && f != GenericFontFamily::Fantasy, + _ => false, + } + }); + + if let Some(0) = index_of_first_generic { + return; // Already first + } + + let mut new_list = self.list.iter().cloned().collect::>(); + let element_to_prepend = match index_of_first_generic { + Some(i) => new_list.remove(i), + None => SingleFontFamily::Generic(generic), }; - for family in families.iter() { - match *family { - SingleFontFamily::FamilyName(ref f) => unsafe { - bindings::Gecko_nsTArray_FontFamilyName_AppendNamed( - names, - f.name.as_ptr(), - f.syntax, - ); - }, - SingleFontFamily::Generic(family) => unsafe { - bindings::Gecko_nsTArray_FontFamilyName_AppendGeneric(names, family); - }, - } - } - - FontFamilyList::SharedFontList(unsafe { RefPtr::from_addrefed(fontlist) }) - } - - /// Return iterator of SingleFontFamily - #[cfg(feature = "servo")] - pub fn iter(&self) -> slice::Iter { - self.0.iter() - } - - /// Return iterator of SingleFontFamily - #[cfg(feature = "gecko")] - pub fn iter(&self) -> FontFamilyNameIter { - FontFamilyNameIter { - names: &self.shared_font_list().mNames, - cur: 0, - } + new_list.insert(0, element_to_prepend); + self.list = crate::ArcSlice::from_iter(new_list.into_iter()); } /// Return the generic ID if it is a single generic font @@ -580,67 +615,10 @@ impl FontFamilyList { } None } - - /// Return a reference to the Gecko SharedFontList. - #[cfg(feature = "gecko")] - pub fn shared_font_list(&self) -> &RefPtr { - match *self { - FontFamilyList::SharedFontList(ref r) => r, - FontFamilyList::Generic(t) => { - unsafe { - // TODO(heycam): Should really add StaticRefPtr sugar. - let index = t as usize; - mem::transmute::< - &structs::StaticRefPtr, - &RefPtr, - >(&structs::SharedFontList_sSingleGenerics[index]) - } - }, - } - } -} - -/// Iterator of FontFamily -#[cfg(feature = "gecko")] -pub struct FontFamilyNameIter<'a> { - names: &'a structs::nsTArray, - cur: usize, -} - -#[cfg(feature = "gecko")] -impl<'a> Iterator for FontFamilyNameIter<'a> { - type Item = SingleFontFamily; - - fn next(&mut self) -> Option { - if self.cur < self.names.len() { - let item = SingleFontFamily::from_font_family_name(&self.names[self.cur]); - self.cur += 1; - Some(item) - } else { - None - } - } } /// Preserve the readability of text when font fallback occurs -#[derive( - Animate, - Clone, - ComputeSquaredDistance, - Copy, - Debug, - MallocSizeOf, - PartialEq, - ToCss, - ToResolvedValue, -)] -pub enum FontSizeAdjust { - #[animation(error)] - /// None variant - None, - /// Number variant - Number(CSSFloat), -} +pub type FontSizeAdjust = generics::GenericFontSizeAdjust; impl FontSizeAdjust { #[inline] @@ -648,40 +626,6 @@ impl FontSizeAdjust { pub fn none() -> Self { FontSizeAdjust::None } - - /// Get font-size-adjust with float number - pub fn from_gecko_adjust(gecko: f32) -> Self { - if gecko == -1.0 { - FontSizeAdjust::None - } else { - FontSizeAdjust::Number(gecko) - } - } -} - -impl ToAnimatedZero for FontSizeAdjust { - #[inline] - // FIXME(emilio): why? - fn to_animated_zero(&self) -> Result { - Err(()) - } -} - -impl ToAnimatedValue for FontSizeAdjust { - type AnimatedValue = Self; - - #[inline] - fn to_animated_value(self) -> Self { - self - } - - #[inline] - fn from_animated_value(animated: Self::AnimatedValue) -> Self { - match animated { - FontSizeAdjust::Number(number) => FontSizeAdjust::Number(number.max(0.)), - _ => animated, - } - } } /// Use VariantAlternatesList as computed type of FontVariantAlternates diff --git a/components/style/values/computed/length.rs b/components/style/values/computed/length.rs index bcba3eb9bce..3c4f7a2dbdc 100644 --- a/components/style/values/computed/length.rs +++ b/components/style/values/computed/length.rs @@ -5,6 +5,7 @@ //! `` computed values, and related ones. use super::{Context, Number, ToComputedValue}; +use crate::computed_value_flags::ComputedValueFlags; use crate::values::animated::ToAnimatedValue; use crate::values::computed::NonNegativeNumber; use crate::values::generics::length as generics; @@ -36,6 +37,7 @@ impl ToComputedValue for specified::NoCalcLength { length.to_computed_value(context, FontBaseSize::CurrentStyle) }, specified::NoCalcLength::ViewportPercentage(length) => { + context.builder.add_flags(ComputedValueFlags::USES_VIEWPORT_UNITS); length.to_computed_value(context.viewport_size_for_viewport_unit_resolution()) }, specified::NoCalcLength::ServoCharacterWidth(length) => { @@ -188,7 +190,8 @@ impl Size { GenericSize::MinContent | GenericSize::MaxContent | GenericSize::MozFitContent | - GenericSize::MozAvailable => false + GenericSize::MozAvailable | + GenericSize::FitContentFunction(_) => false } } } diff --git a/components/style/values/computed/length_percentage.rs b/components/style/values/computed/length_percentage.rs index 098b1abfafc..cea2466ce6f 100644 --- a/components/style/values/computed/length_percentage.rs +++ b/components/style/values/computed/length_percentage.rs @@ -607,22 +607,11 @@ impl CalcLengthPercentageLeaf { impl PartialOrd for CalcLengthPercentageLeaf { fn partial_cmp(&self, other: &Self) -> Option { use self::CalcLengthPercentageLeaf::*; - - if std::mem::discriminant(self) != std::mem::discriminant(other) { - return None; - } - + // NOTE: Percentages can't be compared reasonably here because the + // percentage basis might be negative, see bug 1709018. match (self, other) { (&Length(ref one), &Length(ref other)) => one.partial_cmp(other), - (&Percentage(ref one), &Percentage(ref other)) => one.partial_cmp(other), - _ => { - match *self { - Length(..) | Percentage(..) => {}, - } - unsafe { - debug_unreachable!("Forgot a branch?"); - } - }, + _ => None, } } } diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 826bb2a2448..c87142ac1b5 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -50,7 +50,7 @@ pub use self::box_::{Display, Overflow, OverflowAnchor, TransitionProperty}; pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize}; pub use self::box_::{ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStrictness, ScrollSnapType}; pub use self::box_::{TouchAction, VerticalAlign, WillChange}; -pub use self::color::{Color, ColorOrAuto, ColorPropertyValue}; +pub use self::color::{Color, ColorOrAuto, ColorPropertyValue, ColorScheme}; pub use self::column::ColumnCount; pub use self::counters::{Content, ContentItem, CounterIncrement, CounterSetOrReset}; pub use self::easing::TimingFunction; @@ -82,7 +82,7 @@ pub use self::position::{ pub use self::ratio::Ratio; pub use self::rect::NonNegativeLengthOrNumberRect; pub use self::resolution::Resolution; -pub use self::svg::MozContextProperties; +pub use self::svg::{DProperty, MozContextProperties}; pub use self::svg::{SVGLength, SVGOpacity, SVGPaint, SVGPaintKind}; pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth}; pub use self::text::TextUnderlinePosition; diff --git a/components/style/values/computed/page.rs b/components/style/values/computed/page.rs index d8ef03c1f6d..27b16d0af15 100644 --- a/components/style/values/computed/page.rs +++ b/components/style/values/computed/page.rs @@ -5,10 +5,69 @@ //! Computed @page at-rule properties use crate::values::computed::length::NonNegativeLength; +use crate::values::computed::{Context, ToComputedValue}; use crate::values::generics; use crate::values::generics::size::Size2D; +use crate::values::specified::page as specified; +pub use generics::page::GenericPageSize; pub use generics::page::Orientation; pub use generics::page::PaperSize; + /// Computed value of the @page size descriptor -pub type PageSize = generics::page::GenericPageSize>; +/// +/// The spec says that the computed value should be the same as the specified +/// value but with all absolute units, but it's not currently possibly observe +/// the computed value of page-size. +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, ToCss, ToResolvedValue, ToShmem)] +#[repr(C, u8)] +pub enum PageSize { + /// Specified size, paper size, or paper size and orientation. + Size(Size2D), + /// `landscape` or `portrait` value, no specified size. + Orientation(Orientation), + /// `auto` value + Auto, +} + +impl ToComputedValue for specified::PageSize { + type ComputedValue = PageSize; + + fn to_computed_value(&self, ctx: &Context) -> Self::ComputedValue { + match &*self { + Self::Size(s) => PageSize::Size(s.to_computed_value(ctx)), + Self::PaperSize(p, Orientation::Landscape) => PageSize::Size(Size2D { + width: p.long_edge().to_computed_value(ctx), + height: p.short_edge().to_computed_value(ctx), + }), + Self::PaperSize(p, Orientation::Portrait) => PageSize::Size(Size2D { + width: p.short_edge().to_computed_value(ctx), + height: p.long_edge().to_computed_value(ctx), + }), + Self::Orientation(o) => PageSize::Orientation(*o), + Self::Auto => PageSize::Auto, + } + } + + fn from_computed_value(computed: &Self::ComputedValue) -> Self { + match *computed { + PageSize::Size(s) => Self::Size(ToComputedValue::from_computed_value(&s)), + PageSize::Orientation(o) => Self::Orientation(o), + PageSize::Auto => Self::Auto, + } + } +} + +impl PageSize { + /// `auto` value. + #[inline] + pub fn auto() -> Self { + PageSize::Auto + } + + /// Whether this is the `auto` value. + #[inline] + pub fn is_auto(&self) -> bool { + matches!(*self, PageSize::Auto) + } +} diff --git a/components/style/values/computed/svg.rs b/components/style/values/computed/svg.rs index a348d071ab9..344f1d83518 100644 --- a/components/style/values/computed/svg.rs +++ b/components/style/values/computed/svg.rs @@ -11,7 +11,7 @@ use crate::values::generics::svg as generic; use crate::values::RGBA; use crate::Zero; -pub use crate::values::specified::{MozContextProperties, SVGPaintOrder}; +pub use crate::values::specified::{DProperty, MozContextProperties, SVGPaintOrder}; /// Computed SVG Paint value pub type SVGPaint = generic::GenericSVGPaint; diff --git a/components/style/values/computed/text.rs b/components/style/values/computed/text.rs index b42dfb488a3..629626b06b3 100644 --- a/components/style/values/computed/text.rs +++ b/components/style/values/computed/text.rs @@ -110,6 +110,14 @@ impl ToComputedValue for specified::WordSpacing { /// A computed value for the `line-height` property. pub type LineHeight = GenericLineHeight; +impl WordSpacing { + /// Return the `normal` computed value, which is just zero. + #[inline] + pub fn normal() -> Self { + LengthPercentage::zero() + } +} + #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToResolvedValue)] #[repr(C)] /// text-overflow. diff --git a/components/style/values/computed/transform.rs b/components/style/values/computed/transform.rs index 46c471b7e2e..5eafa0cd627 100644 --- a/components/style/values/computed/transform.rs +++ b/components/style/values/computed/transform.rs @@ -24,6 +24,9 @@ pub type Transform = generic::GenericTransform; pub type TransformOrigin = generic::GenericTransformOrigin; +/// The computed value of the `perspective()` transform function. +pub type PerspectiveFunction = generic::PerspectiveFunction; + /// A vector to represent the direction vector (rotate axis) for Rotate3D. pub type DirectionVector = Vector3D; @@ -516,8 +519,8 @@ impl ToAnimatedZero for TransformOperation { generic::TransformOperation::Rotate(_) => { Ok(generic::TransformOperation::Rotate(Angle::zero())) }, - generic::TransformOperation::Perspective(ref l) => Ok( - generic::TransformOperation::Perspective(l.to_animated_zero()?), + generic::TransformOperation::Perspective(_) => Ok( + generic::TransformOperation::Perspective(generic::PerspectiveFunction::None) ), generic::TransformOperation::AccumulateMatrix { .. } | generic::TransformOperation::InterpolateMatrix { .. } => { diff --git a/components/style/values/generics/border.rs b/components/style/values/generics/border.rs index 37d91464b01..e4e78e31222 100644 --- a/components/style/values/generics/border.rs +++ b/components/style/values/generics/border.rs @@ -147,7 +147,7 @@ impl BorderSpacing { } } -/// A generic value for `border-radius`, `outline-radius` and `inset()`. +/// A generic value for `border-radius` and `inset()`. /// /// #[derive( diff --git a/components/style/values/generics/font.rs b/components/style/values/generics/font.rs index 226613d4545..a88df7be173 100644 --- a/components/style/values/generics/font.rs +++ b/components/style/values/generics/font.rs @@ -202,3 +202,60 @@ pub enum FontStyle { #[value_info(starts_with_keyword)] Oblique(Angle), } + +/// A generic value for the `font-size-adjust` property. +/// +/// https://www.w3.org/TR/css-fonts-4/#font-size-adjust-prop +/// https://github.com/w3c/csswg-drafts/issues/6160 +/// https://github.com/w3c/csswg-drafts/issues/6288 +#[allow(missing_docs)] +#[repr(u8)] +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Copy, + Debug, + Hash, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToAnimatedValue, + ToAnimatedZero, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] +pub enum GenericFontSizeAdjust { + #[animation(error)] + None, + // 'ex-height' is the implied basis, so the keyword can be omitted + ExHeight(Number), + #[value_info(starts_with_keyword)] + CapHeight(Number), + #[value_info(starts_with_keyword)] + ChWidth(Number), + #[value_info(starts_with_keyword)] + IcWidth(Number), + #[value_info(starts_with_keyword)] + IcHeight(Number), +} + +impl ToCss for GenericFontSizeAdjust { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + let (prefix, value) = match self { + Self::None => return dest.write_str("none"), + Self::ExHeight(v) => ("", v), + Self::CapHeight(v) => ("cap-height ", v), + Self::ChWidth(v) => ("ch-width ", v), + Self::IcWidth(v) => ("ic-width ", v), + Self::IcHeight(v) => ("ic-height ", v), + }; + + dest.write_str(prefix)?; + value.to_css(dest) + } +} diff --git a/components/style/values/generics/length.rs b/components/style/values/generics/length.rs index f3e025f80b7..dfb3504e3cf 100644 --- a/components/style/values/generics/length.rs +++ b/components/style/values/generics/length.rs @@ -161,6 +161,10 @@ pub enum GenericSize { #[cfg(feature = "gecko")] #[animation(error)] MozAvailable, + #[cfg(feature = "gecko")] + #[animation(error)] + #[css(function = "fit-content")] + FitContentFunction(LengthPercent) } pub use self::GenericSize as Size; @@ -215,6 +219,10 @@ pub enum GenericMaxSize { #[cfg(feature = "gecko")] #[animation(error)] MozAvailable, + #[cfg(feature = "gecko")] + #[animation(error)] + #[css(function = "fit-content")] + FitContentFunction(LengthPercent), } pub use self::GenericMaxSize as MaxSize; @@ -227,7 +235,7 @@ impl MaxSize { } } -/// A generic `` | `` value for the `-moz-tab-size` property. +/// A generic `` | `` value for the `tab-size` property. #[derive( Animate, Clone, diff --git a/components/style/values/generics/mod.rs b/components/style/values/generics/mod.rs index 39c898efed7..c0cdf04b71d 100644 --- a/components/style/values/generics/mod.rs +++ b/components/style/values/generics/mod.rs @@ -95,6 +95,21 @@ impl CounterStyle { pub fn decimal() -> Self { CounterStyle::Name(CustomIdent(atom!("decimal"))) } + + /// Is this a bullet? (i.e. `list-style-type: disc|circle|square|disclosure-closed|disclosure-open`) + #[inline] + pub fn is_bullet(&self) -> bool { + match self { + CounterStyle::Name(CustomIdent(ref name)) => { + name == &atom!("disc") || + name == &atom!("circle") || + name == &atom!("square") || + name == &atom!("disclosure-closed") || + name == &atom!("disclosure-open") + } + _ => false, + } + } } impl Parse for CounterStyle { diff --git a/components/style/values/generics/page.rs b/components/style/values/generics/page.rs index 35ffee7b97e..1de1a8e912c 100644 --- a/components/style/values/generics/page.rs +++ b/components/style/values/generics/page.rs @@ -4,22 +4,14 @@ //! @page at-rule properties +use crate::values::generics::NonNegative; +use crate::values::specified::length::AbsoluteLength; + /// Page size names. /// /// https://drafts.csswg.org/css-page-3/#typedef-page-size-page-size #[derive( - Clone, - Copy, - Debug, - Eq, - MallocSizeOf, - Parse, - PartialEq, - SpecifiedValueInfo, - ToComputedValue, - ToCss, - ToResolvedValue, - ToShmem, + Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, )] #[repr(u8)] pub enum PaperSize { @@ -45,6 +37,39 @@ pub enum PaperSize { Ledger, } +impl PaperSize { + /// Gets the long edge length of the paper size + pub fn long_edge(&self) -> NonNegative { + NonNegative(match *self { + PaperSize::A5 => AbsoluteLength::Mm(210.0), + PaperSize::A4 => AbsoluteLength::Mm(297.0), + PaperSize::A3 => AbsoluteLength::Mm(420.0), + PaperSize::B5 => AbsoluteLength::Mm(250.0), + PaperSize::B4 => AbsoluteLength::Mm(353.0), + PaperSize::JisB5 => AbsoluteLength::Mm(257.0), + PaperSize::JisB4 => AbsoluteLength::Mm(364.0), + PaperSize::Letter => AbsoluteLength::In(11.0), + PaperSize::Legal => AbsoluteLength::In(14.0), + PaperSize::Ledger => AbsoluteLength::In(17.0), + }) + } + /// Gets the short edge length of the paper size + pub fn short_edge(&self) -> NonNegative { + NonNegative(match *self { + PaperSize::A5 => AbsoluteLength::Mm(148.0), + PaperSize::A4 => AbsoluteLength::Mm(210.0), + PaperSize::A3 => AbsoluteLength::Mm(297.0), + PaperSize::B5 => AbsoluteLength::Mm(176.0), + PaperSize::B4 => AbsoluteLength::Mm(250.0), + PaperSize::JisB5 => AbsoluteLength::Mm(182.0), + PaperSize::JisB4 => AbsoluteLength::Mm(257.0), + PaperSize::Letter => AbsoluteLength::In(8.5), + PaperSize::Legal => AbsoluteLength::In(8.5), + PaperSize::Ledger => AbsoluteLength::In(11.0), + }) + } +} + /// Paper orientation /// /// https://drafts.csswg.org/css-page-3/#page-size-prop @@ -57,7 +82,6 @@ pub enum PaperSize { Parse, PartialEq, SpecifiedValueInfo, - ToComputedValue, ToCss, ToResolvedValue, ToShmem, @@ -70,33 +94,25 @@ pub enum Orientation { Landscape, } +#[inline] +fn is_portrait(orientation: &Orientation) -> bool { + *orientation == Orientation::Portrait +} + /// Page size property /// /// https://drafts.csswg.org/css-page-3/#page-size-prop -#[derive( - Clone, - Copy, - Debug, - MallocSizeOf, - PartialEq, - SpecifiedValueInfo, - ToComputedValue, - ToCss, - ToResolvedValue, - ToShmem, -)] +#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)] #[repr(C, u8)] pub enum GenericPageSize { - /// Page dimensions. - Size(S), - /// Paper size with no orientation. - PaperSize(PaperSize), - /// An orientation with no size. - Orientation(Orientation), - /// Paper size by name, with an orientation. - PaperSizeAndOrientation(PaperSize, Orientation), /// `auto` value. Auto, + /// Page dimensions. + Size(S), + /// An orientation with no size. + Orientation(Orientation), + /// Paper size by name + PaperSize(PaperSize, #[css(skip_if = "is_portrait")] Orientation), } pub use self::GenericPageSize as PageSize; diff --git a/components/style/values/generics/transform.rs b/components/style/values/generics/transform.rs index 4d1df3d58ea..92987f89fe4 100644 --- a/components/style/values/generics/transform.rs +++ b/components/style/values/generics/transform.rs @@ -141,6 +141,41 @@ fn is_same(x: &N, y: &N) -> bool { x == y } +/// A value for the `perspective()` transform function, which is either a +/// non-negative `` or `none`. +#[derive( + Clone, + Debug, + Deserialize, + MallocSizeOf, + PartialEq, + Serialize, + SpecifiedValueInfo, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(C, u8)] +pub enum GenericPerspectiveFunction { + /// `none` + None, + /// A ``. + Length(L), +} + +impl GenericPerspectiveFunction { + /// Returns `f32::INFINITY` or the result of a function on the length value. + pub fn infinity_or(&self, f: impl FnOnce(&L) -> f32) -> f32 { + match *self { + Self::None => std::f32::INFINITY, + Self::Length(ref l) => f(l), + } + } +} + +pub use self::GenericPerspectiveFunction as PerspectiveFunction; + #[derive( Clone, Debug, @@ -240,7 +275,7 @@ where /// /// The value must be greater than or equal to zero. #[css(function)] - Perspective(Length), + Perspective(GenericPerspectiveFunction), /// A intermediate type for interpolation of mismatched transform lists. #[allow(missing_docs)] #[css(comma, function = "interpolatematrix")] @@ -443,36 +478,38 @@ where use self::TransformOperation::*; use std::f64; - const TWO_PI: f64 = 2.0f64 * f64::consts::PI; let reference_width = reference_box.map(|v| v.size.width); let reference_height = reference_box.map(|v| v.size.height); let matrix = match *self { Rotate3D(ax, ay, az, theta) => { - let theta = TWO_PI - theta.radians64(); + let theta = theta.radians64(); let (ax, ay, az, theta) = get_normalized_vector_and_angle(ax.into(), ay.into(), az.into(), theta); Transform3D::rotation( ax as f64, ay as f64, az as f64, - -euclid::Angle::radians(theta), + euclid::Angle::radians(theta), ) }, RotateX(theta) => { - let theta = euclid::Angle::radians(TWO_PI - theta.radians64()); - Transform3D::rotation(1., 0., 0., -theta) + let theta = euclid::Angle::radians(theta.radians64()); + Transform3D::rotation(1., 0., 0., theta) }, RotateY(theta) => { - let theta = euclid::Angle::radians(TWO_PI - theta.radians64()); - Transform3D::rotation(0., 1., 0., -theta) + let theta = euclid::Angle::radians(theta.radians64()); + Transform3D::rotation(0., 1., 0., theta) }, RotateZ(theta) | Rotate(theta) => { - let theta = euclid::Angle::radians(TWO_PI - theta.radians64()); - Transform3D::rotation(0., 0., 1., -theta) + let theta = euclid::Angle::radians(theta.radians64()); + Transform3D::rotation(0., 0., 1., theta) }, - Perspective(ref d) => { - let m = create_perspective_matrix(d.to_pixel_length(None)?); - m.cast() + Perspective(ref p) => { + let px = match p { + PerspectiveFunction::None => std::f32::INFINITY, + PerspectiveFunction::Length(ref p) => p.to_pixel_length(None)?, + }; + create_perspective_matrix(px).cast() }, Scale3D(sx, sy, sz) => Transform3D::scale(sx.into(), sy.into(), sz.into()), Scale(sx, sy) => Transform3D::scale(sx.into(), sy.into(), 1.), @@ -583,17 +620,10 @@ impl Transform { /// Return the transform matrix from a perspective length. #[inline] pub fn create_perspective_matrix(d: CSSFloat) -> Transform3D { - // TODO(gw): The transforms spec says that perspective length must - // be positive. However, there is some confusion between the spec - // and browser implementations as to handling the case of 0 for the - // perspective value. Until the spec bug is resolved, at least ensure - // that a provided perspective value of <= 0.0 doesn't cause panics - // and behaves as it does in other browsers. - // See https://lists.w3.org/Archives/Public/www-style/2016Jan/0020.html for more details. - if d <= 0.0 { - Transform3D::identity() + if d.is_finite() { + Transform3D::perspective(d.max(1.)) } else { - Transform3D::perspective(d) + Transform3D::identity() } } diff --git a/components/style/values/resolved/counters.rs b/components/style/values/resolved/counters.rs index bf6490f7401..cdaebcdea3e 100644 --- a/components/style/values/resolved/counters.rs +++ b/components/style/values/resolved/counters.rs @@ -7,6 +7,14 @@ use super::{Context, ToResolvedValue}; use crate::values::computed; +#[inline] +fn allow_element_content_none() -> bool { + #[cfg(feature = "gecko")] + return static_prefs::pref!("layout.css.element-content-none.enabled"); + #[cfg(feature = "servo")] + return false; +} + /// https://drafts.csswg.org/css-content/#content-property /// /// We implement this at resolved value time because otherwise it causes us to @@ -23,19 +31,19 @@ impl ToResolvedValue for computed::Content { #[inline] fn to_resolved_value(self, context: &Context) -> Self { - let is_before_or_after = context - .style - .pseudo() - .map_or(false, |p| p.is_before_or_after()); - + let (is_pseudo, is_before_or_after, is_marker) = + match context.style.pseudo() { + Some(ref pseudo) => (true, pseudo.is_before_or_after(), pseudo.is_marker()), + None => (false, false, false) + }; match self { Self::Normal if is_before_or_after => Self::None, - // For now, make `content: none` compute to `normal` on other - // elements, as we don't respect it. - // - // FIXME(emilio, bug 1605473): for marker this should be preserved - // and respected, probably. - Self::None if !is_before_or_after => Self::Normal, + // For now, make `content: none` compute to `normal` for pseudos + // other than ::before, ::after and ::marker, as we don't respect it. + // https://github.com/w3c/csswg-drafts/issues/6124 + // Ditto for non-pseudo elements if the pref is disabled. + Self::None if (is_pseudo && !is_before_or_after && !is_marker) || + (!is_pseudo && !allow_element_content_none()) => Self::Normal, other => other, } } diff --git a/components/style/values/specified/basic_shape.rs b/components/style/values/specified/basic_shape.rs index 37aaae2ec2a..3c571ff8e84 100644 --- a/components/style/values/specified/basic_shape.rs +++ b/components/style/values/specified/basic_shape.rs @@ -55,15 +55,6 @@ pub type ShapeRadius = generic::ShapeRadius; /// The specified value of `Polygon` pub type Polygon = generic::GenericPolygon; -#[cfg(feature = "gecko")] -fn is_clip_path_path_enabled(context: &ParserContext) -> bool { - context.chrome_rules_enabled() || static_prefs::pref!("layout.css.clip-path-path.enabled") -} -#[cfg(feature = "servo")] -fn is_clip_path_path_enabled(_: &ParserContext) -> bool { - false -} - /// A helper for both clip-path and shape-outside parsing of shapes. fn parse_shape_or_box<'i, 't, R, ReferenceBox>( context: &ParserContext, @@ -116,10 +107,8 @@ impl Parse for ClipPath { return Ok(ClipPath::None); } - if is_clip_path_path_enabled(context) { - if let Ok(p) = input.try_parse(|i| Path::parse(context, i)) { - return Ok(ClipPath::Path(p)); - } + if let Ok(p) = input.try_parse(|i| Path::parse(context, i)) { + return Ok(ClipPath::Path(p)); } if let Ok(url) = input.try_parse(|i| SpecifiedUrl::parse(context, i)) { diff --git a/components/style/values/specified/box.rs b/components/style/values/specified/box.rs index 32f4f9bacae..76dd263e831 100644 --- a/components/style/values/specified/box.rs +++ b/components/style/values/specified/box.rs @@ -1838,8 +1838,6 @@ pub enum Appearance { #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] MozWindowTitlebarMaximized, - #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] - MozGtkInfoBar, #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] MozMacActiveSourceListSelection, #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] @@ -1896,28 +1894,20 @@ pub enum BreakBetween { } impl BreakBetween { - /// Parse a legacy break-between value for `page-break-*`. + /// Parse a legacy break-between value for `page-break-{before,after}`. /// /// See https://drafts.csswg.org/css-break/#page-break-properties. + #[cfg(feature = "gecko")] #[inline] - pub fn parse_legacy<'i>(input: &mut Parser<'i, '_>) -> Result> { - let location = input.current_source_location(); - let ident = input.expect_ident()?; - let break_value = match BreakBetween::from_ident(ident) { - Ok(v) => v, - Err(()) => { - return Err(location - .new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))); - }, - }; + pub(crate) fn parse_legacy<'i>(_: &ParserContext, input: &mut Parser<'i, '_>) -> Result> { + let break_value = BreakBetween::parse(input)?; match break_value { BreakBetween::Always => Ok(BreakBetween::Page), BreakBetween::Auto | BreakBetween::Avoid | BreakBetween::Left | BreakBetween::Right => { Ok(break_value) }, BreakBetween::Page => { - Err(location - .new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident.clone()))) + Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) }, } } @@ -1925,7 +1915,8 @@ impl BreakBetween { /// Serialize a legacy break-between value for `page-break-*`. /// /// See https://drafts.csswg.org/css-break/#page-break-properties. - pub fn to_css_legacy(&self, dest: &mut CssWriter) -> fmt::Result + #[cfg(feature = "gecko")] + pub(crate) fn to_css_legacy(&self, dest: &mut CssWriter) -> fmt::Result where W: Write, { @@ -1962,6 +1953,39 @@ impl BreakBetween { pub enum BreakWithin { Auto, Avoid, + AvoidPage, + AvoidColumn, +} + +impl BreakWithin { + /// Parse a legacy break-between value for `page-break-inside`. + /// + /// See https://drafts.csswg.org/css-break/#page-break-properties. + #[cfg(feature = "gecko")] + #[inline] + pub(crate) fn parse_legacy<'i>(_: &ParserContext, input: &mut Parser<'i, '_>) -> Result> { + let break_value = BreakWithin::parse(input)?; + match break_value { + BreakWithin::Auto | BreakWithin::Avoid => Ok(break_value), + BreakWithin::AvoidPage | BreakWithin::AvoidColumn => { + Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) + }, + } + } + + /// Serialize a legacy break-between value for `page-break-inside`. + /// + /// See https://drafts.csswg.org/css-break/#page-break-properties. + #[cfg(feature = "gecko")] + pub(crate) fn to_css_legacy(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + match *self { + BreakWithin::Auto | BreakWithin::Avoid => self.to_css(dest), + BreakWithin::AvoidPage | BreakWithin::AvoidColumn => Ok(()), + } + } } /// The value for the `overflow-x` / `overflow-y` properties. diff --git a/components/style/values/specified/calc.rs b/components/style/values/specified/calc.rs index d5733a6ce4a..a22d6fd5f18 100644 --- a/components/style/values/specified/calc.rs +++ b/components/style/values/specified/calc.rs @@ -107,8 +107,12 @@ impl PartialOrd for Leaf { } match (self, other) { + // NOTE: Percentages can't be compared reasonably here because the + // percentage basis might be negative, see bug 1709018. + // Conveniently, we only use this for (for raw + // percentages, we go through resolve()). + (&Percentage(..), &Percentage(..)) => None, (&Length(ref one), &Length(ref other)) => one.partial_cmp(other), - (&Percentage(ref one), &Percentage(ref other)) => one.partial_cmp(other), (&Angle(ref one), &Angle(ref other)) => one.degrees().partial_cmp(&other.degrees()), (&Time(ref one), &Time(ref other)) => one.seconds().partial_cmp(&other.seconds()), (&Number(ref one), &Number(ref other)) => one.partial_cmp(other), diff --git a/components/style/values/specified/color.rs b/components/style/values/specified/color.rs index 773a93366b8..f84883702c7 100644 --- a/components/style/values/specified/color.rs +++ b/components/style/values/specified/color.rs @@ -9,9 +9,10 @@ use super::AllowQuirks; use crate::gecko_bindings::structs::nscolor; use crate::parser::{Parse, ParserContext}; use crate::values::computed::{Color as ComputedColor, Context, ToComputedValue}; -use crate::values::generics::color::{GenericColorOrAuto, GenericCaretColor}; +use crate::values::generics::color::{GenericCaretColor, GenericColorOrAuto}; use crate::values::specified::calc::CalcNode; use crate::values::specified::Percentage; +use crate::values::CustomIdent; use cssparser::{AngleOrNumber, Color as CSSParserColor, Parser, Token, RGBA}; use cssparser::{BasicParseErrorKind, NumberOrPercentage, ParseErrorKind}; use itoa; @@ -20,29 +21,67 @@ use std::io::Write as IoWrite; use style_traits::{CssType, CssWriter, KeywordsCollectFn, ParseError, StyleParseErrorKind}; use style_traits::{SpecifiedValueInfo, ToCss, ValueParseErrorKind}; +/// A color space as defined in [1]. +/// +/// [1]: https://drafts.csswg.org/css-color-5/#typedef-colorspace +#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)] +pub enum ColorSpaceKind { + /// The sRGB color space. + Srgb, + /// The CIEXYZ color space. + Xyz, + /// The CIELAB color space. + Lab, + /// The CIELAB color space, expressed in cylindrical coordinates. + Lch, +} + +/// A hue adjuster as defined in [1]. +/// +/// [1]: https://drafts.csswg.org/css-color-5/#typedef-hue-adjuster +#[derive(Clone, Copy, Debug, Eq, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)] +pub enum HueAdjuster { + /// The "shorter" angle adjustment. + Shorter, + /// The "longer" angle adjustment. + Longer, + /// The "increasing" angle adjustment. + Increasing, + /// The "decreasing" angle adjustment. + Decreasing, + /// The "specified" angle adjustment. + Specified, +} + /// A restricted version of the css `color-mix()` function, which only supports -/// percentages and sRGB color-space interpolation. +/// percentages. /// /// https://drafts.csswg.org/css-color-5/#color-mix #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] #[allow(missing_docs)] pub struct ColorMix { + pub color_space: ColorSpaceKind, pub left: Color, pub left_percentage: Percentage, pub right: Color, pub right_percentage: Percentage, + pub hue_adjuster: HueAdjuster, } -#[cfg(feature = "gecko")] #[inline] fn allow_color_mix() -> bool { - static_prefs::pref!("layout.css.color-mix.enabled") + #[cfg(feature = "gecko")] + return static_prefs::pref!("layout.css.color-mix.enabled"); + #[cfg(feature = "servo")] + return false; } -#[cfg(feature = "servo")] #[inline] -fn allow_color_mix() -> bool { - false +fn allow_color_mix_color_spaces() -> bool { + #[cfg(feature = "gecko")] + return static_prefs::pref!("layout.css.color-mix.color-spaces.enabled"); + #[cfg(feature = "servo")] + return false; } // NOTE(emilio): Syntax is still a bit in-flux, since [1] doesn't seem @@ -61,6 +100,9 @@ impl Parse for ColorMix { return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); } + let color_spaces_enabled = context.chrome_rules_enabled() || + allow_color_mix_color_spaces(); + input.expect_function_matching("color-mix")?; // NOTE(emilio): This implements the syntax described here for now, @@ -69,12 +111,18 @@ impl Parse for ColorMix { // https://github.com/w3c/csswg-drafts/issues/6066#issuecomment-789836765 input.parse_nested_block(|input| { input.expect_ident_matching("in")?; - // TODO: support multiple interpolation spaces. - input.expect_ident_matching("srgb")?; + let color_space = if color_spaces_enabled { + ColorSpaceKind::parse(input)? + } else { + input.expect_ident_matching("srgb")?; + ColorSpaceKind::Srgb + }; input.expect_comma()?; let left = Color::parse(context, input)?; - let left_percentage = input.try_parse(|input| Percentage::parse(context, input)).ok(); + let left_percentage = input + .try_parse(|input| Percentage::parse(context, input)) + .ok(); input.expect_comma()?; @@ -87,11 +135,18 @@ impl Parse for ColorMix { let left_percentage = left_percentage.unwrap_or_else(|| Percentage::new(1.0 - right_percentage.get())); + + let hue_adjuster = input + .try_parse(|input| HueAdjuster::parse(input)) + .unwrap_or(HueAdjuster::Shorter); + Ok(ColorMix { + color_space, left, left_percentage, right, right_percentage, + hue_adjuster, }) }) } @@ -115,7 +170,9 @@ impl ToCss for ColorMix { (1.0 - percent.get() - other.get()).abs() <= f32::EPSILON } - dest.write_str("color-mix(in srgb, ")?; + dest.write_str("color-mix(in ")?; + self.color_space.to_css(dest)?; + dest.write_str(", ")?; self.left.to_css(dest)?; if !can_omit(&self.left_percentage, &self.right_percentage, true) { dest.write_str(" ")?; @@ -127,10 +184,30 @@ impl ToCss for ColorMix { dest.write_str(" ")?; self.right_percentage.to_css(dest)?; } + + if self.hue_adjuster != HueAdjuster::Shorter { + dest.write_str(" ")?; + self.hue_adjuster.to_css(dest)?; + } + dest.write_str(")") } } +/// The color scheme for a specific system color. +#[cfg(feature = "gecko")] +#[derive(Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, ToCss, ToShmem)] +#[repr(u8)] +pub enum SystemColorScheme { + /// The default color-scheme for the document. + #[css(skip)] + Default, + /// A light color scheme. + Light, + /// A dark color scheme. + Dark, +} + /// Specified color value #[derive(Clone, Debug, MallocSizeOf, PartialEq, ToShmem)] pub enum Color { @@ -145,9 +222,10 @@ pub enum Color { }, /// A complex color value from computed value Complex(ComputedColor), - /// A system color + /// Either a system color, or a `-moz-system-color(, light|dark)` + /// function which allows chrome code to choose between color schemes. #[cfg(feature = "gecko")] - System(SystemColor), + System(SystemColor, SystemColorScheme), /// A color mix. ColorMix(Box), /// Quirksmode-only rule for inheriting color from the body @@ -181,13 +259,13 @@ pub enum SystemColor { TextBackground, #[css(skip)] TextForeground, - #[css(skip)] + #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] TextSelectBackground, - #[css(skip)] + #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] TextSelectForeground, - #[css(skip)] + #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] TextSelectBackgroundDisabled, - #[css(skip)] + #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] TextSelectBackgroundAttention, #[css(skip)] TextHighlightBackground, @@ -358,6 +436,10 @@ pub enum SystemColor { /// colors. MozNativehyperlinktext, + /// As above, but visited link color. + #[css(skip)] + MozNativevisitedhyperlinktext, + #[parse(aliases = "-moz-hyperlinktext")] Linktext, #[parse(aliases = "-moz-activehyperlinktext")] @@ -369,14 +451,20 @@ pub enum SystemColor { MozComboboxtext, MozCombobox, - MozGtkInfoBarText, - /// Color of tree column headers #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] MozColheadertext, #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] MozColheaderhovertext, + /// Color of text in the (active) titlebar. + #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] + MozGtkTitlebarText, + + /// Color of text in the (inactive) titlebar. + #[parse(condition = "ParserContext::in_ua_or_chrome_sheet")] + MozGtkTitlebarInactiveText, + #[css(skip)] End, // Just for array-indexing purposes. } @@ -384,20 +472,29 @@ pub enum SystemColor { #[cfg(feature = "gecko")] impl SystemColor { #[inline] - fn compute(&self, cx: &Context) -> ComputedColor { + fn compute(&self, cx: &Context, scheme: SystemColorScheme) -> ComputedColor { use crate::gecko_bindings::bindings; - let prefs = cx.device().pref_sheet_prefs(); + let colors = &cx.device().pref_sheet_prefs().mColors; + let style_color_scheme = cx.style().get_inherited_ui().clone_color_scheme(); + // TODO: At least Canvas / CanvasText should be color-scheme aware + // (probably the link colors too). convert_nscolor_to_computedcolor(match *self { - SystemColor::Canvastext => prefs.mDefaultColor, - SystemColor::Canvas => prefs.mDefaultBackgroundColor, - SystemColor::Linktext => prefs.mLinkColor, - SystemColor::Activetext => prefs.mActiveLinkColor, - SystemColor::Visitedtext => prefs.mVisitedLinkColor, + SystemColor::Canvastext => colors.mDefault, + SystemColor::Canvas => colors.mDefaultBackground, + SystemColor::Linktext => colors.mLink, + SystemColor::Activetext => colors.mActiveLink, + SystemColor::Visitedtext => colors.mVisitedLink, - _ => unsafe { - bindings::Gecko_GetLookAndFeelSystemColor(*self as i32, cx.device().document()) + _ => { + let color = unsafe { + bindings::Gecko_GetLookAndFeelSystemColor(*self as i32, cx.device().document(), scheme, &style_color_scheme) + }; + if color == bindings::NS_SAME_AS_FOREGROUND_COLOR { + return ComputedColor::currentcolor(); + } + color }, }) } @@ -473,6 +570,21 @@ impl<'a, 'b: 'a, 'i: 'a> ::cssparser::ColorComponentParser<'i> for ColorComponen } } +#[cfg(feature = "gecko")] +fn parse_moz_system_color<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, +) -> Result<(SystemColor, SystemColorScheme), ParseError<'i>> { + debug_assert!(context.chrome_rules_enabled()); + input.expect_function_matching("-moz-system-color")?; + input.parse_nested_block(|input| { + let color = SystemColor::parse(context, input)?; + input.expect_comma()?; + let scheme = SystemColorScheme::parse(input)?; + Ok((color, scheme)) + }) +} + impl Parse for Color { fn parse<'i, 't>( context: &ParserContext, @@ -498,7 +610,15 @@ impl Parse for Color { #[cfg(feature = "gecko")] { if let Ok(system) = input.try_parse(|i| SystemColor::parse(context, i)) { - return Ok(Color::System(system)); + return Ok(Color::System(system, SystemColorScheme::Default)); + } + + if context.chrome_rules_enabled() { + if let Ok((color, scheme)) = + input.try_parse(|i| parse_moz_system_color(context, i)) + { + return Ok(Color::System(color, scheme)); + } } } @@ -537,7 +657,17 @@ impl ToCss for Color { Color::Complex(_) => Ok(()), Color::ColorMix(ref mix) => mix.to_css(dest), #[cfg(feature = "gecko")] - Color::System(system) => system.to_css(dest), + Color::System(system, scheme) => { + if scheme == SystemColorScheme::Default { + system.to_css(dest) + } else { + dest.write_str("-moz-system-color(")?; + system.to_css(dest)?; + dest.write_str(", ")?; + scheme.to_css(dest)?; + dest.write_char(')') + } + }, #[cfg(feature = "gecko")] Color::InheritFromBodyQuirk => Ok(()), } @@ -696,14 +826,16 @@ impl Color { let left = mix.left.to_computed_color(context)?.to_animated_value(); let right = mix.right.to_computed_color(context)?.to_animated_value(); ToAnimatedValue::from_animated_value(AnimatedColor::mix( + mix.color_space, &left, mix.left_percentage.get(), &right, mix.right_percentage.get(), + mix.hue_adjuster, )) }, #[cfg(feature = "gecko")] - Color::System(system) => system.compute(context?), + Color::System(system, scheme) => system.compute(context?, scheme), #[cfg(feature = "gecko")] Color::InheritFromBodyQuirk => ComputedColor::rgba(context?.device().body_text_color()), }) @@ -818,3 +950,123 @@ impl Parse for CaretColor { ColorOrAuto::parse(context, input).map(GenericCaretColor) } } + +bitflags! { + /// Various flags to represent the color-scheme property in an efficient + /// way. + #[derive(Default, MallocSizeOf, SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem)] + #[repr(C)] + #[value_info(other_values = "light,dark,only")] + pub struct ColorSchemeFlags: u8 { + /// Whether the author specified `light`. + const LIGHT = 1 << 0; + /// Whether the author specified `dark`. + const DARK = 1 << 1; + /// Whether the author specified `only`. + const ONLY = 1 << 2; + } +} + +/// +#[derive( + Clone, + Debug, + Default, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToResolvedValue, + ToShmem, +)] +#[repr(C)] +#[value_info(other_values = "normal")] +pub struct ColorScheme { + #[ignore_malloc_size_of = "Arc"] + idents: crate::ArcSlice, + bits: ColorSchemeFlags, +} + +impl ColorScheme { + /// Returns the `normal` value. + pub fn normal() -> Self { + Self { + idents: Default::default(), + bits: ColorSchemeFlags::empty(), + } + } +} + +impl Parse for ColorScheme { + fn parse<'i, 't>(_: &ParserContext, input: &mut Parser<'i, 't>) -> Result> { + let mut idents = vec![]; + let mut bits = ColorSchemeFlags::empty(); + + let mut location = input.current_source_location(); + while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) { + let mut is_only = false; + match_ignore_ascii_case! { &ident, + "normal" => { + if idents.is_empty() && bits.is_empty() { + return Ok(Self::normal()); + } + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + }, + "light" => bits.insert(ColorSchemeFlags::LIGHT), + "dark" => bits.insert(ColorSchemeFlags::DARK), + "only" => { + if bits.intersects(ColorSchemeFlags::ONLY) { + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + bits.insert(ColorSchemeFlags::ONLY); + is_only = true; + }, + _ => {}, + }; + + if is_only { + if !idents.is_empty() { + // Only is allowed either at the beginning or at the end, + // but not in the middle. + break; + } + } else { + idents.push(CustomIdent::from_ident(location, &ident, &[])?); + } + location = input.current_source_location(); + } + + if idents.is_empty() { + return Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)); + } + + Ok(Self { + idents: crate::ArcSlice::from_iter(idents.into_iter()), + bits, + }) + } +} + +impl ToCss for ColorScheme { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + if self.idents.is_empty() { + debug_assert!(self.bits.is_empty()); + return dest.write_str("normal"); + } + let mut first = true; + for ident in self.idents.iter() { + if !first { + dest.write_char(' ')?; + } + first = false; + ident.to_css(dest)?; + } + if self.bits.intersects(ColorSchemeFlags::ONLY) { + dest.write_str(" only")?; + } + Ok(()) + } +} diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs index 329dedd31ca..e78fec45d02 100644 --- a/components/style/values/specified/font.rs +++ b/components/style/values/specified/font.rs @@ -6,15 +6,14 @@ #[cfg(feature = "gecko")] use crate::context::QuirksMode; -#[cfg(feature = "gecko")] -use crate::gecko_bindings::bindings; use crate::parser::{Parse, ParserContext}; use crate::values::computed::font::{FamilyName, FontFamilyList, FontStyleAngle, SingleFontFamily}; use crate::values::computed::{font as computed, Length, NonNegativeLength}; use crate::values::computed::{Angle as ComputedAngle, Percentage as ComputedPercentage}; use crate::values::computed::{CSSPixelLength, Context, ToComputedValue}; +use crate::values::computed::FontSizeAdjust as ComputedFontSizeAdjust; use crate::values::generics::font::VariationValue; -use crate::values::generics::font::{self as generics, FeatureTagValue, FontSettings, FontTag}; +use crate::values::generics::font::{self as generics, FeatureTagValue, FontSettings, FontTag, GenericFontSizeAdjust}; use crate::values::generics::NonNegative; use crate::values::specified::length::{FontBaseSize, PX_PER_PT}; use crate::values::specified::{AllowQuirks, Angle, Integer, LengthPercentage}; @@ -23,7 +22,7 @@ use crate::values::CustomIdent; use crate::Atom; use cssparser::{Parser, Token}; #[cfg(feature = "gecko")] -use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; +use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf}; use std::fmt::{self, Write}; use style_traits::values::SequenceWriter; use style_traits::{CssWriter, KeywordsCollectFn, ParseError}; @@ -682,14 +681,6 @@ pub enum FontFamily { impl FontFamily { system_font_methods!(FontFamily, font_family); - - /// Parse a specified font-family value - pub fn parse_specified<'i, 't>(input: &mut Parser<'i, 't>) -> Result> { - let values = input.parse_comma_separated(SingleFontFamily::parse)?; - Ok(FontFamily::Values(FontFamilyList::new( - values.into_boxed_slice(), - ))) - } } impl ToComputedValue for FontFamily { @@ -697,8 +688,8 @@ impl ToComputedValue for FontFamily { fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { match *self { - FontFamily::Values(ref v) => computed::FontFamily { - families: v.clone(), + FontFamily::Values(ref list) => computed::FontFamily { + families: list.clone(), is_system_font: false, }, FontFamily::System(_) => self.compute_system(context), @@ -712,18 +703,12 @@ impl ToComputedValue for FontFamily { #[cfg(feature = "gecko")] impl MallocSizeOf for FontFamily { - fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { + fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { match *self { FontFamily::Values(ref v) => { - // Although a SharedFontList object is refcounted, we always - // attribute its size to the specified value, as long as it's - // not a value in SharedFontList::sSingleGenerics. - if matches!(v, FontFamilyList::SharedFontList(_)) { - let ptr = v.shared_font_list().get(); - unsafe { bindings::Gecko_SharedFontList_SizeOfIncludingThis(ptr) } - } else { - 0 - } + // Although the family list is refcounted, we always attribute + // its size to the specified value. + v.list.unconditional_size_of(ops) }, FontFamily::System(_) => 0, } @@ -735,23 +720,31 @@ impl Parse for FontFamily { /// = | [ + ] /// TODO: fn parse<'i, 't>( - _: &ParserContext, + context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { - FontFamily::parse_specified(input) + let values = input.parse_comma_separated(|input| SingleFontFamily::parse(context, input))?; + Ok(FontFamily::Values(FontFamilyList { + #[cfg(feature = "gecko")] + list: crate::ArcSlice::from_iter(values.into_iter()), + #[cfg(feature = "servo")] + list: values.into_boxed_slice(), + #[cfg(feature = "gecko")] + fallback: computed::GenericFontFamily::None, + })) } } impl SpecifiedValueInfo for FontFamily {} -/// `FamilyName::parse` is based on `SingleFontFamily::parse` and not the other way around -/// because we want the former to exclude generic family keywords. +/// `FamilyName::parse` is based on `SingleFontFamily::parse` and not the other +/// way around because we want the former to exclude generic family keywords. impl Parse for FamilyName { fn parse<'i, 't>( - _: &ParserContext, + context: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { - match SingleFontFamily::parse(input) { + match SingleFontFamily::parse(context, input) { Ok(SingleFontFamily::FamilyName(name)) => Ok(name), Ok(SingleFontFamily::Generic(_)) => { Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) @@ -761,16 +754,13 @@ impl Parse for FamilyName { } } -#[derive( - Clone, Copy, Debug, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, -)] /// Preserve the readability of text when font fallback occurs +#[derive( + Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem, +)] +#[allow(missing_docs)] pub enum FontSizeAdjust { - /// None variant - None, - /// Number variant - Number(NonNegativeNumber), - /// system font + Value(GenericFontSizeAdjust), #[css(skip)] System(SystemFont), } @@ -779,34 +769,57 @@ impl FontSizeAdjust { #[inline] /// Default value of font-size-adjust pub fn none() -> Self { - FontSizeAdjust::None + FontSizeAdjust::Value(GenericFontSizeAdjust::None) } system_font_methods!(FontSizeAdjust, font_size_adjust); } +impl Parse for FontSizeAdjust { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + let location = input.current_source_location(); + if let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) { + #[cfg(feature = "gecko")] + let basis_enabled = static_prefs::pref!("layout.css.font-size-adjust.basis.enabled"); + #[cfg(feature = "servo")] + let basis_enabled = false; + let basis = match_ignore_ascii_case! { &ident, + "none" => return Ok(FontSizeAdjust::none()), + // Check for size adjustment basis keywords if enabled. + "ex-height" if basis_enabled => GenericFontSizeAdjust::ExHeight, + "cap-height" if basis_enabled => GenericFontSizeAdjust::CapHeight, + "ch-width" if basis_enabled => GenericFontSizeAdjust::ChWidth, + "ic-width" if basis_enabled => GenericFontSizeAdjust::IcWidth, + "ic-height" if basis_enabled => GenericFontSizeAdjust::IcHeight, + // Unknown (or disabled) keyword. + _ => return Err(location.new_custom_error( + ::selectors::parser::SelectorParseErrorKind::UnexpectedIdent(ident) + )), + }; + let value = NonNegativeNumber::parse(context, input)?; + return Ok(FontSizeAdjust::Value(basis(value))); + } + // Without a basis keyword, the number refers to the 'ex-height' metric. + let value = NonNegativeNumber::parse(context, input)?; + Ok(FontSizeAdjust::Value(GenericFontSizeAdjust::ExHeight(value))) + } +} + impl ToComputedValue for FontSizeAdjust { - type ComputedValue = computed::FontSizeAdjust; + type ComputedValue = ComputedFontSizeAdjust; fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { match *self { - FontSizeAdjust::None => computed::FontSizeAdjust::None, - FontSizeAdjust::Number(ref n) => { - // The computed version handles clamping of animated values - // itself. - computed::FontSizeAdjust::Number(n.to_computed_value(context).0) - }, + FontSizeAdjust::Value(v) => v.to_computed_value(context), FontSizeAdjust::System(_) => self.compute_system(context), } } - fn from_computed_value(computed: &computed::FontSizeAdjust) -> Self { - match *computed { - computed::FontSizeAdjust::None => FontSizeAdjust::None, - computed::FontSizeAdjust::Number(v) => { - FontSizeAdjust::Number(NonNegativeNumber::from_computed_value(&v.into())) - }, - } + fn from_computed_value(computed: &ComputedFontSizeAdjust) -> Self { + Self::Value(ToComputedValue::from_computed_value(computed)) } } @@ -1977,23 +1990,24 @@ impl Parse for FontFeatureSettings { Debug, MallocSizeOf, PartialEq, - SpecifiedValueInfo, ToComputedValue, ToResolvedValue, ToShmem, )] /// Whether user agents are allowed to synthesize bold or oblique font faces -/// when a font family lacks bold or italic faces +/// when a font family lacks those faces, or a small-caps variant when this is +/// not supported by the face. pub struct FontSynthesis { /// If a `font-weight` is requested that the font family does not contain, /// the user agent may synthesize the requested weight from the weights /// that do exist in the font family. - #[css(represents_keyword)] pub weight: bool, /// If a font-style is requested that the font family does not contain, /// the user agent may synthesize the requested style from the normal face in the font family. - #[css(represents_keyword)] pub style: bool, + /// This bit controls whether the user agent is allowed to synthesize small caps variant + /// when a font face lacks it. + pub small_caps: bool, } impl FontSynthesis { @@ -2003,8 +2017,31 @@ impl FontSynthesis { FontSynthesis { weight: true, style: true, + small_caps: true, } } + #[inline] + /// Get the 'none' value of font-synthesis + pub fn none() -> Self { + FontSynthesis { + weight: false, + style: false, + small_caps: false, + } + } + #[inline] + /// Return true if this is the 'none' value + pub fn is_none(&self) -> bool { + *self == Self::none() + } +} + +#[inline] +fn allow_font_synthesis_small_caps() -> bool { + #[cfg(feature = "gecko")] + return static_prefs::pref!("layout.css.font-synthesis-small-caps.enabled"); + #[cfg(feature = "servo")] + return false; } impl Parse for FontSynthesis { @@ -2012,26 +2049,22 @@ impl Parse for FontSynthesis { _: &ParserContext, input: &mut Parser<'i, 't>, ) -> Result> { - let mut result = FontSynthesis { - weight: false, - style: false, - }; - try_match_ident_ignore_ascii_case! { input, - "none" => Ok(result), - "weight" => { - result.weight = true; - if input.try_parse(|input| input.expect_ident_matching("style")).is_ok() { - result.style = true; - } - Ok(result) - }, - "style" => { - result.style = true; - if input.try_parse(|input| input.expect_ident_matching("weight")).is_ok() { - result.weight = true; - } - Ok(result) - }, + use crate::values::SelectorParseErrorKind; + let mut result = Self::none(); + while let Ok(ident) = input.try_parse(|i| i.expect_ident_cloned()) { + match_ignore_ascii_case! { &ident, + "none" if result.is_none() => return Ok(result), + "weight" if !result.weight => result.weight = true, + "style" if !result.style => result.style = true, + "small-caps" if !result.small_caps && allow_font_synthesis_small_caps() + => result.small_caps = true, + _ => return Err(input.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(ident))), + } + } + if !result.is_none() { + Ok(result) + } else { + Err(input.new_custom_error(StyleParseErrorKind::UnspecifiedError)) } } } @@ -2041,14 +2074,41 @@ impl ToCss for FontSynthesis { where W: Write, { - if self.weight && self.style { - dest.write_str("weight style") - } else if self.style { - dest.write_str("style") - } else if self.weight { - dest.write_str("weight") - } else { - dest.write_str("none") + if self.is_none() { + return dest.write_str("none"); + } + + let mut need_space = false; + if self.weight { + dest.write_str("weight")?; + need_space = true; + } + if self.style { + if need_space { + dest.write_str(" ")?; + } + dest.write_str("style")?; + need_space = true; + } + if self.small_caps { + if need_space { + dest.write_str(" ")?; + } + dest.write_str("small-caps")?; + } + Ok(()) + } +} + +impl SpecifiedValueInfo for FontSynthesis { + fn collect_completion_keywords(f: KeywordsCollectFn) { + f(&[ + "none", + "weight", + "style", + ]); + if allow_font_synthesis_small_caps() { + f(&["small-caps"]); } } } @@ -2061,6 +2121,7 @@ impl From for FontSynthesis { FontSynthesis { weight: bits & structs::NS_FONT_SYNTHESIS_WEIGHT as u8 != 0, style: bits & structs::NS_FONT_SYNTHESIS_STYLE as u8 != 0, + small_caps: bits & structs::NS_FONT_SYNTHESIS_SMALL_CAPS as u8 != 0, } } } @@ -2077,6 +2138,9 @@ impl From for u8 { if v.style { bits |= structs::NS_FONT_SYNTHESIS_STYLE as u8; } + if v.small_caps { + bits |= structs::NS_FONT_SYNTHESIS_SMALL_CAPS as u8; + } bits } } diff --git a/components/style/values/specified/image.rs b/components/style/values/specified/image.rs index 85c178e064d..f2167e52800 100644 --- a/components/style/values/specified/image.rs +++ b/components/style/values/specified/image.rs @@ -83,7 +83,7 @@ fn cross_fade_enabled() -> bool { #[cfg(feature = "gecko")] fn image_set_enabled() -> bool { - static_prefs::pref!("layout.css.image-set.enabled") + true } #[cfg(feature = "servo")] @@ -365,7 +365,14 @@ impl ImageSet { cors_mode: CorsMode, only_url: bool, ) -> Result> { - input.expect_function_matching("image-set")?; + let function = input.expect_function()?; + match_ignore_ascii_case! { &function, + "-webkit-image-set" | "image-set" => {}, + _ => { + let func = function.clone(); + return Err(input.new_custom_error(StyleParseErrorKind::UnexpectedFunction(func))); + } + } let items = input.parse_nested_block(|input| { input.parse_comma_separated(|input| ImageSetItem::parse(context, input, cors_mode, only_url)) })?; diff --git a/components/style/values/specified/length.rs b/components/style/values/specified/length.rs index aaad895dbce..e73a9ddfde0 100644 --- a/components/style/values/specified/length.rs +++ b/components/style/values/specified/length.rs @@ -1247,6 +1247,27 @@ macro_rules! parse_size_non_length { }}; } +#[cfg(feature = "gecko")] +fn is_fit_content_function_enabled() -> bool { + static_prefs::pref!("layout.css.fit-content-function.enabled") +} + +#[cfg(feature = "gecko")] +macro_rules! parse_fit_content_function { + ($size:ident, $input:expr, $context:expr, $allow_quirks:expr) => { + if is_fit_content_function_enabled() { + if let Ok(length) = $input.try_parse(|input| { + input.expect_function_matching("fit-content")?; + input.parse_nested_block(|i| { + NonNegativeLengthPercentage::parse_quirky($context, i, $allow_quirks) + }) + }) { + return Ok($size::FitContentFunction(length)); + } + } + }; +} + impl Size { /// Parses, with quirks. pub fn parse_quirky<'i, 't>( @@ -1255,6 +1276,8 @@ impl Size { allow_quirks: AllowQuirks, ) -> Result> { parse_size_non_length!(Size, input, "auto" => Auto); + #[cfg(feature = "gecko")] + parse_fit_content_function!(Size, input, context, allow_quirks); let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?; Ok(GenericSize::LengthPercentage(length)) @@ -1287,6 +1310,8 @@ impl MaxSize { allow_quirks: AllowQuirks, ) -> Result> { parse_size_non_length!(MaxSize, input, "none" => None); + #[cfg(feature = "gecko")] + parse_fit_content_function!(MaxSize, input, context, allow_quirks); let length = NonNegativeLengthPercentage::parse_quirky(context, input, allow_quirks)?; Ok(GenericMaxSize::LengthPercentage(length)) diff --git a/components/style/values/specified/list.rs b/components/style/values/specified/list.rs index 5d31c0b34d2..14004e65cc8 100644 --- a/components/style/values/specified/list.rs +++ b/components/style/values/specified/list.rs @@ -67,6 +67,15 @@ impl ListStyleType { _ => unreachable!("Unknown counter style keyword value"), }))) } + + /// Is this a bullet? (i.e. `list-style-type: disc|circle|square|disclosure-closed|disclosure-open`) + #[inline] + pub fn is_bullet(&self) -> bool { + match self { + ListStyleType::CounterStyle(ref style) => style.is_bullet(), + _ => false, + } + } } #[cfg(feature = "gecko")] diff --git a/components/style/values/specified/mod.rs b/components/style/values/specified/mod.rs index 8a9cb73f4da..6cd952cd82f 100644 --- a/components/style/values/specified/mod.rs +++ b/components/style/values/specified/mod.rs @@ -42,7 +42,7 @@ pub use self::box_::{Clear, Float, Overflow, OverflowAnchor}; pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize}; pub use self::box_::{ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStrictness, ScrollSnapType}; pub use self::box_::{TouchAction, TransitionProperty, VerticalAlign, WillChange}; -pub use self::color::{Color, ColorOrAuto, ColorPropertyValue}; +pub use self::color::{Color, ColorOrAuto, ColorPropertyValue, ColorScheme}; pub use self::column::ColumnCount; pub use self::counters::{Content, ContentItem, CounterIncrement, CounterSetOrReset}; pub use self::easing::TimingFunction; @@ -80,7 +80,7 @@ pub use self::position::{PositionComponent, ZIndex}; pub use self::ratio::Ratio; pub use self::rect::NonNegativeLengthOrNumberRect; pub use self::resolution::Resolution; -pub use self::svg::MozContextProperties; +pub use self::svg::{DProperty, MozContextProperties}; pub use self::svg::{SVGLength, SVGOpacity, SVGPaint}; pub use self::svg::{SVGPaintOrder, SVGStrokeDashArray, SVGWidth}; pub use self::svg_path::SVGPathData; diff --git a/components/style/values/specified/page.rs b/components/style/values/specified/page.rs index 81684e114a3..4d96b532689 100644 --- a/components/style/values/specified/page.rs +++ b/components/style/values/specified/page.rs @@ -23,15 +23,15 @@ impl Parse for PageSize { ) -> Result> { // Try to parse as [ ] if let Ok(paper_size) = input.try_parse(PaperSize::parse) { - if let Ok(orientation) = input.try_parse(Orientation::parse) { - return Ok(PageSize::PaperSizeAndOrientation(paper_size, orientation)); - } - return Ok(PageSize::PaperSize(paper_size)); + let orientation = input + .try_parse(Orientation::parse) + .unwrap_or(Orientation::Portrait); + return Ok(PageSize::PaperSize(paper_size, orientation)); } // Try to parse as [ ] if let Ok(orientation) = input.try_parse(Orientation::parse) { if let Ok(paper_size) = input.try_parse(PaperSize::parse) { - return Ok(PageSize::PaperSizeAndOrientation(paper_size, orientation)); + return Ok(PageSize::PaperSize(paper_size, orientation)); } return Ok(PageSize::Orientation(orientation)); } diff --git a/components/style/values/specified/svg.rs b/components/style/values/specified/svg.rs index abc6587239c..f06fc43c46d 100644 --- a/components/style/values/specified/svg.rs +++ b/components/style/values/specified/svg.rs @@ -10,6 +10,7 @@ use crate::values::specified::color::Color; use crate::values::specified::url::SpecifiedUrl; use crate::values::specified::AllowQuirks; use crate::values::specified::LengthPercentage; +use crate::values::specified::SVGPathData; use crate::values::specified::{NonNegativeLengthPercentage, Opacity}; use crate::values::CustomIdent; use cssparser::{Parser, Token}; @@ -334,3 +335,57 @@ impl Parse for MozContextProperties { }) } } + +/// The svg d property type. +/// +/// https://svgwg.org/svg2-draft/paths.html#TheDProperty +#[derive( + Animate, + Clone, + ComputeSquaredDistance, + Debug, + Deserialize, + MallocSizeOf, + PartialEq, + Serialize, + SpecifiedValueInfo, + ToAnimatedZero, + ToComputedValue, + ToCss, + ToResolvedValue, + ToShmem, +)] +#[repr(C, u8)] +pub enum DProperty { + /// Path value for path() or just a . + #[css(function)] + Path(SVGPathData), + /// None value. + #[animation(error)] + None, +} + +impl DProperty { + /// return none. + #[inline] + pub fn none() -> Self { + DProperty::None + } +} + +impl Parse for DProperty { + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + // Parse none. + if input.try_parse(|i| i.expect_ident_matching("none")).is_ok() { + return Ok(DProperty::none()); + } + + // Parse possible functions. + input.expect_function_matching("path")?; + let path_data = input.parse_nested_block(|i| SVGPathData::parse(context, i))?; + Ok(DProperty::Path(path_data)) + } +} diff --git a/components/style/values/specified/svg_path.rs b/components/style/values/specified/svg_path.rs index 9f1c5c5e32c..a443a443326 100644 --- a/components/style/values/specified/svg_path.rs +++ b/components/style/values/specified/svg_path.rs @@ -60,6 +60,111 @@ impl SVGPathData { SVGPathData(crate::ArcSlice::from_iter(result.into_iter())) } + + // FIXME: Bug 1714238, we may drop this once we use the same data structure for both SVG and + // CSS. + /// Decode the svg path raw data from Gecko. + #[cfg(feature = "gecko")] + pub fn decode_from_f32_array(path: &[f32]) -> Result { + use crate::gecko_bindings::structs::dom::SVGPathSeg_Binding::*; + + let mut result: Vec = Vec::new(); + let mut i: usize = 0; + while i < path.len() { + // See EncodeType() and DecodeType() in SVGPathSegUtils.h. + // We are using reinterpret_cast<> to encode and decode between u32 and f32, so here we + // use to_bits() to decode the type. + let seg_type = path[i].to_bits() as u16; + i = i + 1; + match seg_type { + PATHSEG_CLOSEPATH => result.push(PathCommand::ClosePath), + PATHSEG_MOVETO_ABS | PATHSEG_MOVETO_REL => { + debug_assert!(i + 1 < path.len()); + result.push(PathCommand::MoveTo { + point: CoordPair::new(path[i], path[i + 1]), + absolute: IsAbsolute::new(seg_type == PATHSEG_MOVETO_ABS), + }); + i = i + 2; + } + PATHSEG_LINETO_ABS | PATHSEG_LINETO_REL => { + debug_assert!(i + 1 < path.len()); + result.push(PathCommand::LineTo { + point: CoordPair::new(path[i], path[i + 1]), + absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_ABS), + }); + i = i + 2; + } + PATHSEG_CURVETO_CUBIC_ABS | PATHSEG_CURVETO_CUBIC_REL => { + debug_assert!(i + 5 < path.len()); + result.push(PathCommand::CurveTo { + control1: CoordPair::new(path[i], path[i + 1]), + control2: CoordPair::new(path[i + 2], path[i + 3]), + point: CoordPair::new(path[i + 4], path[i + 5]), + absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_CUBIC_ABS), + }); + i = i + 6; + } + PATHSEG_CURVETO_QUADRATIC_ABS | PATHSEG_CURVETO_QUADRATIC_REL => { + debug_assert!(i + 3 < path.len()); + result.push(PathCommand::QuadBezierCurveTo { + control1: CoordPair::new(path[i], path[i + 1]), + point: CoordPair::new(path[i + 2], path[i + 3]), + absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_QUADRATIC_ABS), + }); + i = i + 4; + } + PATHSEG_ARC_ABS | PATHSEG_ARC_REL => { + debug_assert!(i + 6 < path.len()); + result.push(PathCommand::EllipticalArc { + rx: path[i], + ry: path[i + 1], + angle: path[i + 2], + large_arc_flag: ArcFlag(path[i + 3] != 0.0f32), + sweep_flag: ArcFlag(path[i + 4] != 0.0f32), + point: CoordPair::new(path[i + 5], path[i + 6]), + absolute: IsAbsolute::new(seg_type == PATHSEG_ARC_ABS), + }); + i = i + 7; + } + PATHSEG_LINETO_HORIZONTAL_ABS | PATHSEG_LINETO_HORIZONTAL_REL => { + debug_assert!(i < path.len()); + result.push(PathCommand::HorizontalLineTo { + x: path[i], + absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_HORIZONTAL_ABS), + }); + i = i + 1; + } + PATHSEG_LINETO_VERTICAL_ABS | PATHSEG_LINETO_VERTICAL_REL => { + debug_assert!(i < path.len()); + result.push(PathCommand::VerticalLineTo { + y: path[i], + absolute: IsAbsolute::new(seg_type == PATHSEG_LINETO_VERTICAL_ABS), + }); + i = i + 1; + } + PATHSEG_CURVETO_CUBIC_SMOOTH_ABS | PATHSEG_CURVETO_CUBIC_SMOOTH_REL => { + debug_assert!(i + 3 < path.len()); + result.push(PathCommand::SmoothCurveTo { + control2: CoordPair::new(path[i], path[i + 1]), + point: CoordPair::new(path[i + 2], path[i + 3]), + absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS), + }); + i = i + 4; + } + PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS | PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL => { + debug_assert!(i + 1 < path.len()); + result.push(PathCommand::SmoothQuadBezierCurveTo { + point: CoordPair::new(path[i], path[i + 1]), + absolute: IsAbsolute::new(seg_type == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS), + }); + i = i + 2; + } + PATHSEG_UNKNOWN | _ => return Err(()), + } + } + + Ok(SVGPathData(crate::ArcSlice::from_iter(result.into_iter()))) + } } impl ToCss for SVGPathData { @@ -506,6 +611,17 @@ impl IsAbsolute { pub fn is_yes(&self) -> bool { *self == IsAbsolute::Yes } + + /// Return Yes if value is true. Otherwise, return No. + #[inline] + #[cfg(feature = "gecko")] + fn new(value: bool) -> Self { + if value { + IsAbsolute::Yes + } else { + IsAbsolute::No + } + } } /// The path coord type. diff --git a/components/style/values/specified/transform.rs b/components/style/values/specified/transform.rs index ba2be4ee8d3..c10b79a089b 100644 --- a/components/style/values/specified/transform.rs +++ b/components/style/values/specified/transform.rs @@ -238,8 +238,14 @@ impl Transform { Ok(generic::TransformOperation::SkewY(theta)) }, "perspective" => { - let d = specified::Length::parse_non_negative(context, input)?; - Ok(generic::TransformOperation::Perspective(d)) + let p = match input.try_parse(|input| specified::Length::parse_non_negative(context, input)) { + Ok(p) => generic::PerspectiveFunction::Length(p), + Err(..) => { + input.expect_ident_matching("none")?; + generic::PerspectiveFunction::None + } + }; + Ok(generic::TransformOperation::Perspective(p)) }, _ => Err(()), }; diff --git a/components/style_traits/arc_slice.rs b/components/style_traits/arc_slice.rs index 95afe6b3aad..e55861d183b 100644 --- a/components/style_traits/arc_slice.rs +++ b/components/style_traits/arc_slice.rs @@ -13,6 +13,8 @@ use std::ptr::NonNull; use std::{iter, mem}; use to_shmem_derive::ToShmem; +use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalSizeOf}; + /// A canary that we stash in ArcSlices. /// /// Given we cannot use a zero-sized-type for the header, since well, C++ @@ -137,6 +139,22 @@ impl ArcSlice { std::mem::forget(empty); ptr as *mut _ } + + /// Returns whether there's only one reference to this ArcSlice. + pub fn is_unique(&self) -> bool { + self.0.with_arc(|arc| arc.is_unique()) + } +} + +impl MallocUnconditionalSizeOf for ArcSlice { + #[allow(unsafe_code)] + fn unconditional_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { + let mut size = unsafe { ops.malloc_size_of(self.0.heap_ptr()) }; + for el in self.iter() { + size += el.size_of(ops); + } + size + } } /// The inner pointer of an ArcSlice, to be sent via FFI. diff --git a/servo-tidy.toml b/servo-tidy.toml index 637a7355962..ae743148d5d 100644 --- a/servo-tidy.toml +++ b/servo-tidy.toml @@ -81,10 +81,14 @@ packages = [ files = [ "./components/net/tests/parsable_mime/text", # These are ignored to avoid diverging from Gecko + "./components/style/counter_style/mod.rs", "./components/style/properties/helpers.mako.rs", "./components/style/stylesheets/rule_parser.rs", "./components/style/stylist.rs", + "./components/style/values/computed/font.rs", "./components/style/values/computed/image.rs", + "./components/style/values/specified/color.rs", + "./components/style/values/specified/transform.rs", # Mako does not lend itself easily to splitting long lines "./components/style/properties/helpers/animated_properties.mako.rs", "./components/style/properties/shorthands/text.mako.rs", diff --git a/tests/wpt/metadata-layout-2020/css/css-backgrounds/background-position-calc-minmax-001.html.ini b/tests/wpt/metadata-layout-2020/css/css-backgrounds/background-position-calc-minmax-001.html.ini deleted file mode 100644 index ac3423de86d..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-backgrounds/background-position-calc-minmax-001.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[background-position-calc-minmax-001.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-backgrounds/background-position-negative-percentage-comparison.html.ini b/tests/wpt/metadata-layout-2020/css/css-backgrounds/background-position-negative-percentage-comparison.html.ini deleted file mode 100644 index 97f5bd10ffd..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-backgrounds/background-position-negative-percentage-comparison.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[background-position-negative-percentage-comparison.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-transforms/animation/transform-interpolation-001.html.ini b/tests/wpt/metadata-layout-2020/css/css-transforms/animation/transform-interpolation-001.html.ini index c9ba291778f..4cb29198072 100644 --- a/tests/wpt/metadata-layout-2020/css/css-transforms/animation/transform-interpolation-001.html.ini +++ b/tests/wpt/metadata-layout-2020/css/css-transforms/animation/transform-interpolation-001.html.ini @@ -926,51 +926,6 @@ [Web Animations: property from [scaleZ(2)\] to [scaleZ(2) perspective(500px)\] at (2) should be [scaleZ(2) perspective(250px)\]] expected: FAIL - [CSS Transitions: property from [perspective(none)\] to [perspective(500px)\] at (-1) should be [perspective(none)\]] - expected: FAIL - - [CSS Transitions: property from [perspective(none)\] to [perspective(500px)\] at (0) should be [perspective(none)\]] - expected: FAIL - - [CSS Transitions: property from [perspective(none)\] to [perspective(500px)\] at (0.5) should be [perspective(1000px)\]] - expected: FAIL - - [CSS Transitions: property from [perspective(none)\] to [perspective(500px)\] at (1) should be [perspective(500px)\]] - expected: FAIL - - [CSS Transitions: property from [perspective(none)\] to [perspective(500px)\] at (2) should be [perspective(250px)\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [perspective(none)\] to [perspective(500px)\] at (-1) should be [perspective(none)\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [perspective(none)\] to [perspective(500px)\] at (0) should be [perspective(none)\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [perspective(none)\] to [perspective(500px)\] at (0.5) should be [perspective(1000px)\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [perspective(none)\] to [perspective(500px)\] at (1) should be [perspective(500px)\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [perspective(none)\] to [perspective(500px)\] at (2) should be [perspective(250px)\]] - expected: FAIL - - [CSS Animations: property from [perspective(none)\] to [perspective(500px)\] at (-1) should be [perspective(none)\]] - expected: FAIL - - [CSS Animations: property from [perspective(none)\] to [perspective(500px)\] at (0) should be [perspective(none)\]] - expected: FAIL - - [CSS Animations: property from [perspective(none)\] to [perspective(500px)\] at (0.5) should be [perspective(1000px)\]] - expected: FAIL - - [CSS Animations: property from [perspective(none)\] to [perspective(500px)\] at (1) should be [perspective(500px)\]] - expected: FAIL - - [CSS Animations: property from [perspective(none)\] to [perspective(500px)\] at (2) should be [perspective(250px)\]] - expected: FAIL - [Web Animations: property from [perspective(none)\] to [perspective(500px)\] at (-1) should be [perspective(none)\]] expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-transforms/animation/transform-interpolation-verify-reftests.html.ini b/tests/wpt/metadata-layout-2020/css/css-transforms/animation/transform-interpolation-verify-reftests.html.ini index 0f6e1f8159e..926da2b159f 100644 --- a/tests/wpt/metadata-layout-2020/css/css-transforms/animation/transform-interpolation-verify-reftests.html.ini +++ b/tests/wpt/metadata-layout-2020/css/css-transforms/animation/transform-interpolation-verify-reftests.html.ini @@ -203,39 +203,12 @@ [Web Animations: property from [matrix(1,0,0,1,0,0) rotate(0deg)\] to [matrix(2,0,0,2,0,0) rotate(360deg)\] at (0.5) should be [matrix(1.5,0,0,1.5,0,0) rotate(180deg)\]] expected: FAIL - [CSS Transitions: property from [perspective(none) translateZ(15px)\] to [perspective(none) translateZ(15px)\] at (0.5) should be [perspective(none) translateZ(15px)\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [perspective(none) translateZ(15px)\] to [perspective(none) translateZ(15px)\] at (0.5) should be [perspective(none) translateZ(15px)\]] - expected: FAIL - - [CSS Animations: property from [perspective(none) translateZ(15px)\] to [perspective(none) translateZ(15px)\] at (0.5) should be [perspective(none) translateZ(15px)\]] - expected: FAIL - [Web Animations: property from [perspective(none) translateZ(15px)\] to [perspective(none) translateZ(15px)\] at (0.5) should be [perspective(none) translateZ(15px)\]] expected: FAIL - [CSS Transitions: property from [perspective(100px) translateZ(50px)\] to [perspective(none) translateZ(50px)\] at (0.5) should be [perspective(200px) translateZ(50px)\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [perspective(100px) translateZ(50px)\] to [perspective(none) translateZ(50px)\] at (0.5) should be [perspective(200px) translateZ(50px)\]] - expected: FAIL - - [CSS Animations: property from [perspective(100px) translateZ(50px)\] to [perspective(none) translateZ(50px)\] at (0.5) should be [perspective(200px) translateZ(50px)\]] - expected: FAIL - [Web Animations: property from [perspective(100px) translateZ(50px)\] to [perspective(none) translateZ(50px)\] at (0.5) should be [perspective(200px) translateZ(50px)\]] expected: FAIL - [CSS Transitions: property from [perspective(none) translateZ(15px)\] to [perspective(25px) translateZ(15px)\] at (0.5) should be [perspective(50px) translateZ(15px)\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [perspective(none) translateZ(15px)\] to [perspective(25px) translateZ(15px)\] at (0.5) should be [perspective(50px) translateZ(15px)\]] - expected: FAIL - - [CSS Animations: property from [perspective(none) translateZ(15px)\] to [perspective(25px) translateZ(15px)\] at (0.5) should be [perspective(50px) translateZ(15px)\]] - expected: FAIL - [Web Animations: property from [perspective(none) translateZ(15px)\] to [perspective(25px) translateZ(15px)\] at (0.5) should be [perspective(50px) translateZ(15px)\]] expected: FAIL @@ -248,9 +221,6 @@ [CSS Transitions with transition: all: property from [perspective(0.1px) translateZ(0.25px)\] to [perspective(0.1px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]] expected: FAIL - [CSS Animations: property from [perspective(0.1px) translateZ(0.25px)\] to [perspective(0.1px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]] - expected: FAIL - [Web Animations: property from [perspective(0.1px) translateZ(0.25px)\] to [perspective(0.1px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]] expected: FAIL @@ -260,21 +230,9 @@ [CSS Transitions with transition: all: property from [perspective(0px) translateZ(0.25px)\] to [perspective(0px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]] expected: FAIL - [CSS Animations: property from [perspective(0px) translateZ(0.25px)\] to [perspective(0px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]] - expected: FAIL - [Web Animations: property from [perspective(0px) translateZ(0.25px)\] to [perspective(0px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]] expected: FAIL - [CSS Transitions: property from [perspective(0px) translateZ(0.5px)\] to [perspective(3px) translateZ(0.5px)\] at (0.5) should be [perspective(1.5px) translateZ(0.5px)\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [perspective(0px) translateZ(0.5px)\] to [perspective(3px) translateZ(0.5px)\] at (0.5) should be [perspective(1.5px) translateZ(0.5px)\]] - expected: FAIL - - [CSS Animations: property from [perspective(0px) translateZ(0.5px)\] to [perspective(3px) translateZ(0.5px)\] at (0.5) should be [perspective(1.5px) translateZ(0.5px)\]] - expected: FAIL - [Web Animations: property from [perspective(0px) translateZ(0.5px)\] to [perspective(3px) translateZ(0.5px)\] at (0.5) should be [perspective(1.5px) translateZ(0.5px)\]] expected: FAIL @@ -290,14 +248,5 @@ [Web Animations: property from [perspective(10px) translateZ(0.5px)\] to [perspective(1px) translateZ(0.5px)\] at (-1) should be [translateZ(0.5px)\]] expected: FAIL - [CSS Transitions: property from [perspective(1px) translateZ(0.5px)\] to [perspective(10px) translateZ(0.5px)\] at (-1) should be [perspective(1px) translateZ(0.5px)\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [perspective(1px) translateZ(0.5px)\] to [perspective(10px) translateZ(0.5px)\] at (-1) should be [perspective(1px) translateZ(0.5px)\]] - expected: FAIL - - [CSS Animations: property from [perspective(1px) translateZ(0.5px)\] to [perspective(10px) translateZ(0.5px)\] at (-1) should be [perspective(1px) translateZ(0.5px)\]] - expected: FAIL - [Web Animations: property from [perspective(1px) translateZ(0.5px)\] to [perspective(10px) translateZ(0.5px)\] at (-1) should be [perspective(1px) translateZ(0.5px)\]] expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-transforms/parsing/transform-valid.html.ini b/tests/wpt/metadata-layout-2020/css/css-transforms/parsing/transform-valid.html.ini index 6ee0275f23d..99bacfc4422 100644 --- a/tests/wpt/metadata-layout-2020/css/css-transforms/parsing/transform-valid.html.ini +++ b/tests/wpt/metadata-layout-2020/css/css-transforms/parsing/transform-valid.html.ini @@ -1,6 +1,3 @@ [transform-valid.html] - [e.style['transform'\] = "perspective(none)" should set the property value] - expected: FAIL - [e.style['transform'\] = "translate(1px, 0%)" should set the property value] expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-transforms/perspective-zero-2.html.ini b/tests/wpt/metadata-layout-2020/css/css-transforms/perspective-zero-2.html.ini deleted file mode 100644 index c3150604a8a..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-transforms/perspective-zero-2.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[perspective-zero-2.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-transforms/perspective-zero-3.html.ini b/tests/wpt/metadata-layout-2020/css/css-transforms/perspective-zero-3.html.ini deleted file mode 100644 index b2860a8900b..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-transforms/perspective-zero-3.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[perspective-zero-3.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-transforms/perspective-zero.html.ini b/tests/wpt/metadata-layout-2020/css/css-transforms/perspective-zero.html.ini deleted file mode 100644 index 66e93c50082..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-transforms/perspective-zero.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[perspective-zero.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-transforms/transform3d-perspective-005.html.ini b/tests/wpt/metadata-layout-2020/css/css-transforms/transform3d-perspective-005.html.ini deleted file mode 100644 index ae307d59949..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-transforms/transform3d-perspective-005.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[transform3d-perspective-005.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-values/minmax-percentage-serialize.html.ini b/tests/wpt/metadata-layout-2020/css/css-values/minmax-percentage-serialize.html.ini index b291291efc4..88a8612cfd8 100644 --- a/tests/wpt/metadata-layout-2020/css/css-values/minmax-percentage-serialize.html.ini +++ b/tests/wpt/metadata-layout-2020/css/css-values/minmax-percentage-serialize.html.ini @@ -1,36 +1,3 @@ [minmax-percentage-serialize.html] - ['min(1%, 2%, 3%)' as a specified value should serialize as 'min(1%, 2%, 3%)'.] - expected: FAIL - - ['min(1%, 2%, 3%)' as a computed value should serialize as 'min(1%, 2%, 3%)'.] - expected: FAIL - - ['min(3%, 2%, 1%)' as a specified value should serialize as 'min(3%, 2%, 1%)'.] - expected: FAIL - - ['min(3%, 2%, 1%)' as a computed value should serialize as 'min(3%, 2%, 1%)'.] - expected: FAIL - - ['max(1%, 2%, 3%)' as a specified value should serialize as 'max(1%, 2%, 3%)'.] - expected: FAIL - - ['max(1%, 2%, 3%)' as a computed value should serialize as 'max(1%, 2%, 3%)'.] - expected: FAIL - - ['max(3%, 2%, 1%)' as a specified value should serialize as 'max(3%, 2%, 1%)'.] - expected: FAIL - - ['max(3%, 2%, 1%)' as a computed value should serialize as 'max(3%, 2%, 1%)'.] - expected: FAIL - - ['min(1%, 2%, 3%) 0px' as a specified value should serialize as 'min(1%, 2%, 3%) 0px'.] - expected: FAIL - ['min(1%, 2%, 3%) 0px' as a computed value should serialize as 'min(1%, 2%, 3%) 0px'.] expected: FAIL - - ['calc(min(1%, 2%) + max(3%, 4%) + 10%)' as a specified value should serialize as 'calc(10% + min(1%, 2%) + max(3%, 4%))'.] - expected: FAIL - - ['calc(min(1%, 2%) + max(3%, 4%) + 10%)' as a computed value should serialize as 'calc(10% + min(1%, 2%) + max(3%, 4%))'.] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-variables/variable-cssText.html.ini b/tests/wpt/metadata-layout-2020/css/css-variables/variable-cssText.html.ini index a360bf99f1b..26c397aae58 100644 --- a/tests/wpt/metadata-layout-2020/css/css-variables/variable-cssText.html.ini +++ b/tests/wpt/metadata-layout-2020/css/css-variables/variable-cssText.html.ini @@ -7,9 +7,3 @@ [target6] expected: FAIL - - [target9] - expected: FAIL - - [target11] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-variables/variable-declaration-07.html.ini b/tests/wpt/metadata-layout-2020/css/css-variables/variable-declaration-07.html.ini deleted file mode 100644 index 7255d7fc46e..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-variables/variable-declaration-07.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-declaration-07.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-variables/variable-declaration-09.html.ini b/tests/wpt/metadata-layout-2020/css/css-variables/variable-declaration-09.html.ini deleted file mode 100644 index 177674de03e..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-variables/variable-declaration-09.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-declaration-09.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-variables/variable-declaration-29.html.ini b/tests/wpt/metadata-layout-2020/css/css-variables/variable-declaration-29.html.ini deleted file mode 100644 index 815d162d51a..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-variables/variable-declaration-29.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-declaration-29.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-variables/variable-definition.html.ini b/tests/wpt/metadata-layout-2020/css/css-variables/variable-definition.html.ini index a3daa11079d..7d3d2c0fa5e 100644 --- a/tests/wpt/metadata-layout-2020/css/css-variables/variable-definition.html.ini +++ b/tests/wpt/metadata-layout-2020/css/css-variables/variable-definition.html.ini @@ -5,78 +5,15 @@ [CSSOM.setProperty with space 2] expected: FAIL - [no char variable] - expected: FAIL - - [white space value (single space)] - expected: FAIL - - [white space value (double space)] - expected: FAIL - [can't overwrite with no value] expected: FAIL - [can overwrite with space value] - expected: FAIL - - [ leading white space (single space)] - expected: FAIL - - [ leading white space (double space) 2] - expected: FAIL - - [no char variable (Computed Style)] - expected: FAIL - - [white space value (single space) (Computed Style)] - expected: FAIL - - [white space value (double space) (Computed Style)] - expected: FAIL - [can't overwrite with no value (Computed Style)] expected: FAIL - [can overwrite with space value (Computed Style)] - expected: FAIL - - [ leading white space (single space) (Computed Style)] - expected: FAIL - - [ leading white space (double space) 2 (Computed Style)] - expected: FAIL - - [no char variable (Cascading)] - expected: FAIL - - [white space value (single space) (Cascading)] - expected: FAIL - - [white space value (double space) (Cascading)] - expected: FAIL - [can't overwrite with no value (Cascading)] expected: FAIL - [can overwrite with space value (Cascading)] - expected: FAIL - - [ leading white space (single space) (Cascading)] - expected: FAIL - - [ leading white space (double space) 2 (Cascading)] - expected: FAIL - - [can overwrite with no value] - expected: FAIL - - [can overwrite with no value (Computed Style)] - expected: FAIL - - [can overwrite with no value (Cascading)] - expected: FAIL - [ trailing white space (single space)] expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-variables/variable-empty-name-reserved.html.ini b/tests/wpt/metadata-layout-2020/css/css-variables/variable-empty-name-reserved.html.ini deleted file mode 100644 index 709cf140b1f..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-variables/variable-empty-name-reserved.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[variable-empty-name-reserved.html] - [-- is a reserved property name] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-variables/variable-reference-06.html.ini b/tests/wpt/metadata-layout-2020/css/css-variables/variable-reference-06.html.ini deleted file mode 100644 index cd8687ce1d1..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-variables/variable-reference-06.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-reference-06.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-variables/variable-reference-11.html.ini b/tests/wpt/metadata-layout-2020/css/css-variables/variable-reference-11.html.ini deleted file mode 100644 index d51bc156ae2..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-variables/variable-reference-11.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-reference-11.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-variables/variable-reference.html.ini b/tests/wpt/metadata-layout-2020/css/css-variables/variable-reference.html.ini index 327fe5c08c2..193b9f252c9 100644 --- a/tests/wpt/metadata-layout-2020/css/css-variables/variable-reference.html.ini +++ b/tests/wpt/metadata-layout-2020/css/css-variables/variable-reference.html.ini @@ -1,6 +1,3 @@ [variable-reference.html] - [width: var(--prop,);] - expected: FAIL - [Variable reference left open at end of stylesheet] expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-variables/variable-substitution-variable-declaration.html.ini b/tests/wpt/metadata-layout-2020/css/css-variables/variable-substitution-variable-declaration.html.ini index 7e95ee1dde0..60c36462bdc 100644 --- a/tests/wpt/metadata-layout-2020/css/css-variables/variable-substitution-variable-declaration.html.ini +++ b/tests/wpt/metadata-layout-2020/css/css-variables/variable-substitution-variable-declaration.html.ini @@ -1,24 +1,3 @@ [variable-substitution-variable-declaration.html] [target10 --varC] expected: FAIL - - [target1 --var2] - expected: FAIL - - [target2 --var1] - expected: FAIL - - [target3 --var1] - expected: FAIL - - [target3 --var2] - expected: FAIL - - [target4 --varC] - expected: FAIL - - [target8 --varB] - expected: FAIL - - [target9 --varA] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-variables/variable-supports-05.html.ini b/tests/wpt/metadata-layout-2020/css/css-variables/variable-supports-05.html.ini deleted file mode 100644 index 4d0f1862a6c..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-variables/variable-supports-05.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-supports-05.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-variables/variable-supports-07.html.ini b/tests/wpt/metadata-layout-2020/css/css-variables/variable-supports-07.html.ini deleted file mode 100644 index 69995c11cbe..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-variables/variable-supports-07.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-supports-07.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-variables/variable-supports-37.html.ini b/tests/wpt/metadata-layout-2020/css/css-variables/variable-supports-37.html.ini deleted file mode 100644 index c9b4e3a1ea4..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-variables/variable-supports-37.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-supports-37.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-variables/variable-supports-39.html.ini b/tests/wpt/metadata-layout-2020/css/css-variables/variable-supports-39.html.ini deleted file mode 100644 index 11fc7d6d05c..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-variables/variable-supports-39.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-supports-39.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-variables/variable-supports-57.html.ini b/tests/wpt/metadata-layout-2020/css/css-variables/variable-supports-57.html.ini deleted file mode 100644 index 676c3da63fa..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-variables/variable-supports-57.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-supports-57.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/css-variables/variable-supports-58.html.ini b/tests/wpt/metadata-layout-2020/css/css-variables/variable-supports-58.html.ini deleted file mode 100644 index ebf7e5d65d9..00000000000 --- a/tests/wpt/metadata-layout-2020/css/css-variables/variable-supports-58.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-supports-58.html] - expected: FAIL diff --git a/tests/wpt/metadata-layout-2020/css/cssom/serialize-variable-reference.html.ini b/tests/wpt/metadata-layout-2020/css/cssom/serialize-variable-reference.html.ini deleted file mode 100644 index 0441ef458d3..00000000000 --- a/tests/wpt/metadata-layout-2020/css/cssom/serialize-variable-reference.html.ini +++ /dev/null @@ -1,6 +0,0 @@ -[serialize-variable-reference.html] - [Longhand with variable preserves original serialization but trims whitespace: without whitespace] - expected: FAIL - - [Shorthand with variable preserves original serialization but trims whitespace: without whitespace] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-backgrounds/background-position-calc-minmax-001.html.ini b/tests/wpt/metadata/css/css-backgrounds/background-position-calc-minmax-001.html.ini deleted file mode 100644 index ac3423de86d..00000000000 --- a/tests/wpt/metadata/css/css-backgrounds/background-position-calc-minmax-001.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[background-position-calc-minmax-001.html] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-backgrounds/background-position-negative-percentage-comparison.html.ini b/tests/wpt/metadata/css/css-backgrounds/background-position-negative-percentage-comparison.html.ini deleted file mode 100644 index 97f5bd10ffd..00000000000 --- a/tests/wpt/metadata/css/css-backgrounds/background-position-negative-percentage-comparison.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[background-position-negative-percentage-comparison.html] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-transforms/animation/transform-interpolation-001.html.ini b/tests/wpt/metadata/css/css-transforms/animation/transform-interpolation-001.html.ini index 4d012f732e9..0b468b702d1 100644 --- a/tests/wpt/metadata/css/css-transforms/animation/transform-interpolation-001.html.ini +++ b/tests/wpt/metadata/css/css-transforms/animation/transform-interpolation-001.html.ini @@ -929,51 +929,6 @@ [Web Animations: property from [scaleZ(2)\] to [scaleZ(2) perspective(500px)\] at (2) should be [scaleZ(2) perspective(250px)\]] expected: FAIL - [CSS Transitions: property from [perspective(none)\] to [perspective(500px)\] at (-1) should be [perspective(none)\]] - expected: FAIL - - [CSS Transitions: property from [perspective(none)\] to [perspective(500px)\] at (0) should be [perspective(none)\]] - expected: FAIL - - [CSS Transitions: property from [perspective(none)\] to [perspective(500px)\] at (0.5) should be [perspective(1000px)\]] - expected: FAIL - - [CSS Transitions: property from [perspective(none)\] to [perspective(500px)\] at (1) should be [perspective(500px)\]] - expected: FAIL - - [CSS Transitions: property from [perspective(none)\] to [perspective(500px)\] at (2) should be [perspective(250px)\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [perspective(none)\] to [perspective(500px)\] at (-1) should be [perspective(none)\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [perspective(none)\] to [perspective(500px)\] at (0) should be [perspective(none)\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [perspective(none)\] to [perspective(500px)\] at (0.5) should be [perspective(1000px)\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [perspective(none)\] to [perspective(500px)\] at (1) should be [perspective(500px)\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [perspective(none)\] to [perspective(500px)\] at (2) should be [perspective(250px)\]] - expected: FAIL - - [CSS Animations: property from [perspective(none)\] to [perspective(500px)\] at (-1) should be [perspective(none)\]] - expected: FAIL - - [CSS Animations: property from [perspective(none)\] to [perspective(500px)\] at (0) should be [perspective(none)\]] - expected: FAIL - - [CSS Animations: property from [perspective(none)\] to [perspective(500px)\] at (0.5) should be [perspective(1000px)\]] - expected: FAIL - - [CSS Animations: property from [perspective(none)\] to [perspective(500px)\] at (1) should be [perspective(500px)\]] - expected: FAIL - - [CSS Animations: property from [perspective(none)\] to [perspective(500px)\] at (2) should be [perspective(250px)\]] - expected: FAIL - [Web Animations: property from [perspective(none)\] to [perspective(500px)\] at (-1) should be [perspective(none)\]] expected: FAIL diff --git a/tests/wpt/metadata/css/css-transforms/animation/transform-interpolation-verify-reftests.html.ini b/tests/wpt/metadata/css/css-transforms/animation/transform-interpolation-verify-reftests.html.ini index 0f6e1f8159e..926da2b159f 100644 --- a/tests/wpt/metadata/css/css-transforms/animation/transform-interpolation-verify-reftests.html.ini +++ b/tests/wpt/metadata/css/css-transforms/animation/transform-interpolation-verify-reftests.html.ini @@ -203,39 +203,12 @@ [Web Animations: property from [matrix(1,0,0,1,0,0) rotate(0deg)\] to [matrix(2,0,0,2,0,0) rotate(360deg)\] at (0.5) should be [matrix(1.5,0,0,1.5,0,0) rotate(180deg)\]] expected: FAIL - [CSS Transitions: property from [perspective(none) translateZ(15px)\] to [perspective(none) translateZ(15px)\] at (0.5) should be [perspective(none) translateZ(15px)\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [perspective(none) translateZ(15px)\] to [perspective(none) translateZ(15px)\] at (0.5) should be [perspective(none) translateZ(15px)\]] - expected: FAIL - - [CSS Animations: property from [perspective(none) translateZ(15px)\] to [perspective(none) translateZ(15px)\] at (0.5) should be [perspective(none) translateZ(15px)\]] - expected: FAIL - [Web Animations: property from [perspective(none) translateZ(15px)\] to [perspective(none) translateZ(15px)\] at (0.5) should be [perspective(none) translateZ(15px)\]] expected: FAIL - [CSS Transitions: property from [perspective(100px) translateZ(50px)\] to [perspective(none) translateZ(50px)\] at (0.5) should be [perspective(200px) translateZ(50px)\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [perspective(100px) translateZ(50px)\] to [perspective(none) translateZ(50px)\] at (0.5) should be [perspective(200px) translateZ(50px)\]] - expected: FAIL - - [CSS Animations: property from [perspective(100px) translateZ(50px)\] to [perspective(none) translateZ(50px)\] at (0.5) should be [perspective(200px) translateZ(50px)\]] - expected: FAIL - [Web Animations: property from [perspective(100px) translateZ(50px)\] to [perspective(none) translateZ(50px)\] at (0.5) should be [perspective(200px) translateZ(50px)\]] expected: FAIL - [CSS Transitions: property from [perspective(none) translateZ(15px)\] to [perspective(25px) translateZ(15px)\] at (0.5) should be [perspective(50px) translateZ(15px)\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [perspective(none) translateZ(15px)\] to [perspective(25px) translateZ(15px)\] at (0.5) should be [perspective(50px) translateZ(15px)\]] - expected: FAIL - - [CSS Animations: property from [perspective(none) translateZ(15px)\] to [perspective(25px) translateZ(15px)\] at (0.5) should be [perspective(50px) translateZ(15px)\]] - expected: FAIL - [Web Animations: property from [perspective(none) translateZ(15px)\] to [perspective(25px) translateZ(15px)\] at (0.5) should be [perspective(50px) translateZ(15px)\]] expected: FAIL @@ -248,9 +221,6 @@ [CSS Transitions with transition: all: property from [perspective(0.1px) translateZ(0.25px)\] to [perspective(0.1px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]] expected: FAIL - [CSS Animations: property from [perspective(0.1px) translateZ(0.25px)\] to [perspective(0.1px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]] - expected: FAIL - [Web Animations: property from [perspective(0.1px) translateZ(0.25px)\] to [perspective(0.1px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]] expected: FAIL @@ -260,21 +230,9 @@ [CSS Transitions with transition: all: property from [perspective(0px) translateZ(0.25px)\] to [perspective(0px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]] expected: FAIL - [CSS Animations: property from [perspective(0px) translateZ(0.25px)\] to [perspective(0px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]] - expected: FAIL - [Web Animations: property from [perspective(0px) translateZ(0.25px)\] to [perspective(0px) translateZ(0.25px)\] at (0.5) should be [perspective(1px) translateZ(0.25px)\]] expected: FAIL - [CSS Transitions: property from [perspective(0px) translateZ(0.5px)\] to [perspective(3px) translateZ(0.5px)\] at (0.5) should be [perspective(1.5px) translateZ(0.5px)\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [perspective(0px) translateZ(0.5px)\] to [perspective(3px) translateZ(0.5px)\] at (0.5) should be [perspective(1.5px) translateZ(0.5px)\]] - expected: FAIL - - [CSS Animations: property from [perspective(0px) translateZ(0.5px)\] to [perspective(3px) translateZ(0.5px)\] at (0.5) should be [perspective(1.5px) translateZ(0.5px)\]] - expected: FAIL - [Web Animations: property from [perspective(0px) translateZ(0.5px)\] to [perspective(3px) translateZ(0.5px)\] at (0.5) should be [perspective(1.5px) translateZ(0.5px)\]] expected: FAIL @@ -290,14 +248,5 @@ [Web Animations: property from [perspective(10px) translateZ(0.5px)\] to [perspective(1px) translateZ(0.5px)\] at (-1) should be [translateZ(0.5px)\]] expected: FAIL - [CSS Transitions: property from [perspective(1px) translateZ(0.5px)\] to [perspective(10px) translateZ(0.5px)\] at (-1) should be [perspective(1px) translateZ(0.5px)\]] - expected: FAIL - - [CSS Transitions with transition: all: property from [perspective(1px) translateZ(0.5px)\] to [perspective(10px) translateZ(0.5px)\] at (-1) should be [perspective(1px) translateZ(0.5px)\]] - expected: FAIL - - [CSS Animations: property from [perspective(1px) translateZ(0.5px)\] to [perspective(10px) translateZ(0.5px)\] at (-1) should be [perspective(1px) translateZ(0.5px)\]] - expected: FAIL - [Web Animations: property from [perspective(1px) translateZ(0.5px)\] to [perspective(10px) translateZ(0.5px)\] at (-1) should be [perspective(1px) translateZ(0.5px)\]] expected: FAIL diff --git a/tests/wpt/metadata/css/css-transforms/parsing/transform-valid.html.ini b/tests/wpt/metadata/css/css-transforms/parsing/transform-valid.html.ini index f3a04d4e445..d79492e1f8b 100644 --- a/tests/wpt/metadata/css/css-transforms/parsing/transform-valid.html.ini +++ b/tests/wpt/metadata/css/css-transforms/parsing/transform-valid.html.ini @@ -11,8 +11,5 @@ [e.style['transform'\] = "scaleX(2e80)" should set the property value] expected: FAIL - [e.style['transform'\] = "perspective(none)" should set the property value] - expected: FAIL - [e.style['transform'\] = "translate(1px, 0%)" should set the property value] expected: FAIL diff --git a/tests/wpt/metadata/css/css-transforms/perspective-zero-2.html.ini b/tests/wpt/metadata/css/css-transforms/perspective-zero-2.html.ini deleted file mode 100644 index c3150604a8a..00000000000 --- a/tests/wpt/metadata/css/css-transforms/perspective-zero-2.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[perspective-zero-2.html] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-transforms/perspective-zero-3.html.ini b/tests/wpt/metadata/css/css-transforms/perspective-zero-3.html.ini deleted file mode 100644 index b2860a8900b..00000000000 --- a/tests/wpt/metadata/css/css-transforms/perspective-zero-3.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[perspective-zero-3.html] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-transforms/perspective-zero.html.ini b/tests/wpt/metadata/css/css-transforms/perspective-zero.html.ini deleted file mode 100644 index 66e93c50082..00000000000 --- a/tests/wpt/metadata/css/css-transforms/perspective-zero.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[perspective-zero.html] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-transforms/transform3d-perspective-005.html.ini b/tests/wpt/metadata/css/css-transforms/transform3d-perspective-005.html.ini deleted file mode 100644 index bb87827bac7..00000000000 --- a/tests/wpt/metadata/css/css-transforms/transform3d-perspective-005.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[transform3d-perspective-005.html] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata/css/css-values/minmax-percentage-serialize.html.ini b/tests/wpt/metadata/css/css-values/minmax-percentage-serialize.html.ini index fe7a776c7c8..961fe9f21c2 100644 --- a/tests/wpt/metadata/css/css-values/minmax-percentage-serialize.html.ini +++ b/tests/wpt/metadata/css/css-values/minmax-percentage-serialize.html.ini @@ -35,38 +35,5 @@ [e.style['margin-left'\] = "min(3%, 2%, 1%)" should set the property value] expected: FAIL - ['max(1%, 2%, 3%)' as a computed value should serialize as 'max(1%, 2%, 3%)'.] - expected: FAIL - - ['min(3%, 2%, 1%)' as a specified value should serialize as 'min(3%, 2%, 1%)'.] - expected: FAIL - - ['max(3%, 2%, 1%)' as a specified value should serialize as 'max(3%, 2%, 1%)'.] - expected: FAIL - - ['min(1%, 2%, 3%)' as a computed value should serialize as 'min(1%, 2%, 3%)'.] - expected: FAIL - - ['min(1%, 2%, 3%)' as a specified value should serialize as 'min(1%, 2%, 3%)'.] - expected: FAIL - - ['max(3%, 2%, 1%)' as a computed value should serialize as 'max(3%, 2%, 1%)'.] - expected: FAIL - - ['min(3%, 2%, 1%)' as a computed value should serialize as 'min(3%, 2%, 1%)'.] - expected: FAIL - - ['max(1%, 2%, 3%)' as a specified value should serialize as 'max(1%, 2%, 3%)'.] - expected: FAIL - - ['min(1%, 2%, 3%) 0px' as a specified value should serialize as 'min(1%, 2%, 3%) 0px'.] - expected: FAIL - ['min(1%, 2%, 3%) 0px' as a computed value should serialize as 'min(1%, 2%, 3%) 0px'.] expected: FAIL - - ['calc(min(1%, 2%) + max(3%, 4%) + 10%)' as a specified value should serialize as 'calc(10% + min(1%, 2%) + max(3%, 4%))'.] - expected: FAIL - - ['calc(min(1%, 2%) + max(3%, 4%) + 10%)' as a computed value should serialize as 'calc(10% + min(1%, 2%) + max(3%, 4%))'.] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-variables/variable-cssText.html.ini b/tests/wpt/metadata/css/css-variables/variable-cssText.html.ini index 21cab20fc4f..3b7b425e100 100644 --- a/tests/wpt/metadata/css/css-variables/variable-cssText.html.ini +++ b/tests/wpt/metadata/css/css-variables/variable-cssText.html.ini @@ -14,14 +14,8 @@ [target6] expected: FAIL - [target9] - expected: FAIL - [target4] expected: FAIL [target5] expected: FAIL - - [target11] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-variables/variable-declaration-07.html.ini b/tests/wpt/metadata/css/css-variables/variable-declaration-07.html.ini deleted file mode 100644 index 7255d7fc46e..00000000000 --- a/tests/wpt/metadata/css/css-variables/variable-declaration-07.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-declaration-07.html] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-variables/variable-declaration-09.html.ini b/tests/wpt/metadata/css/css-variables/variable-declaration-09.html.ini deleted file mode 100644 index 177674de03e..00000000000 --- a/tests/wpt/metadata/css/css-variables/variable-declaration-09.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-declaration-09.html] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-variables/variable-declaration-29.html.ini b/tests/wpt/metadata/css/css-variables/variable-declaration-29.html.ini deleted file mode 100644 index 815d162d51a..00000000000 --- a/tests/wpt/metadata/css/css-variables/variable-declaration-29.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-declaration-29.html] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-variables/variable-definition.html.ini b/tests/wpt/metadata/css/css-variables/variable-definition.html.ini index a3daa11079d..7d3d2c0fa5e 100644 --- a/tests/wpt/metadata/css/css-variables/variable-definition.html.ini +++ b/tests/wpt/metadata/css/css-variables/variable-definition.html.ini @@ -5,78 +5,15 @@ [CSSOM.setProperty with space 2] expected: FAIL - [no char variable] - expected: FAIL - - [white space value (single space)] - expected: FAIL - - [white space value (double space)] - expected: FAIL - [can't overwrite with no value] expected: FAIL - [can overwrite with space value] - expected: FAIL - - [ leading white space (single space)] - expected: FAIL - - [ leading white space (double space) 2] - expected: FAIL - - [no char variable (Computed Style)] - expected: FAIL - - [white space value (single space) (Computed Style)] - expected: FAIL - - [white space value (double space) (Computed Style)] - expected: FAIL - [can't overwrite with no value (Computed Style)] expected: FAIL - [can overwrite with space value (Computed Style)] - expected: FAIL - - [ leading white space (single space) (Computed Style)] - expected: FAIL - - [ leading white space (double space) 2 (Computed Style)] - expected: FAIL - - [no char variable (Cascading)] - expected: FAIL - - [white space value (single space) (Cascading)] - expected: FAIL - - [white space value (double space) (Cascading)] - expected: FAIL - [can't overwrite with no value (Cascading)] expected: FAIL - [can overwrite with space value (Cascading)] - expected: FAIL - - [ leading white space (single space) (Cascading)] - expected: FAIL - - [ leading white space (double space) 2 (Cascading)] - expected: FAIL - - [can overwrite with no value] - expected: FAIL - - [can overwrite with no value (Computed Style)] - expected: FAIL - - [can overwrite with no value (Cascading)] - expected: FAIL - [ trailing white space (single space)] expected: FAIL diff --git a/tests/wpt/metadata/css/css-variables/variable-empty-name-reserved.html.ini b/tests/wpt/metadata/css/css-variables/variable-empty-name-reserved.html.ini deleted file mode 100644 index 709cf140b1f..00000000000 --- a/tests/wpt/metadata/css/css-variables/variable-empty-name-reserved.html.ini +++ /dev/null @@ -1,3 +0,0 @@ -[variable-empty-name-reserved.html] - [-- is a reserved property name] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-variables/variable-reference-06.html.ini b/tests/wpt/metadata/css/css-variables/variable-reference-06.html.ini deleted file mode 100644 index cd8687ce1d1..00000000000 --- a/tests/wpt/metadata/css/css-variables/variable-reference-06.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-reference-06.html] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-variables/variable-reference-11.html.ini b/tests/wpt/metadata/css/css-variables/variable-reference-11.html.ini deleted file mode 100644 index d51bc156ae2..00000000000 --- a/tests/wpt/metadata/css/css-variables/variable-reference-11.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-reference-11.html] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-variables/variable-reference.html.ini b/tests/wpt/metadata/css/css-variables/variable-reference.html.ini index b7248b8df50..193b9f252c9 100644 --- a/tests/wpt/metadata/css/css-variables/variable-reference.html.ini +++ b/tests/wpt/metadata/css/css-variables/variable-reference.html.ini @@ -1,6 +1,3 @@ [variable-reference.html] [Variable reference left open at end of stylesheet] expected: FAIL - - [width: var(--prop,);] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-variables/variable-substitution-variable-declaration.html.ini b/tests/wpt/metadata/css/css-variables/variable-substitution-variable-declaration.html.ini index 7e95ee1dde0..60c36462bdc 100644 --- a/tests/wpt/metadata/css/css-variables/variable-substitution-variable-declaration.html.ini +++ b/tests/wpt/metadata/css/css-variables/variable-substitution-variable-declaration.html.ini @@ -1,24 +1,3 @@ [variable-substitution-variable-declaration.html] [target10 --varC] expected: FAIL - - [target1 --var2] - expected: FAIL - - [target2 --var1] - expected: FAIL - - [target3 --var1] - expected: FAIL - - [target3 --var2] - expected: FAIL - - [target4 --varC] - expected: FAIL - - [target8 --varB] - expected: FAIL - - [target9 --varA] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-variables/variable-supports-05.html.ini b/tests/wpt/metadata/css/css-variables/variable-supports-05.html.ini deleted file mode 100644 index 4d0f1862a6c..00000000000 --- a/tests/wpt/metadata/css/css-variables/variable-supports-05.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-supports-05.html] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-variables/variable-supports-07.html.ini b/tests/wpt/metadata/css/css-variables/variable-supports-07.html.ini deleted file mode 100644 index 69995c11cbe..00000000000 --- a/tests/wpt/metadata/css/css-variables/variable-supports-07.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-supports-07.html] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-variables/variable-supports-37.html.ini b/tests/wpt/metadata/css/css-variables/variable-supports-37.html.ini deleted file mode 100644 index c9b4e3a1ea4..00000000000 --- a/tests/wpt/metadata/css/css-variables/variable-supports-37.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-supports-37.html] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-variables/variable-supports-39.html.ini b/tests/wpt/metadata/css/css-variables/variable-supports-39.html.ini deleted file mode 100644 index 11fc7d6d05c..00000000000 --- a/tests/wpt/metadata/css/css-variables/variable-supports-39.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-supports-39.html] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-variables/variable-supports-57.html.ini b/tests/wpt/metadata/css/css-variables/variable-supports-57.html.ini deleted file mode 100644 index 676c3da63fa..00000000000 --- a/tests/wpt/metadata/css/css-variables/variable-supports-57.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-supports-57.html] - expected: FAIL diff --git a/tests/wpt/metadata/css/css-variables/variable-supports-58.html.ini b/tests/wpt/metadata/css/css-variables/variable-supports-58.html.ini deleted file mode 100644 index ebf7e5d65d9..00000000000 --- a/tests/wpt/metadata/css/css-variables/variable-supports-58.html.ini +++ /dev/null @@ -1,2 +0,0 @@ -[variable-supports-58.html] - expected: FAIL diff --git a/tests/wpt/metadata/css/cssom/serialize-variable-reference.html.ini b/tests/wpt/metadata/css/cssom/serialize-variable-reference.html.ini deleted file mode 100644 index 0441ef458d3..00000000000 --- a/tests/wpt/metadata/css/cssom/serialize-variable-reference.html.ini +++ /dev/null @@ -1,6 +0,0 @@ -[serialize-variable-reference.html] - [Longhand with variable preserves original serialization but trims whitespace: without whitespace] - expected: FAIL - - [Shorthand with variable preserves original serialization but trims whitespace: without whitespace] - expected: FAIL diff --git a/tests/wpt/mozilla/meta-layout-2020/css/test_variable_serialization_computed.html.ini b/tests/wpt/mozilla/meta-layout-2020/css/test_variable_serialization_computed.html.ini index 379a8dea72f..92035e18fb2 100644 --- a/tests/wpt/mozilla/meta-layout-2020/css/test_variable_serialization_computed.html.ini +++ b/tests/wpt/mozilla/meta-layout-2020/css/test_variable_serialization_computed.html.ini @@ -1,3 +1,3 @@ [test_variable_serialization_computed.html] - [subtest #23 with `counter-reset: var(--a)red; --a:orange;`] + [subtest #29 with `counter-reset: var(--a)red; --a:orange;`] expected: FAIL diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 2b08028733d..f8593976dd6 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -12624,7 +12624,7 @@ ] ], "test_variable_serialization_computed.html": [ - "a7f0b63d1c415f2fb49badcedc780f26f6bd3058", + "4b051b2462face35b8a7f982f1826248901ca867", [ null, {} diff --git a/tests/wpt/mozilla/tests/css/test_variable_serialization_computed.html b/tests/wpt/mozilla/tests/css/test_variable_serialization_computed.html index a7f0b63d1c4..4b051b2462f 100644 --- a/tests/wpt/mozilla/tests/css/test_variable_serialization_computed.html +++ b/tests/wpt/mozilla/tests/css/test_variable_serialization_computed.html @@ -22,43 +22,47 @@ so an implementation could fail this test but still be conforming. // its expected computed value. var values = [ ["", "--z", "an-inherited-value"], - ["--a: ", "--a", " "], + ["--a: ", "--a", ""], ["--a: initial", "--a", ""], ["--z: initial", "--z", ""], ["--a: inherit", "--a", ""], ["--z: inherit", "--z", "an-inherited-value"], ["--a: unset", "--a", ""], ["--z: unset", "--z", "an-inherited-value"], - ["--a: 1px", "--a", " 1px"], + ["--a: 1px", "--a", "1px"], ["--a: var(--a)", "--a", ""], ["--a: var(--b)", "--a", ""], - ["--a: var(--b); --b: 1px", "--a", " 1px"], - ["--a: var(--b, 1px)", "--a", " 1px"], + ["--a: var(--b); --b: 1px", "--a", "1px"], + ["--a: var(--b, 1px)", "--a", "1px"], ["--a: var(--a, 1px)", "--a", ""], ["--a: something 3px url(whereever) calc(var(--a) + 1px)", "--a", ""], - ["--a: something 3px url(whereever) calc(var(--b,1em) + 1px)", "--a", " something 3px url(whereever) calc(1em + 1px)"], - ["--a: var(--b, var(--c, var(--d, Black)))", "--a", " Black"], - ["--a: a var(--b) c; --b:b", "--a", " a b c"], - ["--a: a var(--b,b var(--c) d) e; --c:c", "--a", " a b c d e"], - ["--a: var(--b)red; --b:orange;", "--a", " orange/**/red"], - ["--a: var(--b)var(--c); --b:orange; --c:red;", "--a", " orange/**/red"], - ["--a: var(--b)var(--c,red); --b:orange;", "--a", " orange/**/red"], - ["--a: var(--b,orange)var(--c); --c:red;", "--a", " orange/**/red"], + ["--a: something 3px url(whereever) calc(var(--b,1em) + 1px)", "--a", "something 3px url(whereever) calc(1em + 1px)"], + ["--a: var(--b, var(--c, var(--d, Black)))", "--a", "Black"], + ["--a: a var(--b) c; --b:b", "--a", "a b c"], + ["--a: a var(--b,b var(--c) d) e; --c:c", "--a", "a b c d e"], + ["--a: var(--b)red; --b:orange;", "--a", "orange/**/red"], + ["--a: var(--b)var(--c); --b:orange; --c:red;", "--a", "orange/**/red"], + ["--a: var(--b)var(--c,red); --b:orange;", "--a", "orange/**/red"], + ["--a: var(--b,orange)var(--c); --c:red;", "--a", "orange/**/red"], + ["--a: var(--b)-; --b:-;", "--a", "-/**/-"], + ["--a: var(--b)--; --b:-;", "--a", "-/**/--"], + ["--a: var(--b)--x; --b:-;", "--a", "-/**/--x"], + ["--a: var(--b)var(--c); --b:-; --c:-;", "--a", "-/**/-"], + ["--a: var(--b)var(--c); --b:--; --c:-;", "--a", "--/**/-"], + ["--a: var(--b)var(--c); --b:--x; --c:-;", "--a", "--x/**/-"], ["counter-reset: var(--a)red; --a:orange;", "counter-reset", "orange 0 red 0"], - ["--a: var(--b)var(--c); --c:[c]; --b:('ab", "--a", " ('ab')[c]"], - ["--a: '", "--a", " ''"], - ["--a: '\\", "--a", " ''"], - ["--a: \\", "--a", " \\\ufffd"], - ["--a: \"", "--a", " \"\""], - ["--a: \"\\", "--a", " \"\""], - ["--a: /* abc ", "--a", " /* abc */"], - ["--a: /* abc *", "--a", " /* abc */"], - ["--a: url(http://example.org/", "--a", " url(http://example.org/)"], - ["--a: url(http://example.org/\\", "--a", " url(http://example.org/\\\ufffd)"], - ["--a: url('http://example.org/", "--a", " url('http://example.org/')"], - ["--a: url('http://example.org/\\", "--a", " url('http://example.org/')"], - ["--a: url(\"http://example.org/", "--a", " url(\"http://example.org/\")"], - ["--a: url(\"http://example.org/\\", "--a", " url(\"http://example.org/\")"] + ["--a: var(--b)var(--c); --c:[c]; --b:('ab", "--a", "('ab')[c]"], + ["--a: '", "--a", "''"], + ["--a: '\\", "--a", "''"], + ["--a: \\", "--a", "\\\ufffd"], + ["--a: \"", "--a", "\"\""], + ["--a: \"\\", "--a", "\"\""], + ["--a: url(http://example.org/", "--a", "url(http://example.org/)"], + ["--a: url(http://example.org/\\", "--a", "url(http://example.org/\\\ufffd)"], + ["--a: url('http://example.org/", "--a", "url('http://example.org/')"], + ["--a: url('http://example.org/\\", "--a", "url('http://example.org/')"], + ["--a: url(\"http://example.org/", "--a", "url(\"http://example.org/\")"], + ["--a: url(\"http://example.org/\\", "--a", "url(\"http://example.org/\")"] ]; var div = document.querySelector("div");