From dfed17bd04a713f5dce775176c3a28c39c934970 Mon Sep 17 00:00:00 2001 From: Delan Azabani Date: Thu, 15 Feb 2024 18:30:14 +0800 Subject: [PATCH] Commit our changes on top of upstream Stylo This is a rebase of fbd6a1448bb699000ae18067ea1b455e4cce7c97 Signed-off-by: Oriol Brufau --- .gitignore | 1 + Cargo.toml | 16 + atoms/Cargo.toml | 17 + atoms/build.rs | 31 ++ atoms/lib.rs | 5 + atoms/static_atoms.txt | 179 +++++++++ dom/Cargo.toml | 14 + dom/lib.rs | 164 ++++++++ malloc_size_of/Cargo.toml | 4 +- selectors/tree.rs | 5 + style/Cargo.toml | 20 +- style/README.md | 2 +- style/animation.rs | 10 + style/build.rs | 2 +- style/context.rs | 4 +- style/encoding_support.rs | 2 - style/font_face.rs | 12 - style/gecko/media_features.rs | 11 +- style/global_style_data.rs | 2 +- .../invalidation/element/invalidation_map.rs | 2 +- .../invalidation/element/relative_selector.rs | 2 + style/lib.rs | 1 + style/matching.rs | 13 +- .../Mako-1.1.2-py2.py3-none-any.whl | Bin 0 -> 75521 bytes style/properties/build.py | 8 +- style/properties/cascade.rs | 58 +-- style/properties/declaration_block.rs | 2 +- style/properties/helpers.mako.rs | 2 +- .../helpers/animated_properties.mako.rs | 2 +- style/properties/longhands/counters.mako.rs | 2 + .../longhands/inherited_text.mako.rs | 2 + style/properties/longhands/list.mako.rs | 1 + style/properties/longhands/text.mako.rs | 1 + style/properties/mod.rs | 15 +- style/properties/properties.mako.rs | 170 ++++---- style/properties/shorthands/font.mako.rs | 6 +- style/properties_and_values/registry.rs | 1 + style/queries/values.rs | 9 + style/servo/media_queries.rs | 270 +++++++++++-- style/servo/restyle_damage.rs | 4 +- style/servo/selector_parser.rs | 374 +++++++++++++----- style/str.rs | 8 + style/stylesheets/import_rule.rs | 42 +- style/stylesheets/mod.rs | 4 +- style/stylesheets/position_try_rule.rs | 1 + style/stylesheets/stylesheet.rs | 14 +- style/stylist.rs | 13 +- style/values/animated/effects.rs | 2 +- style/values/computed/effects.rs | 2 +- style/values/computed/font.rs | 2 +- style/values/computed/image.rs | 2 +- style/values/computed/length.rs | 54 ++- style/values/computed/length_percentage.rs | 9 +- style/values/generics/image.rs | 4 +- style/values/generics/length.rs | 40 +- style/values/generics/position.rs | 7 + style/values/generics/transform.rs | 7 + style/values/mod.rs | 12 +- style/values/specified/animation.rs | 5 +- style/values/specified/box.rs | 10 +- style/values/specified/effects.rs | 2 +- style/values/specified/font.rs | 39 +- style/values/specified/image.rs | 6 +- style/values/specified/length.rs | 13 +- style/values/specified/list.rs | 2 + style/values/specified/text.rs | 20 +- style_config/Cargo.toml | 14 + style_config/lib.rs | 95 +++++ style_static_prefs/Cargo.toml | 7 + style_static_prefs/src/lib.rs | 39 ++ style_traits/Cargo.toml | 5 +- style_traits/lib.rs | 4 +- style_traits/values.rs | 1 + 73 files changed, 1528 insertions(+), 403 deletions(-) create mode 100644 Cargo.toml create mode 100644 atoms/Cargo.toml create mode 100644 atoms/build.rs create mode 100644 atoms/lib.rs create mode 100644 atoms/static_atoms.txt create mode 100644 dom/Cargo.toml create mode 100644 dom/lib.rs create mode 100644 style/properties/Mako-1.1.2-py2.py3-none-any.whl create mode 100644 style_config/Cargo.toml create mode 100644 style_config/lib.rs create mode 100644 style_static_prefs/Cargo.toml create mode 100644 style_static_prefs/src/lib.rs diff --git a/.gitignore b/.gitignore index 52d314bb6a..fc3c2f9b3c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /_filtered/ /target/ /style/properties/__pycache__/ +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000000..72d5501272 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,16 @@ +[workspace] +resolver = "2" +members = [ + "dom", + "malloc_size_of", + "rustfmt.toml", + "selectors", + "servo_arc", + "style", + "style_derive", + "style_static_prefs", + "style_traits", + "to_shmem", + "to_shmem_derive", +] +default-members = ["style"] diff --git a/atoms/Cargo.toml b/atoms/Cargo.toml new file mode 100644 index 0000000000..aa232e86e5 --- /dev/null +++ b/atoms/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "servo_atoms" +version = "0.0.1" +authors = ["The Servo Project Developers"] +license = "MPL-2.0" +edition = "2018" +publish = false +build = "build.rs" + +[lib] +path = "lib.rs" + +[dependencies] +string_cache = "0.8" + +[build-dependencies] +string_cache_codegen = "0.5" diff --git a/atoms/build.rs b/atoms/build.rs new file mode 100644 index 0000000000..6bd2de3703 --- /dev/null +++ b/atoms/build.rs @@ -0,0 +1,31 @@ +/* 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/. */ + +use std::env; +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::path::Path; + +fn main() { + let static_atoms = + Path::new(&env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("static_atoms.txt"); + let static_atoms = BufReader::new(File::open(&static_atoms).unwrap()); + let mut atom_type = string_cache_codegen::AtomType::new("Atom", "atom!"); + + macro_rules! predefined { + ($($name: expr,)+) => { + { + $( + atom_type.atom($name); + )+ + } + } + } + include!("../style/counter_style/predefined.rs"); + + atom_type + .atoms(static_atoms.lines().map(Result::unwrap)) + .write_to_file(&Path::new(&env::var_os("OUT_DIR").unwrap()).join("atom.rs")) + .unwrap(); +} diff --git a/atoms/lib.rs b/atoms/lib.rs new file mode 100644 index 0000000000..03560a40c0 --- /dev/null +++ b/atoms/lib.rs @@ -0,0 +1,5 @@ +/* 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/. */ + +include!(concat!(env!("OUT_DIR"), "/atom.rs")); diff --git a/atoms/static_atoms.txt b/atoms/static_atoms.txt new file mode 100644 index 0000000000..d658fcf1c6 --- /dev/null +++ b/atoms/static_atoms.txt @@ -0,0 +1,179 @@ +-moz-content-preferred-color-scheme +-moz-device-pixel-ratio +-moz-fixed-pos-containing-block +-moz-gtk-csd-close-button-position +-moz-gtk-csd-maximize-button-position +-moz-gtk-csd-menu-radius +-moz-gtk-csd-minimize-button-position +-moz-gtk-csd-titlebar-button-spacing +-moz-gtk-csd-titlebar-radius +-moz-gtk-menu-radius +-moz-mac-titlebar-height +-moz-overlay-scrollbar-fade-duration +DOMContentLoaded +abort +activate +addtrack +animationcancel +animationend +animationiteration +animationstart +aspect-ratio +beforeunload +block-size +button +canplay +canplaythrough +center +change +characteristicvaluechanged +checkbox +click +close +closing +color +complete +compositionend +compositionstart +compositionupdate +controllerchange +cursive +dark +datachannel +date +datetime-local +dir +device-pixel-ratio +durationchange +email +emptied +end +ended +error +fantasy +fetch +file +fill +fill-opacity +formdata +fullscreenchange +fullscreenerror +gattserverdisconnected +hashchange +height +hidden +icecandidate +iceconnectionstatechange +icegatheringstatechange +image +inline-size +input +inputsourceschange +invalid +keydown +keypress +kind +left +light +ltr +load +loadeddata +loadedmetadata +loadend +loadstart +message +message +messageerror +monospace +month +mousedown +mousemove +mouseover +mouseup +negotiationneeded +none +normal +number +onchange +open +orientation +pagehide +pageshow +password +pause +play +playing +popstate +postershown +prefers-color-scheme +print +progress +radio +range +ratechange +readystatechange +referrer +reftest-wait +rejectionhandled +removetrack +reset +resize +resolution +resourcetimingbufferfull +right +rtl +sans-serif +safe-area-inset-top +safe-area-inset-bottom +safe-area-inset-left +safe-area-inset-right +scan +screen +scroll-position +scrollbar-inline-size +search +seeked +seeking +select +selectend +selectionchange +selectstart +serif +sessionavailable +signalingstatechange +squeeze +squeezeend +squeezestart +srclang +statechange +stroke +stroke-opacity +storage +submit +suspend +system-ui +tel +text +time +timeupdate +toggle +track +transitioncancel +transitionend +transitionrun +transitionstart +uncapturederror +unhandledrejection +unload +url +visibilitychange +volumechange +waiting +webglcontextcreationerror +webkitAnimationEnd +webkitAnimationIteration +webkitAnimationStart +webkitTransitionEnd +webkitTransitionRun +week +width diff --git a/dom/Cargo.toml b/dom/Cargo.toml new file mode 100644 index 0000000000..9d678fa5df --- /dev/null +++ b/dom/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "dom" +version = "0.0.1" +authors = ["The Servo Project Developers"] +license = "MPL-2.0" +edition = "2021" +publish = false + +[lib] +path = "lib.rs" + +[dependencies] +bitflags = "2" +malloc_size_of = { path = "../malloc_size_of" } diff --git a/dom/lib.rs b/dom/lib.rs new file mode 100644 index 0000000000..9137763491 --- /dev/null +++ b/dom/lib.rs @@ -0,0 +1,164 @@ +/* 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/. */ + +use bitflags::bitflags; +use malloc_size_of::malloc_size_of_is_0; + +// DOM types to be shared between Rust and C++. +bitflags! { + /// Event-based element states. + #[repr(C)] + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct ElementState: u64 { + /// The mouse is down on this element. + /// + /// FIXME(#7333): set/unset this when appropriate + const ACTIVE = 1 << 0; + /// This element has focus. + /// + const FOCUS = 1 << 1; + /// The mouse is hovering over this element. + /// + const HOVER = 1 << 2; + /// Content is enabled (and can be disabled). + /// + const ENABLED = 1 << 3; + /// Content is disabled. + /// + const DISABLED = 1 << 4; + /// Content is checked. + /// + const CHECKED = 1 << 5; + /// + const INDETERMINATE = 1 << 6; + /// + const PLACEHOLDER_SHOWN = 1 << 7; + /// + const URLTARGET = 1 << 8; + /// + const FULLSCREEN = 1 << 9; + /// + const VALID = 1 << 10; + /// + const INVALID = 1 << 11; + /// + const USER_VALID = 1 << 12; + /// + const USER_INVALID = 1 << 13; + /// All the validity bits at once. + const VALIDITY_STATES = Self::VALID.bits() | Self::INVALID.bits() | Self::USER_VALID.bits() | Self::USER_INVALID.bits(); + /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-broken + const BROKEN = 1 << 14; + /// + const REQUIRED = 1 << 15; + /// + /// We use an underscore to workaround a silly windows.h define. + const OPTIONAL_ = 1 << 16; + /// + const DEFINED = 1 << 17; + /// + const VISITED = 1 << 18; + /// + const UNVISITED = 1 << 19; + /// + const VISITED_OR_UNVISITED = Self::VISITED.bits() | Self::UNVISITED.bits(); + /// Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-drag-over + const DRAGOVER = 1 << 20; + /// + const INRANGE = 1 << 21; + /// + const OUTOFRANGE = 1 << 22; + /// + const READONLY = 1 << 23; + /// + const READWRITE = 1 << 24; + /// + const DEFAULT = 1 << 25; + /// Non-standard & undocumented. + const OPTIMUM = 1 << 26; + /// Non-standard & undocumented. + const SUB_OPTIMUM = 1 << 27; + /// Non-standard & undocumented. + const SUB_SUB_OPTIMUM = 1 << 28; + /// All the above bits in one place. + const METER_OPTIMUM_STATES = Self::OPTIMUM.bits() | Self::SUB_OPTIMUM.bits() | Self::SUB_SUB_OPTIMUM.bits(); + /// Non-standard & undocumented. + const INCREMENT_SCRIPT_LEVEL = 1 << 29; + /// + const FOCUSRING = 1 << 30; + /// + const FOCUS_WITHIN = 1u64 << 31; + /// :dir matching; the states are used for dynamic change detection. + /// State that elements that match :dir(ltr) are in. + const LTR = 1u64 << 32; + /// State that elements that match :dir(rtl) are in. + const RTL = 1u64 << 33; + /// State that HTML elements that have a "dir" attr are in. + const HAS_DIR_ATTR = 1u64 << 34; + /// State that HTML elements with dir="ltr" (or something + /// case-insensitively equal to "ltr") are in. + const HAS_DIR_ATTR_LTR = 1u64 << 35; + /// State that HTML elements with dir="rtl" (or something + /// case-insensitively equal to "rtl") are in. + const HAS_DIR_ATTR_RTL = 1u64 << 36; + /// State that HTML elements without a valid-valued "dir" attr or + /// any HTML elements (including ) with dir="auto" (or something + /// case-insensitively equal to "auto") are in. + const HAS_DIR_ATTR_LIKE_AUTO = 1u64 << 37; + /// Non-standard & undocumented. + const AUTOFILL = 1u64 << 38; + /// Non-standard & undocumented. + const AUTOFILL_PREVIEW = 1u64 << 39; + /// State for modal elements: + /// + const MODAL = 1u64 << 40; + /// + const INERT = 1u64 << 41; + /// State for the topmost modal element in top layer + const TOPMOST_MODAL = 1u64 << 42; + /// Initially used for the devtools highlighter, but now somehow only + /// used for the devtools accessibility inspector. + const DEVTOOLS_HIGHLIGHTED = 1u64 << 43; + /// Used for the devtools style editor. Probably should go away. + const STYLEEDITOR_TRANSITIONING = 1u64 << 44; + /// For :-moz-value-empty (to show widgets like the reveal password + /// button or the clear button). + const VALUE_EMPTY = 1u64 << 45; + /// For :-moz-revealed. + const REVEALED = 1u64 << 46; + /// https://html.spec.whatwg.org/#selector-popover-open + /// Match element's popover visibility state of showing + const POPOVER_OPEN = 1u64 << 47; + + /// Some convenience unions. + const DIR_STATES = Self::LTR.bits() | Self::RTL.bits(); + + const DIR_ATTR_STATES = Self::HAS_DIR_ATTR.bits() | + Self::HAS_DIR_ATTR_LTR.bits() | + Self::HAS_DIR_ATTR_RTL.bits() | + Self::HAS_DIR_ATTR_LIKE_AUTO.bits(); + + const DISABLED_STATES = Self::DISABLED.bits() | Self::ENABLED.bits(); + + const REQUIRED_STATES = Self::REQUIRED.bits() | Self::OPTIONAL_.bits(); + } +} + +bitflags! { + /// Event-based document states. + #[repr(C)] + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub struct DocumentState: u64 { + /// Window activation status + const WINDOW_INACTIVE = 1 << 0; + /// RTL locale: specific to the XUL localedir attribute + const RTL_LOCALE = 1 << 1; + /// LTR locale: specific to the XUL localedir attribute + const LTR_LOCALE = 1 << 2; + + const ALL_LOCALEDIR_BITS = Self::LTR_LOCALE.bits() | Self::RTL_LOCALE.bits(); + } +} + +malloc_size_of_is_0!(ElementState, DocumentState); diff --git a/malloc_size_of/Cargo.toml b/malloc_size_of/Cargo.toml index eefd6cd89a..68defa0d61 100644 --- a/malloc_size_of/Cargo.toml +++ b/malloc_size_of/Cargo.toml @@ -20,7 +20,7 @@ euclid = "0.22" selectors = { path = "../selectors" } servo_arc = { path = "../servo_arc" } smallbitvec = "2.3.0" -smallvec = "1.0" +smallvec = "1.13" string_cache = { version = "0.8", optional = true } -thin-vec = { version = "0.2.1" } +thin-vec = { version = "0.2.13" } void = "1.0.2" diff --git a/selectors/tree.rs b/selectors/tree.rs index c4765ebb78..fa2df7a13e 100644 --- a/selectors/tree.rs +++ b/selectors/tree.rs @@ -31,6 +31,11 @@ impl OpaqueElement { } } + /// Creates a new OpaqueElement from a type-erased non-null pointer + pub fn from_non_null_ptr(ptr: NonNull<()>) -> Self { + Self(ptr) + } + /// Returns a const ptr to the contained reference. pub unsafe fn as_const_ptr(&self) -> *const T { self.0.as_ptr() as *const T diff --git a/style/Cargo.toml b/style/Cargo.toml index 58de5ba26a..d37a742e7e 100644 --- a/style/Cargo.toml +++ b/style/Cargo.toml @@ -31,15 +31,15 @@ gecko = [ "to_shmem/gecko", ] servo = [ - "arrayvec/use_union", "cssparser/serde", "encoding_rs", "malloc_size_of/servo", "markup5ever", + "mime", "serde", "servo_arc/servo", "servo_atoms", - "servo_config", + "style_config", "string_cache", "style_traits/servo", "url", @@ -48,6 +48,7 @@ servo = [ ] gecko_debug = [] gecko_refcount_logging = [] +nsstring = [] [dependencies] app_units = "0.7" @@ -57,7 +58,7 @@ bitflags = "2" byteorder = "1.0" cssparser = "0.34" derive_more = { version = "0.99", default-features = false, features = ["add", "add_assign", "deref", "deref_mut", "from"] } -dom = { path = "../../../dom/base/rust" } +dom = { path = "../dom" } new_debug_unreachable = "1.0" encoding_rs = {version = "0.8", optional = true} euclid = "0.22" @@ -69,10 +70,10 @@ itoa = "1.0" lazy_static = "1" log = "0.4" malloc_size_of = { path = "../malloc_size_of" } -malloc_size_of_derive = { path = "../../../xpcom/rust/malloc_size_of_derive" } -markup5ever = { version = "0.12", optional = true } +malloc_size_of_derive = "0.1" +markup5ever = { version = "0.14", optional = true } matches = "0.1" -nsstring = {path = "../../../xpcom/rust/nsstring/", optional = true} +mime = { version = "0.3.13", optional = true } num_cpus = {version = "1.1.0"} num-integer = "0.1" num-traits = "0.2" @@ -85,21 +86,20 @@ selectors = { path = "../selectors" } serde = {version = "1.0", optional = true, features = ["derive"]} servo_arc = { path = "../servo_arc" } servo_atoms = {path = "../atoms", optional = true} -servo_config = {path = "../config", optional = true} smallbitvec = "2.3.0" smallvec = "1.0" static_assertions = "1.1" -static_prefs = { path = "../../../modules/libpref/init/static_prefs" } +static_prefs = { path = "../style_static_prefs" } string_cache = { version = "0.8", optional = true } +style_config = { path = "../style_config", optional = true } style_derive = {path = "../style_derive"} style_traits = {path = "../style_traits"} to_shmem = {path = "../to_shmem"} to_shmem_derive = {path = "../to_shmem_derive"} -thin-vec = { version = "0.2.1", features = ["gecko-ffi"] } +thin-vec = "0.2.1" uluru = "3.0" unicode-bidi = { version = "0.3", default-features = false } void = "1.0.2" -gecko-profiler = { path = "../../../tools/profiler/rust-api" } url = { version = "2.5", optional = true, features = ["serde"] } [build-dependencies] diff --git a/style/README.md b/style/README.md index 96457e1b30..bdbe36e44c 100644 --- a/style/README.md +++ b/style/README.md @@ -3,4 +3,4 @@ servo-style Style system for Servo, using [rust-cssparser](https://github.com/servo/rust-cssparser) for parsing. - * [Documentation](https://github.com/servo/servo/blob/master/docs/components/style.md). + * [Documentation](https://github.com/servo/servo/blob/main/docs/components/style.md). diff --git a/style/animation.rs b/style/animation.rs index 29c98b0c7e..f90b9df81f 100644 --- a/style/animation.rs +++ b/style/animation.rs @@ -27,6 +27,7 @@ use crate::stylesheets::layer_rule::LayerOrder; use crate::values::animated::{Animate, Procedure}; use crate::values::computed::{Time, TimingFunction}; use crate::values::generics::easing::BeforeFlag; +use crate::values::specified::TransitionBehavior; use crate::Atom; use fxhash::FxHashMap; use parking_lot::RwLock; @@ -1030,6 +1031,15 @@ impl ElementAnimationSet { new_style: &Arc, ) { let style = new_style.get_ui(); + + #[cfg(feature = "servo")] + if !property_declaration_id.is_animatable() || + (style.transition_behavior_mod(index) != TransitionBehavior::AllowDiscrete && + property_declaration_id.is_discrete_animatable()) + { + return; + } + let timing_function = style.transition_timing_function_mod(index); let duration = style.transition_duration_mod(index); let delay = style.transition_delay_mod(index).seconds() as f64; diff --git a/style/build.rs b/style/build.rs index 4b27edbe2c..eacb9b3fc9 100644 --- a/style/build.rs +++ b/style/build.rs @@ -21,7 +21,7 @@ mod build_gecko { lazy_static! { pub static ref PYTHON: String = env::var("PYTHON3").ok().unwrap_or_else(|| { let candidates = if cfg!(windows) { - ["python3.exe"] + ["python.exe"] } else { ["python3"] }; diff --git a/style/context.rs b/style/context.rs index 1e4fcca15f..5337617689 100644 --- a/style/context.rs +++ b/style/context.rs @@ -475,7 +475,7 @@ impl SequentialTask { /// Executes this task. pub fn execute(self) { use self::SequentialTask::*; - debug_assert_eq!(thread_state::get(), ThreadState::LAYOUT); + debug_assert!(thread_state::get().contains(ThreadState::LAYOUT)); match self { Unused(_) => unreachable!(), #[cfg(feature = "gecko")] @@ -551,7 +551,7 @@ where E: TElement, { fn drop(&mut self) { - debug_assert_eq!(thread_state::get(), ThreadState::LAYOUT); + debug_assert!(thread_state::get().contains(ThreadState::LAYOUT)); for task in self.0.drain(..) { task.execute() } diff --git a/style/encoding_support.rs b/style/encoding_support.rs index c144ad0b3b..b07fccfd30 100644 --- a/style/encoding_support.rs +++ b/style/encoding_support.rs @@ -75,7 +75,6 @@ impl Stylesheet { stylesheet_loader, error_reporter, quirks_mode, - 0, AllowImportRules::Yes, ) } @@ -98,7 +97,6 @@ impl Stylesheet { url_data, stylesheet_loader, error_reporter, - 0, AllowImportRules::Yes, ) } diff --git a/style/font_face.rs b/style/font_face.rs index fd52874da8..9c117604b2 100644 --- a/style/font_face.rs +++ b/style/font_face.rs @@ -485,18 +485,6 @@ pub fn parse_font_face_block( #[cfg(feature = "servo")] pub struct FontFace<'a>(&'a FontFaceRuleData); -#[cfg(feature = "servo")] -impl Iterator for EffectiveSources { - type Item = Source; - fn next(&mut self) -> Option { - self.0.pop() - } - - fn size_hint(&self) -> (usize, Option) { - (self.0.len(), Some(self.0.len())) - } -} - struct FontFaceRuleParser<'a, 'b: 'a> { context: &'a ParserContext<'b>, rule: &'a mut FontFaceRuleData, diff --git a/style/gecko/media_features.rs b/style/gecko/media_features.rs index d0f7dad876..743697d8c5 100644 --- a/style/gecko/media_features.rs +++ b/style/gecko/media_features.rs @@ -8,7 +8,7 @@ use crate::gecko_bindings::bindings; use crate::gecko_bindings::structs; use crate::media_queries::{Device, MediaType}; use crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription}; -use crate::queries::values::Orientation; +use crate::queries::values::{Orientation, PrefersColorScheme}; use crate::values::computed::{CSSPixelLength, Context, Ratio, Resolution}; use crate::values::specified::color::ForcedColors; use crate::values::AtomString; @@ -191,15 +191,6 @@ enum PrefersReducedTransparency { Reduce, } -/// Values for the prefers-color-scheme media feature. -#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss)] -#[repr(u8)] -#[allow(missing_docs)] -pub enum PrefersColorScheme { - Light, - Dark, -} - /// Values for the dynamic-range and video-dynamic-range media features. /// https://drafts.csswg.org/mediaqueries-5/#dynamic-range /// This implements PartialOrd so that lower values will correctly match diff --git a/style/global_style_data.rs b/style/global_style_data.rs index d6e49e7da6..943e53d8fe 100644 --- a/style/global_style_data.rs +++ b/style/global_style_data.rs @@ -139,7 +139,7 @@ impl StyleThreadPool { #[cfg(feature = "servo")] fn stylo_threads_pref() -> i32 { - pref!(layout.threads) + style_config::get_i32("layout.threads") } #[cfg(feature = "gecko")] diff --git a/style/invalidation/element/invalidation_map.rs b/style/invalidation/element/invalidation_map.rs index 80e85bfc7b..f5b940f666 100644 --- a/style/invalidation/element/invalidation_map.rs +++ b/style/invalidation/element/invalidation_map.rs @@ -515,7 +515,7 @@ trait Collector { fn class_map(&mut self) -> &mut IdOrClassDependencyMap; fn state_map(&mut self) -> &mut StateDependencyMap; fn attribute_map(&mut self) -> &mut LocalNameDependencyMap; - fn custom_state_map(&mut self) -> &mut LocalNameDependencyMap; + fn custom_state_map(&mut self) -> &mut CustomStateDependencyMap; fn update_states(&mut self, element_state: ElementState, document_state: DocumentState); // In normal invalidations, type-based dependencies don't need to be explicitly tracked; diff --git a/style/invalidation/element/relative_selector.rs b/style/invalidation/element/relative_selector.rs index e633df468d..e951c2598d 100644 --- a/style/invalidation/element/relative_selector.rs +++ b/style/invalidation/element/relative_selector.rs @@ -22,6 +22,8 @@ use crate::invalidation::element::state_and_attributes::{ check_dependency, dependency_may_be_relevant, invalidated_descendants, invalidated_self, invalidated_sibling, push_invalidation, should_process_descendants, }; +#[cfg(feature = "servo")] +use crate::selector_parser::SnapshotMap as ServoElementSnapshotTable; use crate::stylist::{CascadeData, Stylist}; use dom::ElementState; use fxhash::FxHashMap; diff --git a/style/lib.rs b/style/lib.rs index 578ac5baba..bb69c986e8 100644 --- a/style/lib.rs +++ b/style/lib.rs @@ -173,6 +173,7 @@ pub use style_traits::owned_str::OwnedStr; use std::hash::{BuildHasher, Hash}; +#[macro_use] pub mod properties; #[cfg(feature = "gecko")] diff --git a/style/matching.rs b/style/matching.rs index ddb806e4f8..c1007a3f9d 100644 --- a/style/matching.rs +++ b/style/matching.rs @@ -8,6 +8,8 @@ #![deny(missing_docs)] use crate::computed_value_flags::ComputedValueFlags; +#[cfg(feature = "servo")] +use crate::context::CascadeInputs; use crate::context::{ElementCascadeInputs, QuirksMode}; use crate::context::{SharedStyleContext, StyleContext}; use crate::data::{ElementData, ElementStyles}; @@ -585,7 +587,8 @@ trait PrivateMatchMethods: TElement { // If we have modified animation or transitions, we recascade style for this node. if style_changed { - let mut rule_node = new_resolved_styles.primary_style().rules().clone(); + let primary_style = new_resolved_styles.primary_style(); + let mut rule_node = primary_style.rules().clone(); let declarations = context.shared.animations.get_all_declarations( &AnimationSetKey::new_for_non_pseudo(self.as_node().opaque()), context.shared.current_time_for_animations, @@ -594,20 +597,23 @@ trait PrivateMatchMethods: TElement { Self::replace_single_rule_node( &context.shared, CascadeLevel::Transitions, + LayerOrder::root(), declarations.transitions.as_ref().map(|a| a.borrow_arc()), &mut rule_node, ); Self::replace_single_rule_node( &context.shared, CascadeLevel::Animations, + LayerOrder::root(), declarations.animations.as_ref().map(|a| a.borrow_arc()), &mut rule_node, ); - if rule_node != *new_resolved_styles.primary_style().rules() { + if rule_node != *primary_style.rules() { let inputs = CascadeInputs { rules: Some(rule_node), - visited_rules: new_resolved_styles.primary_style().visited_rules().cloned(), + visited_rules: primary_style.visited_rules().cloned(), + flags: primary_style.flags.for_cascade_inputs(), }; new_resolved_styles.primary.style = StyleResolverForElement::new( @@ -696,6 +702,7 @@ trait PrivateMatchMethods: TElement { let inputs = CascadeInputs { rules: Some(rule_node), visited_rules: style.visited_rules().cloned(), + flags: style.flags.for_cascade_inputs(), }; let new_style = StyleResolverForElement::new( diff --git a/style/properties/Mako-1.1.2-py2.py3-none-any.whl b/style/properties/Mako-1.1.2-py2.py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..9593025a4739374da2fde2966afdaffa1d0b8e05 GIT binary patch literal 75521 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

wOg(?)H>s{J*n4v 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%0A>{n;f&I{@ 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 literal 0 HcmV?d00001 diff --git a/style/properties/build.py b/style/properties/build.py index d1f76d4a25..1a92544bfd 100644 --- a/style/properties/build.py +++ b/style/properties/build.py @@ -8,6 +8,7 @@ import sys BASE = os.path.dirname(__file__.replace("\\", "/")) +sys.path.insert(0, os.path.join(BASE, "Mako-1.1.2-py2.py3-none-any.whl")) sys.path.insert(0, BASE) # For importing `data.py` from mako import exceptions @@ -66,7 +67,12 @@ def main(): os.path.join(BASE, "properties.html.mako"), properties=properties_dict ) as_json = json.dumps(properties_dict, indent=4, sort_keys=True) - doc_servo = os.path.join(BASE, "..", "..", "..", "target", "doc", "servo") + + # Four dotdots: /path/to/target(4)/debug(3)/build(2)/style-*(1)/out + # Do not ascend above the target dir, because it may not be called target + # or even have a parent (see CARGO_TARGET_DIR). + doc_servo = os.path.join(OUT_DIR, "..", "..", "..", "..", "doc", "stylo") + write(doc_servo, "css-properties.html", as_html) write(doc_servo, "css-properties.json", as_json) write(OUT_DIR, "css-properties.json", as_json) diff --git a/style/properties/cascade.rs b/style/properties/cascade.rs index e38259dd39..56f0a13522 100644 --- a/style/properties/cascade.rs +++ b/style/properties/cascade.rs @@ -11,8 +11,6 @@ use crate::custom_properties::{ CustomPropertiesBuilder, DeferFontRelativeCustomPropertyResolution, }; use crate::dom::TElement; -#[cfg(feature = "gecko")] -use crate::font_metrics::FontMetricsOrientation; use crate::logical_geometry::WritingMode; use crate::properties::{ property_counts, CSSWideKeyword, ComputedValues, DeclarationImportanceIterator, Importance, @@ -761,7 +759,9 @@ impl<'b> Cascade<'b> { return; } - let has_writing_mode = apply!(WritingMode) | apply!(Direction) | apply!(TextOrientation); + let has_writing_mode = apply!(WritingMode) | apply!(Direction); + #[cfg(feature = "gecko")] + let has_writing_mode = has_writing_mode | apply!(TextOrientation); if has_writing_mode { context.builder.writing_mode = WritingMode::new(context.builder.get_inherited_box()) } @@ -781,29 +781,41 @@ impl<'b> Cascade<'b> { // Compute font-family. let has_font_family = apply!(FontFamily); let has_lang = apply!(XLang); - if has_lang { - self.recompute_initial_font_family_if_needed(&mut context.builder); - } - if has_font_family { - self.prioritize_user_fonts_if_needed(&mut context.builder); + + #[cfg(feature = "gecko")] { + if has_lang { + self.recompute_initial_font_family_if_needed(&mut context.builder); + } + if has_font_family { + self.prioritize_user_fonts_if_needed(&mut context.builder); + } } // Compute font-size. - if apply!(XTextScale) { - self.unzoom_fonts_if_needed(&mut context.builder); - } - let has_font_size = apply!(FontSize); - let has_math_depth = apply!(MathDepth); - let has_min_font_size_ratio = apply!(MozMinFontSizeRatio); + #[cfg(feature = "gecko")] { + if apply!(XTextScale) { + self.unzoom_fonts_if_needed(&mut context.builder); + } + let has_font_size = apply!(FontSize); + let has_math_depth = apply!(MathDepth); + let has_min_font_size_ratio = apply!(MozMinFontSizeRatio); - if has_math_depth && has_font_size { - self.recompute_math_font_size_if_needed(context); - } - if has_lang || has_font_family { - self.recompute_keyword_font_size_if_needed(context); + if has_math_depth && has_font_size { + self.recompute_math_font_size_if_needed(context); + } + if has_lang || has_font_family { + self.recompute_keyword_font_size_if_needed(context); + } + if has_font_size || has_min_font_size_ratio || has_lang || has_font_family { + self.constrain_font_size_if_needed(&mut context.builder); + } } - if has_font_size || has_min_font_size_ratio || has_lang || has_font_family { - self.constrain_font_size_if_needed(&mut context.builder); + #[cfg(feature = "servo")] + { + apply!(FontSize); + if has_lang || has_font_family { + self.recompute_keyword_font_size_if_needed(context); + } } // Compute the rest of the first-available-font-affecting properties. @@ -1035,6 +1047,7 @@ impl<'b> Cascade<'b> { builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_WORD_SPACING); } + #[cfg(feature = "gecko")] if self .author_specified .contains(LonghandId::FontSynthesisWeight) @@ -1042,6 +1055,7 @@ impl<'b> Cascade<'b> { builder.add_flags(ComputedValueFlags::HAS_AUTHOR_SPECIFIED_FONT_SYNTHESIS_WEIGHT); } + #[cfg(feature = "gecko")] if self .author_specified .contains(LonghandId::FontSynthesisStyle) @@ -1177,7 +1191,6 @@ impl<'b> Cascade<'b> { } /// Some keyword sizes depend on the font family and language. - #[cfg(feature = "gecko")] fn recompute_keyword_font_size_if_needed(&self, context: &mut computed::Context) { use crate::values::computed::ToComputedValue; @@ -1196,6 +1209,7 @@ impl<'b> Cascade<'b> { }, }; + #[cfg(feature = "gecko")] if font.mScriptUnconstrainedSize == new_size.computed_size { return; } diff --git a/style/properties/declaration_block.rs b/style/properties/declaration_block.rs index 49b1d020f7..78a3bdbfbb 100644 --- a/style/properties/declaration_block.rs +++ b/style/properties/declaration_block.rs @@ -940,7 +940,7 @@ impl PropertyDeclarationBlock { ); if let Some(cv) = computed_values { - context.builder.custom_properties = cv.custom_properties.clone(); + context.builder.custom_properties = cv.custom_properties().clone(); }; match (declaration, computed_values) { diff --git a/style/properties/helpers.mako.rs b/style/properties/helpers.mako.rs index 764ae9288a..3523516416 100644 --- a/style/properties/helpers.mako.rs +++ b/style/properties/helpers.mako.rs @@ -598,7 +598,7 @@ <%def name="inner_body(keyword, needs_conversion=False)"> pub use self::computed_value::T as SpecifiedValue; pub mod computed_value { - #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] + #[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))] #[derive(Clone, Copy, Debug, Eq, FromPrimitive, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToComputedValue, ToCss, ToResolvedValue, ToShmem)] pub enum T { % for variant in keyword.values_for(engine): diff --git a/style/properties/helpers/animated_properties.mako.rs b/style/properties/helpers/animated_properties.mako.rs index 513a198a6a..cf5665782b 100644 --- a/style/properties/helpers/animated_properties.mako.rs +++ b/style/properties/helpers/animated_properties.mako.rs @@ -396,7 +396,7 @@ impl AnimationValue { Some(animatable) } - /// Get an AnimationValue for an declaration id from a given computed values. + /// Get an AnimationValue for an AnimatableLonghand from a given computed values. pub fn from_computed_values( property: PropertyDeclarationId, style: &ComputedValues, diff --git a/style/properties/longhands/counters.mako.rs b/style/properties/longhands/counters.mako.rs index 80f5c50847..ae871d1d31 100644 --- a/style/properties/longhands/counters.mako.rs +++ b/style/properties/longhands/counters.mako.rs @@ -20,6 +20,7 @@ ${helpers.predefined_type( "counter-increment", "CounterIncrement", engines="gecko servo", + servo_pref="layout.legacy_layout", initial_value="Default::default()", animation_type="discrete", spec="https://drafts.csswg.org/css-lists/#propdef-counter-increment", @@ -31,6 +32,7 @@ ${helpers.predefined_type( "counter-reset", "CounterReset", engines="gecko servo", + servo_pref="layout.legacy_layout", initial_value="Default::default()", animation_type="discrete", spec="https://drafts.csswg.org/css-lists-3/#propdef-counter-reset", diff --git a/style/properties/longhands/inherited_text.mako.rs b/style/properties/longhands/inherited_text.mako.rs index 537cf2f486..49fb0dab1c 100644 --- a/style/properties/longhands/inherited_text.mako.rs +++ b/style/properties/longhands/inherited_text.mako.rs @@ -147,6 +147,7 @@ ${helpers.single_keyword( gecko_aliases="-moz-pre-space=preserve-spaces", engines="gecko servo", gecko_enum_prefix="StyleWhiteSpaceCollapse", + needs_conversion="True", animation_type="discrete", spec="https://drafts.csswg.org/css-text-4/#propdef-white-space-collapse", servo_restyle_damage="rebuild_and_reflow", @@ -381,6 +382,7 @@ ${helpers.single_keyword( "wrap nowrap", engines="gecko servo", gecko_enum_prefix="StyleTextWrapMode", + needs_conversion="True", animation_type="discrete", spec="https://drafts.csswg.org/css-text-4/#propdef-text-wrap-mode", servo_restyle_damage="rebuild_and_reflow", diff --git a/style/properties/longhands/list.mako.rs b/style/properties/longhands/list.mako.rs index 603b3b09d5..83b30b189b 100644 --- a/style/properties/longhands/list.mako.rs +++ b/style/properties/longhands/list.mako.rs @@ -69,6 +69,7 @@ ${helpers.predefined_type( "Quotes", "computed::Quotes::get_initial_value()", engines="gecko servo", + servo_pref="layout.legacy_layout", animation_type="discrete", spec="https://drafts.csswg.org/css-content/#propdef-quotes", servo_restyle_damage="rebuild_and_reflow", diff --git a/style/properties/longhands/text.mako.rs b/style/properties/longhands/text.mako.rs index 55b0928ba4..9cb725048b 100644 --- a/style/properties/longhands/text.mako.rs +++ b/style/properties/longhands/text.mako.rs @@ -9,6 +9,7 @@ ${helpers.predefined_type( "TextOverflow", "computed::TextOverflow::get_initial_value()", engines="gecko servo", + servo_pref="layout.legacy_layout", animation_type="discrete", boxed=True, spec="https://drafts.csswg.org/css-ui/#propdef-text-overflow", diff --git a/style/properties/mod.rs b/style/properties/mod.rs index a6a853abb1..d2299d6760 100644 --- a/style/properties/mod.rs +++ b/style/properties/mod.rs @@ -360,7 +360,10 @@ impl PropertyId { pub fn is_animatable(&self) -> bool { match self { Self::NonCustom(id) => id.is_animatable(), - Self::Custom(..) => true, + #[cfg(feature = "gecko")] + Self::Custom(_) => true, + #[cfg(feature = "servo")] + Self::Custom(_) => false, } } @@ -1086,7 +1089,10 @@ impl<'a> PropertyDeclarationId<'a> { pub fn is_animatable(&self) -> bool { match self { Self::Longhand(id) => id.is_animatable(), - Self::Custom(_) => true, + #[cfg(feature = "gecko")] + PropertyDeclarationId::Custom(_) => true, + #[cfg(feature = "servo")] + PropertyDeclarationId::Custom(_) => false, } } @@ -1096,7 +1102,10 @@ impl<'a> PropertyDeclarationId<'a> { match self { Self::Longhand(longhand) => longhand.is_discrete_animatable(), // TODO(bug 1885995): Refine this. + #[cfg(feature = "gecko")] Self::Custom(_) => true, + #[cfg(feature = "servo")] + Self::Custom(_) => false, } } @@ -1301,7 +1310,7 @@ pub struct SourcePropertyDeclaration { // This is huge, but we allocate it on the stack and then never move it, // we only pass `&mut SourcePropertyDeclaration` references around. -size_of_test!(SourcePropertyDeclaration, 632); +size_of_test!(SourcePropertyDeclaration, 568); impl SourcePropertyDeclaration { /// Create one with a single PropertyDeclaration. diff --git a/style/properties/properties.mako.rs b/style/properties/properties.mako.rs index 22b7758c85..84b15c0284 100644 --- a/style/properties/properties.mako.rs +++ b/style/properties/properties.mako.rs @@ -27,7 +27,6 @@ use crate::media_queries::Device; use crate::parser::ParserContext; use crate::selector_parser::PseudoElement; use crate::stylist::Stylist; -#[cfg(feature = "servo")] use servo_config::prefs; use style_traits::{CssWriter, KeywordsCollectFn, ParseError, SpecifiedValueInfo, StyleParseErrorKind, ToCss}; use crate::stylesheets::{CssRuleType, CssRuleTypes, Origin}; use crate::logical_geometry::{LogicalAxis, LogicalCorner, LogicalSide}; @@ -455,7 +454,7 @@ pub mod property_counts { /// The number of non-custom properties. pub const NON_CUSTOM: usize = LONGHANDS_AND_SHORTHANDS + ALIASES; /// The number of prioritary properties that we have. - pub const PRIORITARY: usize = ${len(PRIORITARY_PROPERTIES)}; + pub const PRIORITARY: usize = ${len(PRIORITARY_PROPERTIES.intersection(set(list(map(lambda p: p.name, data.longhands)))))}; /// The max number of longhands that a shorthand other than "all" expands to. pub const MAX_SHORTHAND_EXPANDED: usize = ${max(len(s.sub_properties) for s in data.shorthands_except_all())}; @@ -529,7 +528,7 @@ impl NonCustomPropertyId { Some(pref) => pref, }; - prefs::pref_map().get(pref).as_bool().unwrap_or(false) + style_config::get_bool(pref) % endif }; @@ -1375,9 +1374,6 @@ pub mod style_structs { use fxhash::FxHasher; use super::longhands; use std::hash::{Hash, Hasher}; - use crate::logical_geometry::WritingMode; - use crate::media_queries::Device; - use crate::values::computed::NonNegativeLength; use crate::values::specified::color::ColorSchemeFlags; % for style_struct in data.active_style_structs(): @@ -1507,8 +1503,6 @@ pub mod style_structs { /// Computes a font hash in order to be able to cache fonts /// effectively in GFX and layout. pub fn compute_font_hash(&mut self) { - // Corresponds to the fields in - // `gfx::font_template::FontTemplateDescriptor`. let mut hasher: FxHasher = Default::default(); self.font_weight.hash(&mut hasher); self.font_stretch.hash(&mut hasher); @@ -1516,25 +1510,17 @@ pub mod style_structs { self.font_family.hash(&mut hasher); self.hash = hasher.finish() } - - /// (Servo does not handle MathML, so this just calls copy_font_size_from) - pub fn inherit_font_size_from(&mut self, parent: &Self, - _: Option, - _: &Device) { - self.copy_font_size_from(parent); - } - /// (Servo does not handle MathML, so this just calls set_font_size) - pub fn apply_font_size(&mut self, - v: longhands::font_size::computed_value::T, - _: &Self, - _: &Device) -> Option { - self.set_font_size(v); - None - } - /// (Servo does not handle MathML, so this does nothing) - pub fn apply_unconstrained_font_size(&mut self, _: NonNegativeLength) { + /// Create a new Font with the initial values of all members. + pub fn initial_values() -> Self { + Self { + % for longhand in style_struct.longhands: + % if not longhand.logical: + ${longhand.ident}: longhands::${longhand.ident}::get_initial_value(), + % endif + % endfor + hash: 0, + } } - % elif style_struct.name == "InheritedUI": /// Returns the ColorSchemeFlags corresponding to the value of `color-scheme`. #[inline] @@ -1708,7 +1694,7 @@ pub struct ComputedValuesInner { pub writing_mode: WritingMode, /// The effective zoom value. - pub effective_zoom: Zoom, + pub effective_zoom: computed::Zoom, /// A set of flags we use to store misc information regarding this style. pub flags: ComputedValueFlags, @@ -1950,16 +1936,67 @@ impl ComputedValues { } /// Get the initial computed values. - pub fn initial_values() -> &'static Self { &*INITIAL_SERVO_VALUES } + pub fn initial_values_with_font_override(default_font: super::style_structs::Font) -> Arc { + use crate::logical_geometry::WritingMode; + use crate::computed_value_flags::ComputedValueFlags; + use servo_arc::Arc; + use super::{ComputedValues, ComputedValuesInner, longhands, style_structs}; + + Arc::new(ComputedValues { + inner: ComputedValuesInner { + % for style_struct in data.active_style_structs(): + % if style_struct.name == "Font": + font: Arc::new(default_font), + <% continue %> + % endif % + + ${style_struct.ident}: Arc::new(style_structs::${style_struct.name} { + % for longhand in style_struct.longhands: + % if not longhand.logical: + ${longhand.ident}: longhands::${longhand.ident}::get_initial_value(), + % endif + % endfor + % if style_struct.name == "InheritedText": + text_decorations_in_effect: + crate::values::computed::text::TextDecorationsInEffect::default(), + % endif + % if style_struct.name == "Box": + original_display: longhands::display::get_initial_value(), + % endif + }), + % endfor + custom_properties: crate::custom_properties::ComputedCustomProperties::default(), + writing_mode: WritingMode::empty(), + rules: None, + visited_style: None, + effective_zoom: crate::values::computed::Zoom::ONE, + flags: ComputedValueFlags::empty(), + }, + pseudo: None, + }) + } + + /// Converts the computed values to an Arc<> from a reference. + pub fn to_arc(&self) -> Arc { + // SAFETY: We're guaranteed to be allocated as an Arc<> since the + // functions above are the only ones that create ComputedValues + // instances in Servo (and that must be the case since ComputedValues' + // member is private). + unsafe { Arc::from_raw_addrefed(self) } + } /// Serializes the computed value of this property as a string. pub fn computed_value_to_string(&self, property: PropertyDeclarationId) -> String { match property { PropertyDeclarationId::Longhand(id) => { + let context = resolved::Context { + style: self, + }; let mut s = String::new(); - self.get_longhand_property_value( + self.computed_or_resolved_value( id, - &mut CssWriter::new(&mut s) + Some(&context), + &mut s ).unwrap(); s } @@ -1970,9 +2007,8 @@ impl ComputedValues { let p = &self.custom_properties; let value = p .inherited - .as_ref() - .and_then(|map| map.get(name)) - .or_else(|| p.non_inherited.as_ref().and_then(|map| map.get(name))); + .get(name) + .or_else(|| p.non_inherited.get(name)); value.map_or(String::new(), |value| value.to_css_string()) } } @@ -2034,7 +2070,7 @@ impl ComputedValuesInner { use crate::values::generics::counters::Content; match self.get_counters().content { Content::Normal | Content::None => true, - Content::Items(ref items) => items.is_empty(), + Content::Items(ref items) => items.items.is_empty(), } } @@ -2128,7 +2164,7 @@ impl ComputedValuesInner { /// Gets the logical computed margin from this style. #[inline] - pub fn logical_margin(&self) -> LogicalMargin<<&computed::LengthPercentageOrAuto> { + pub fn logical_margin(&self) -> LogicalMargin<<&computed::Margin> { let margin_style = self.get_margin(); LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new( &margin_style.margin_top, @@ -2140,7 +2176,7 @@ impl ComputedValuesInner { /// Gets the logical position from this style. #[inline] - pub fn logical_position(&self) -> LogicalMargin<<&computed::LengthPercentageOrAuto> { + pub fn logical_position(&self) -> LogicalMargin<<&computed::Inset> { // FIXME(SimonSapin): should be the writing mode of the containing block, maybe? let position_style = self.get_position(); LogicalMargin::from_physical(self.writing_mode, SideOffsets2D::new( @@ -2803,52 +2839,6 @@ impl<'a> StyleBuilder<'a> { % endfor } -#[cfg(feature = "servo")] -pub use self::lazy_static_module::INITIAL_SERVO_VALUES; - -// Use a module to work around #[cfg] on lazy_static! not being applied to every generated item. -#[cfg(feature = "servo")] -#[allow(missing_docs)] -mod lazy_static_module { - use crate::logical_geometry::WritingMode; - use crate::computed_value_flags::ComputedValueFlags; - use servo_arc::Arc; - use super::{ComputedValues, ComputedValuesInner, longhands, style_structs}; - - lazy_static! { - /// The initial values for all style structs as defined by the specification. - pub static ref INITIAL_SERVO_VALUES: ComputedValues = ComputedValues { - inner: ComputedValuesInner { - % for style_struct in data.active_style_structs(): - ${style_struct.ident}: Arc::new(style_structs::${style_struct.name} { - % for longhand in style_struct.longhands: - % if not longhand.logical: - ${longhand.ident}: longhands::${longhand.ident}::get_initial_value(), - % endif - % endfor - % if style_struct.name == "InheritedText": - text_decorations_in_effect: - crate::values::computed::text::TextDecorationsInEffect::default(), - % endif - % if style_struct.name == "Font": - hash: 0, - % endif - % if style_struct.name == "Box": - original_display: longhands::display::get_initial_value(), - % endif - }), - % endfor - custom_properties, - writing_mode: WritingMode::empty(), - rules: None, - visited_style: None, - flags: ComputedValueFlags::empty(), - }, - pseudo: None, - }; - } -} - /// A per-longhand function that performs the CSS cascade for that longhand. pub type CascadePropertyFn = unsafe extern "Rust" fn( @@ -2928,10 +2918,10 @@ macro_rules! css_properties_accessors { % for prop in [property] + property.aliases: % if '-' in prop.name: [${prop.ident.capitalize()}, Set${prop.ident.capitalize()}, - PropertyId::${kind}(${kind}Id::${property.camel_case})], + PropertyId::NonCustom(${kind}Id::${property.camel_case}.into())], % endif [${prop.camel_case}, Set${prop.camel_case}, - PropertyId::${kind}(${kind}Id::${property.camel_case})], + PropertyId::NonCustom(${kind}Id::${property.camel_case}.into())], % endfor % endif % endfor @@ -2945,6 +2935,9 @@ macro_rules! css_properties_accessors { /// ``` /// { snake_case_ident } /// ``` +/// +/// … where the boolean indicates whether the property value type +/// is wrapped in a `Box<_>` in the corresponding `PropertyDeclaration` variant. #[macro_export] macro_rules! longhand_properties_idents { ($macro_name: ident) => { @@ -2957,7 +2950,7 @@ macro_rules! longhand_properties_idents { } // Large pages generate tens of thousands of ComputedValues. -size_of_test!(ComputedValues, 240); +size_of_test!(ComputedValues, 208); // FFI relies on this. size_of_test!(Option>, 8); @@ -2987,6 +2980,9 @@ const_assert!(std::mem::size_of::( % for effect_name in ["repaint", "reflow_out_of_flow", "reflow", "rebuild_and_reflow_inline", "rebuild_and_reflow"]: macro_rules! restyle_damage_${effect_name} { ($old: ident, $new: ident, $damage: ident, [ $($effect:expr),* ]) => ({ + restyle_damage_${effect_name}!($old, $new, $damage, [$($effect),*], false) + }); + ($old: ident, $new: ident, $damage: ident, [ $($effect:expr),* ], $extra:expr) => ({ if % for style_struct in data.active_style_structs(): % for longhand in style_struct.longhands: @@ -2997,13 +2993,13 @@ const_assert!(std::mem::size_of::( % endfor % endfor - false { + $extra || false { $damage.insert($($effect)|*); true } else { false } - }) + }); } % endfor % endif diff --git a/style/properties/shorthands/font.mako.rs b/style/properties/shorthands/font.mako.rs index 198e862871..d708a737ea 100644 --- a/style/properties/shorthands/font.mako.rs +++ b/style/properties/shorthands/font.mako.rs @@ -34,7 +34,9 @@ > use crate::computed_values::font_variant_caps::T::SmallCaps; use crate::parser::Parse; - use crate::properties::longhands::{font_family, font_style, font_size, font_weight, font_stretch}; + use crate::properties::longhands::{font_family, font_style, font_weight, font_stretch}; + #[cfg(feature = "gecko")] + use crate::properties::longhands::font_size; use crate::properties::longhands::font_variant_caps; use crate::values::specified::font::LineHeight; use crate::values::specified::{FontSize, FontWeight}; @@ -315,7 +317,9 @@ % for p in subprops_for_value_info: ${p}::collect_completion_keywords(f); % endfor + % if engine == "gecko": ::collect_completion_keywords(f); + % endif } } diff --git a/style/properties_and_values/registry.rs b/style/properties_and_values/registry.rs index e3cd552c9c..5734aa1b2c 100644 --- a/style/properties_and_values/registry.rs +++ b/style/properties_and_values/registry.rs @@ -69,6 +69,7 @@ impl PropertyRegistration { /// The script registry of custom properties. /// #[derive(Default)] +#[cfg_attr(feature = "servo", derive(MallocSizeOf))] pub struct ScriptRegistry { properties: PrecomputedHashMap, } diff --git a/style/queries/values.rs b/style/queries/values.rs index f4934408c4..077f8f7a08 100644 --- a/style/queries/values.rs +++ b/style/queries/values.rs @@ -34,3 +34,12 @@ impl Orientation { } } } + +/// Values for the prefers-color-scheme media feature. +#[derive(Clone, Copy, Debug, FromPrimitive, Parse, PartialEq, ToCss, MallocSizeOf)] +#[repr(u8)] +#[allow(missing_docs)] +pub enum PrefersColorScheme { + Light, + Dark, +} diff --git a/style/servo/media_queries.rs b/style/servo/media_queries.rs index 4f87c2c49e..799f1ef5fa 100644 --- a/style/servo/media_queries.rs +++ b/style/servo/media_queries.rs @@ -7,21 +7,44 @@ use crate::color::AbsoluteColor; use crate::context::QuirksMode; use crate::custom_properties::CssEnvironment; -use crate::media_queries::media_feature::{AllowsRanges, ParsingRequirements}; -use crate::media_queries::media_feature::{Evaluator, MediaFeatureDescription}; -use crate::media_queries::media_feature_expression::RangeOrOperator; +use crate::font_metrics::FontMetrics; +use crate::queries::feature::{AllowsRanges, Evaluator, FeatureFlags, QueryFeatureDescription}; +use crate::queries::values::PrefersColorScheme; +use crate::logical_geometry::WritingMode; use crate::media_queries::MediaType; +use crate::properties::style_structs::Font; use crate::properties::ComputedValues; -use crate::values::computed::CSSPixelLength; +use crate::values::computed::{CSSPixelLength, Context, Length, LineHeight, NonNegativeLength, Resolution}; +use crate::values::computed::font::GenericFontFamily; use crate::values::specified::color::{ColorSchemeFlags, ForcedColors}; -use crate::values::specified::font::FONT_MEDIUM_PX; +use crate::values::specified::font::{FONT_MEDIUM_LINE_HEIGHT_PX, FONT_MEDIUM_PX}; +use crate::values::specified::ViewportVariant; use crate::values::KeyframesName; -use app_units::Au; +use app_units::{Au, AU_PER_PX}; use euclid::default::Size2D as UntypedSize2D; use euclid::{Scale, SideOffsets2D, Size2D}; +use mime::Mime; +use servo_arc::Arc; +use std::fmt::Debug; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use style_traits::{CSSPixel, DevicePixel}; +/// A trait used to query font metrics in clients of Stylo. This is used by Device to +/// query font metrics in a way that is specific to the client using Stylo. +pub trait FontMetricsProvider: Debug + Sync { + /// Query the font metrics for the given font and the given base font size. + fn query_font_metrics( + &self, + vertical: bool, + font: &Font, + base_size: CSSPixelLength, + in_media_query: bool, + retrieve_math_scales: bool, + ) -> FontMetrics; + /// Gets the base size given a generic font family. + fn base_size_for_generic(&self, generic: GenericFontFamily) -> Length; +} + /// A device is a structure that represents the current media a given document /// is displayed in. /// @@ -48,16 +71,34 @@ pub struct Device { /// a relaxed atomic here. #[ignore_malloc_size_of = "Pure stack type"] root_font_size: AtomicU32, + /// Line height of the root element, used for rlh units in other elements. + #[ignore_malloc_size_of = "Pure stack type"] + root_line_height: AtomicU32, /// Whether any styles computed in the document relied on the root font-size /// by using rem units. #[ignore_malloc_size_of = "Pure stack type"] used_root_font_size: AtomicBool, + /// Whether any styles computed in the document relied on the root line-height + /// by using rlh units. + #[ignore_malloc_size_of = "Pure stack type"] + used_root_line_height: AtomicBool, + /// Whether any styles computed in the document relied on font metrics. + used_font_metrics: AtomicBool, /// Whether any styles computed in the document relied on the viewport size. #[ignore_malloc_size_of = "Pure stack type"] used_viewport_units: AtomicBool, + /// Whether the user prefers light mode or dark mode + #[ignore_malloc_size_of = "Pure stack type"] + prefers_color_scheme: PrefersColorScheme, /// The CssEnvironment object responsible of getting CSS environment /// variables. environment: CssEnvironment, + /// An implementation of a trait which implements support for querying font metrics. + #[ignore_malloc_size_of = "Owned by embedder"] + font_metrics_provider: Box, + /// The default computed values for this Device. + #[ignore_malloc_size_of = "Arc is shared"] + default_computed_values: Arc, } impl Device { @@ -67,17 +108,25 @@ impl Device { quirks_mode: QuirksMode, viewport_size: Size2D, device_pixel_ratio: Scale, + font_metrics_provider: Box, + default_computed_values: Arc, + prefers_color_scheme: PrefersColorScheme, ) -> Device { Device { media_type, viewport_size, device_pixel_ratio, quirks_mode, - // FIXME(bz): Seems dubious? root_font_size: AtomicU32::new(FONT_MEDIUM_PX.to_bits()), + root_line_height: AtomicU32::new(FONT_MEDIUM_LINE_HEIGHT_PX.to_bits()), used_root_font_size: AtomicBool::new(false), + used_root_line_height: AtomicBool::new(false), + used_font_metrics: AtomicBool::new(false), used_viewport_units: AtomicBool::new(false), + prefers_color_scheme, environment: CssEnvironment, + font_metrics_provider, + default_computed_values, } } @@ -89,10 +138,7 @@ impl Device { /// Return the default computed values for this device. pub fn default_computed_values(&self) -> &ComputedValues { - // FIXME(bz): This isn't really right, but it's no more wrong - // than what we used to do. See - // https://github.com/servo/servo/issues/14773 for fixing it properly. - ComputedValues::initial_values() + &self.default_computed_values } /// Get the font size of the root element (for rem) @@ -101,10 +147,39 @@ impl Device { CSSPixelLength::new(f32::from_bits(self.root_font_size.load(Ordering::Relaxed))) } - /// Set the font size of the root element (for rem) - pub fn set_root_font_size(&self, size: CSSPixelLength) { - self.root_font_size - .store(size.px().to_bits(), Ordering::Relaxed) + /// Set the font size of the root element (for rem), in zoom-independent CSS pixels. + pub fn set_root_font_size(&self, size: f32) { + self.root_font_size.store(size.to_bits(), Ordering::Relaxed) + } + + /// Get the line height of the root element (for rlh) + pub fn root_line_height(&self) -> CSSPixelLength { + self.used_root_line_height.store(true, Ordering::Relaxed); + CSSPixelLength::new(f32::from_bits( + self.root_line_height.load(Ordering::Relaxed), + )) + } + + /// Set the line height of the root element (for rlh), in zoom-independent CSS pixels. + pub fn set_root_line_height(&self, size: f32) { + self.root_line_height.store(size.to_bits(), Ordering::Relaxed); + } + + /// Returns the computed line-height for the font in a given computed values instance. + /// + /// If you pass down an element, then the used line-height is returned. + pub fn calc_line_height( + &self, + font: &crate::properties::style_structs::Font, + _writing_mode: WritingMode, + _element: Option<()>, + ) -> NonNegativeLength { + (match font.line_height { + // TODO: compute `normal` from the font metrics + LineHeight::Normal => CSSPixelLength::new(0.), + LineHeight::Number(number) => font.font_size.computed_size() * number.0, + LineHeight::Length(length) => length.0, + }).into() } /// Get the quirks mode of the current device. @@ -119,6 +194,11 @@ impl Device { // Servo doesn't implement this quirk (yet) } + /// Gets the base size given a generic font family. + pub fn base_size_for_generic(&self, generic: GenericFontFamily) -> Length { + self.font_metrics_provider.base_size_for_generic(generic) + } + /// Whether a given animation name may be referenced from style. pub fn animation_name_may_be_referenced(&self, _: &KeyframesName) -> bool { // Assume it is, since we don't have any good way to prove it's not. @@ -130,6 +210,16 @@ impl Device { self.used_root_font_size.load(Ordering::Relaxed) } + /// Returns whether we ever looked up the root line-height of the device. + pub fn used_root_line_height(&self) -> bool { + self.used_root_line_height.load(Ordering::Relaxed) + } + + /// Returns whether font metrics have been queried. + pub fn used_font_metrics(&self) -> bool { + self.used_font_metrics.load(Ordering::Relaxed) + } + /// Returns the viewport size of the current device in app units, needed, /// among other things, to resolve viewport units. #[inline] @@ -141,8 +231,13 @@ impl Device { } /// Like the above, but records that we've used viewport units. - pub fn au_viewport_size_for_viewport_unit_resolution(&self) -> UntypedSize2D { + pub fn au_viewport_size_for_viewport_unit_resolution( + &self, + _: ViewportVariant, + ) -> UntypedSize2D { self.used_viewport_units.store(true, Ordering::Relaxed); + // Servo doesn't have dynamic UA interfaces that affect the viewport, + // so we can just ignore the ViewportVariant. self.au_viewport_size() } @@ -151,11 +246,41 @@ impl Device { self.used_viewport_units.load(Ordering::Relaxed) } + /// Returns the number of app units per device pixel we're using currently. + pub fn app_units_per_device_pixel(&self) -> i32 { + (AU_PER_PX as f32 / self.device_pixel_ratio.0) as i32 + } + /// Returns the device pixel ratio. pub fn device_pixel_ratio(&self) -> Scale { self.device_pixel_ratio } + /// Gets the size of the scrollbar in CSS pixels. + pub fn scrollbar_inline_size(&self) -> CSSPixelLength { + // TODO: implement this. + CSSPixelLength::new(0.0) + } + + /// Queries font metrics using the [`FontMetricsProvider`] interface. + pub fn query_font_metrics( + &self, + vertical: bool, + font: &Font, + base_size: CSSPixelLength, + in_media_query: bool, + retrieve_math_scales: bool, + ) -> FontMetrics { + self.used_font_metrics.store(true, Ordering::Relaxed); + self.font_metrics_provider.query_font_metrics( + vertical, + font, + base_size, + in_media_query, + retrieve_math_scales, + ) + } + /// Return the media type of the current device. pub fn media_type(&self) -> MediaType { self.media_type.clone() @@ -176,6 +301,11 @@ impl Device { AbsoluteColor::BLACK } + /// Returns the color scheme of this [`Device`]. + pub fn color_scheme(&self) -> PrefersColorScheme { + self.prefers_color_scheme + } + pub(crate) fn is_dark_color_scheme(&self, _: ColorSchemeFlags) -> bool { false } @@ -184,19 +314,34 @@ impl Device { pub fn safe_area_insets(&self) -> SideOffsets2D { SideOffsets2D::zero() } + + /// Returns true if the given MIME type is supported + pub fn is_supported_mime_type(&self, mime_type: &str) -> bool { + match mime_type.parse::() { + Ok(m) => { + // Keep this in sync with 'image_classifer' from + // components/net/mime_classifier.rs + m == mime::IMAGE_BMP + || m == mime::IMAGE_GIF + || m == mime::IMAGE_PNG + || m == mime::IMAGE_JPEG + || m == "image/x-icon" + || m == "image/webp" + } + _ => false, + } + } + + /// Return whether the document is a chrome document. + #[inline] + pub fn chrome_rules_enabled_for_document(&self) -> bool { + false + } } /// https://drafts.csswg.org/mediaqueries-4/#width -fn eval_width( - device: &Device, - value: Option, - range_or_operator: Option, -) -> bool { - RangeOrOperator::evaluate( - range_or_operator, - value.map(Au::from), - device.au_viewport_size().width, - ) +fn eval_width(context: &Context) -> CSSPixelLength { + CSSPixelLength::new(context.device().au_viewport_size().width.to_f32_px()) } #[derive(Clone, Copy, Debug, FromPrimitive, Parse, ToCss)] @@ -207,26 +352,65 @@ enum Scan { } /// https://drafts.csswg.org/mediaqueries-4/#scan -fn eval_scan(_: &Device, _: Option) -> bool { +fn eval_scan(_: &Context, _: Option) -> bool { // Since we doesn't support the 'tv' media type, the 'scan' feature never // matches. false } -lazy_static! { - /// A list with all the media features that Servo supports. - pub static ref MEDIA_FEATURES: [MediaFeatureDescription; 2] = [ - feature!( - atom!("width"), - AllowsRanges::Yes, - Evaluator::Length(eval_width), - ParsingRequirements::empty(), - ), - feature!( - atom!("scan"), - AllowsRanges::No, - keyword_evaluator!(eval_scan, Scan), - ParsingRequirements::empty(), - ), - ]; +/// https://drafts.csswg.org/mediaqueries-4/#resolution +fn eval_resolution(context: &Context) -> Resolution { + Resolution::from_dppx(context.device().device_pixel_ratio.0) +} + +/// https://compat.spec.whatwg.org/#css-media-queries-webkit-device-pixel-ratio +fn eval_device_pixel_ratio(context: &Context) -> f32 { + eval_resolution(context).dppx() } + +fn eval_prefers_color_scheme(context: &Context, query_value: Option) -> bool { + match query_value { + Some(v) => context.device().prefers_color_scheme == v, + None => true, + } +} + +/// A list with all the media features that Servo supports. +pub static MEDIA_FEATURES: [QueryFeatureDescription; 6] = [ + feature!( + atom!("width"), + AllowsRanges::Yes, + Evaluator::Length(eval_width), + FeatureFlags::empty(), + ), + feature!( + atom!("scan"), + AllowsRanges::No, + keyword_evaluator!(eval_scan, Scan), + FeatureFlags::empty(), + ), + feature!( + atom!("resolution"), + AllowsRanges::Yes, + Evaluator::Resolution(eval_resolution), + FeatureFlags::empty(), + ), + feature!( + atom!("device-pixel-ratio"), + AllowsRanges::Yes, + Evaluator::Float(eval_device_pixel_ratio), + FeatureFlags::WEBKIT_PREFIX, + ), + feature!( + atom!("-moz-device-pixel-ratio"), + AllowsRanges::Yes, + Evaluator::Float(eval_device_pixel_ratio), + FeatureFlags::empty(), + ), + feature!( + atom!("prefers-color-scheme"), + AllowsRanges::No, + keyword_evaluator!(eval_prefers_color_scheme, PrefersColorScheme), + FeatureFlags::empty(), + ), +]; diff --git a/style/servo/restyle_damage.rs b/style/servo/restyle_damage.rs index fe17fa6198..6355959805 100644 --- a/style/servo/restyle_damage.rs +++ b/style/servo/restyle_damage.rs @@ -12,6 +12,7 @@ use std::fmt; bitflags! { /// Individual layout actions that may be necessary after restyling. + #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub struct ServoRestyleDamage: u8 { /// Repaint the node itself. /// @@ -210,7 +211,8 @@ fn compute_damage(old: &ComputedValues, new: &ComputedValues) -> ServoRestyleDam ServoRestyleDamage::REFLOW_OUT_OF_FLOW, ServoRestyleDamage::REFLOW, ServoRestyleDamage::RECONSTRUCT_FLOW - ] + ], + old.get_box().original_display != new.get_box().original_display ) || (new.get_box().display == Display::Inline && restyle_damage_rebuild_and_reflow_inline!( old, diff --git a/style/servo/selector_parser.rs b/style/servo/selector_parser.rs index b20f1754a0..556c3992a3 100644 --- a/style/servo/selector_parser.rs +++ b/style/servo/selector_parser.rs @@ -7,6 +7,7 @@ //! Servo's selector parser. use crate::attr::{AttrIdentifier, AttrValue}; +use crate::computed_value_flags::ComputedValueFlags; use crate::dom::{OpaqueNode, TElement, TNode}; use crate::invalidation::element::document_state::InvalidationMatchingData; use crate::invalidation::element::element_wrapper::ElementSnapshot; @@ -49,27 +50,29 @@ pub enum PseudoElement { // them. Also, make sure the UA sheet has the !important rules some of the // APPLIES_TO_PLACEHOLDER properties expect! + Backdrop, + // Non-eager pseudos. DetailsSummary, DetailsContent, - ServoText, - ServoInputText, - ServoTableWrapper, - ServoAnonymousTableWrapper, + ServoAnonymousBox, ServoAnonymousTable, - ServoAnonymousTableRow, ServoAnonymousTableCell, - ServoAnonymousBlock, - ServoInlineBlockWrapper, - ServoInlineAbsolute, + ServoAnonymousTableRow, + ServoLegacyText, + ServoLegacyInputText, + ServoLegacyTableWrapper, + ServoLegacyAnonymousTableWrapper, + ServoLegacyAnonymousTable, + ServoLegacyAnonymousBlock, + ServoLegacyInlineBlockWrapper, + ServoLegacyInlineAbsolute, + ServoTableGrid, + ServoTableWrapper, } /// The count of all pseudo-elements. -pub const PSEUDO_COUNT: usize = PseudoElement::ServoInlineAbsolute as usize + 1; - -impl ::selectors::parser::PseudoElement for PseudoElement { - type Impl = SelectorImpl; -} +pub const PSEUDO_COUNT: usize = PseudoElement::ServoTableWrapper as usize + 1; impl ToCss for PseudoElement { fn to_css(&self, dest: &mut W) -> fmt::Result @@ -81,22 +84,31 @@ impl ToCss for PseudoElement { After => "::after", Before => "::before", Selection => "::selection", + Backdrop => "::backdrop", DetailsSummary => "::-servo-details-summary", DetailsContent => "::-servo-details-content", - ServoText => "::-servo-text", - ServoInputText => "::-servo-input-text", - ServoTableWrapper => "::-servo-table-wrapper", - ServoAnonymousTableWrapper => "::-servo-anonymous-table-wrapper", + ServoAnonymousBox => "::-servo-anonymous-box", ServoAnonymousTable => "::-servo-anonymous-table", - ServoAnonymousTableRow => "::-servo-anonymous-table-row", ServoAnonymousTableCell => "::-servo-anonymous-table-cell", - ServoAnonymousBlock => "::-servo-anonymous-block", - ServoInlineBlockWrapper => "::-servo-inline-block-wrapper", - ServoInlineAbsolute => "::-servo-inline-absolute", + ServoAnonymousTableRow => "::-servo-anonymous-table-row", + ServoLegacyText => "::-servo-legacy-text", + ServoLegacyInputText => "::-servo-legacy-input-text", + ServoLegacyTableWrapper => "::-servo-legacy-table-wrapper", + ServoLegacyAnonymousTableWrapper => "::-servo-legacy-anonymous-table-wrapper", + ServoLegacyAnonymousTable => "::-servo-legacy-anonymous-table", + ServoLegacyAnonymousBlock => "::-servo-legacy-anonymous-block", + ServoLegacyInlineBlockWrapper => "::-servo-legacy-inline-block-wrapper", + ServoLegacyInlineAbsolute => "::-servo-legacy-inline-absolute", + ServoTableGrid => "::-servo-table-grid", + ServoTableWrapper => "::-servo-table-wrapper", }) } } +impl ::selectors::parser::PseudoElement for PseudoElement { + type Impl = SelectorImpl; +} + /// The number of eager pseudo-elements. Keep this in sync with cascade_type. pub const EAGER_PSEUDO_COUNT: usize = 3; @@ -223,18 +235,22 @@ impl PseudoElement { PseudoElement::After | PseudoElement::Before | PseudoElement::Selection => { PseudoElementCascadeType::Eager }, - PseudoElement::DetailsSummary => PseudoElementCascadeType::Lazy, + PseudoElement::Backdrop | PseudoElement::DetailsSummary => PseudoElementCascadeType::Lazy, PseudoElement::DetailsContent | - PseudoElement::ServoText | - PseudoElement::ServoInputText | - PseudoElement::ServoTableWrapper | - PseudoElement::ServoAnonymousTableWrapper | + PseudoElement::ServoAnonymousBox | PseudoElement::ServoAnonymousTable | - PseudoElement::ServoAnonymousTableRow | PseudoElement::ServoAnonymousTableCell | - PseudoElement::ServoAnonymousBlock | - PseudoElement::ServoInlineBlockWrapper | - PseudoElement::ServoInlineAbsolute => PseudoElementCascadeType::Precomputed, + PseudoElement::ServoAnonymousTableRow | + PseudoElement::ServoLegacyText | + PseudoElement::ServoLegacyInputText | + PseudoElement::ServoLegacyTableWrapper | + PseudoElement::ServoLegacyAnonymousTableWrapper | + PseudoElement::ServoLegacyAnonymousTable | + PseudoElement::ServoLegacyAnonymousBlock | + PseudoElement::ServoLegacyInlineBlockWrapper | + PseudoElement::ServoLegacyInlineAbsolute | + PseudoElement::ServoTableGrid | + PseudoElement::ServoTableWrapper => PseudoElementCascadeType::Precomputed, } } @@ -273,6 +289,10 @@ impl PseudoElement { /// The type used for storing `:lang` arguments. pub type Lang = Box; +/// The type used to store the state argument to the `:state` pseudo-class. +#[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToCss, ToShmem)] +pub struct CustomState(pub AtomIdent); + /// A non tree-structural pseudo-class. /// See https://drafts.csswg.org/selectors-4/#structural-pseudos #[derive(Clone, Debug, Eq, Hash, MallocSizeOf, PartialEq, ToShmem)] @@ -280,21 +300,37 @@ pub type Lang = Box; pub enum NonTSPseudoClass { Active, AnyLink, + Autofill, Checked, + /// The :state` pseudo-class. + CustomState(CustomState), + Default, Defined, Disabled, Enabled, Focus, + FocusWithin, + FocusVisible, Fullscreen, Hover, + InRange, Indeterminate, + Invalid, Lang(Lang), Link, + Modal, + Optional, + OutOfRange, PlaceholderShown, - ReadWrite, + PopoverOpen, ReadOnly, + ReadWrite, + Required, ServoNonZeroBorder, Target, + UserInvalid, + UserValid, + Valid, Visited, } @@ -335,24 +371,38 @@ impl ToCss for NonTSPseudoClass { } dest.write_str(match *self { - Active => ":active", - AnyLink => ":any-link", - Checked => ":checked", - Defined => ":defined", - Disabled => ":disabled", - Enabled => ":enabled", - Focus => ":focus", - Fullscreen => ":fullscreen", - Hover => ":hover", - Indeterminate => ":indeterminate", - Link => ":link", - PlaceholderShown => ":placeholder-shown", - ReadWrite => ":read-write", - ReadOnly => ":read-only", - ServoNonZeroBorder => ":-servo-nonzero-border", - Target => ":target", - Visited => ":visited", - Lang(_) => unreachable!(), + Self::Active => ":active", + Self::AnyLink => ":any-link", + Self::Autofill => ":autofill", + Self::Checked => ":checked", + Self::Default => ":default", + Self::Defined => ":defined", + Self::Disabled => ":disabled", + Self::Enabled => ":enabled", + Self::Focus => ":focus", + Self::FocusVisible => ":focus-visible", + Self::FocusWithin => ":focus-within", + Self::Fullscreen => ":fullscreen", + Self::Hover => ":hover", + Self::InRange => ":in-range", + Self::Indeterminate => ":indeterminate", + Self::Invalid => ":invalid", + Self::Link => ":link", + Self::Modal => ":modal", + Self::Optional => ":optional", + Self::OutOfRange => ":out-of-range", + Self::PlaceholderShown => ":placeholder-shown", + Self::PopoverOpen => ":popover-open", + Self::ReadOnly => ":read-only", + Self::ReadWrite => ":read-write", + Self::Required => ":required", + Self::ServoNonZeroBorder => ":-servo-nonzero-border", + Self::Target => ":target", + Self::UserInvalid => ":user-invalid", + Self::UserValid => ":user-valid", + Self::Valid => ":valid", + Self::Visited => ":visited", + Self::Lang(_) | Self::CustomState(_) => unreachable!(), }) } } @@ -361,22 +411,38 @@ impl NonTSPseudoClass { /// Gets a given state flag for this pseudo-class. This is used to do /// selector matching, and it's set from the DOM. pub fn state_flag(&self) -> ElementState { - use self::NonTSPseudoClass::*; match *self { - Active => ElementState::IN_ACTIVE_STATE, - Focus => ElementState::IN_FOCUS_STATE, - Fullscreen => ElementState::IN_FULLSCREEN_STATE, - Hover => ElementState::IN_HOVER_STATE, - Defined => ElementState::IN_DEFINED_STATE, - Enabled => ElementState::IN_ENABLED_STATE, - Disabled => ElementState::IN_DISABLED_STATE, - Checked => ElementState::IN_CHECKED_STATE, - Indeterminate => ElementState::IN_INDETERMINATE_STATE, - ReadOnly | ReadWrite => ElementState::IN_READWRITE_STATE, - PlaceholderShown => ElementState::IN_PLACEHOLDER_SHOWN_STATE, - Target => ElementState::IN_TARGET_STATE, - - AnyLink | Lang(_) | Link | Visited | ServoNonZeroBorder => ElementState::empty(), + Self::Active => ElementState::ACTIVE, + Self::AnyLink => ElementState::VISITED_OR_UNVISITED, + Self::Autofill => ElementState::AUTOFILL, + Self::Checked => ElementState::CHECKED, + Self::Default => ElementState::DEFAULT, + Self::Defined => ElementState::DEFINED, + Self::Disabled => ElementState::DISABLED, + Self::Enabled => ElementState::ENABLED, + Self::Focus => ElementState::FOCUS, + Self::FocusVisible => ElementState::FOCUSRING, + Self::FocusWithin => ElementState::FOCUS_WITHIN, + Self::Fullscreen => ElementState::FULLSCREEN, + Self::Hover => ElementState::HOVER, + Self::InRange => ElementState::INRANGE, + Self::Indeterminate => ElementState::INDETERMINATE, + Self::Invalid => ElementState::INVALID, + Self::Link => ElementState::UNVISITED, + Self::Modal => ElementState::MODAL, + Self::Optional => ElementState::OPTIONAL_, + Self::OutOfRange => ElementState::OUTOFRANGE, + Self::PlaceholderShown => ElementState::PLACEHOLDER_SHOWN, + Self::PopoverOpen => ElementState::POPOVER_OPEN, + Self::ReadOnly => ElementState::READONLY, + Self::ReadWrite => ElementState::READWRITE, + Self::Required => ElementState::REQUIRED, + Self::Target => ElementState::URLTARGET, + Self::UserInvalid => ElementState::USER_INVALID, + Self::UserValid => ElementState::USER_VALID, + Self::Valid => ElementState::VALID, + Self::Visited => ElementState::VISITED, + Self::CustomState(_) | Self::Lang(_) | Self::ServoNonZeroBorder => ElementState::empty(), } } @@ -397,57 +463,106 @@ impl NonTSPseudoClass { #[cfg_attr(feature = "servo", derive(MallocSizeOf))] pub struct SelectorImpl; +/// A set of extra data to carry along with the matching context, either for +/// selector-matching or invalidation. +#[derive(Debug, Default)] +pub struct ExtraMatchingData<'a> { + /// The invalidation data to invalidate doc-state pseudo-classes correctly. + pub invalidation_data: InvalidationMatchingData, + + /// The invalidation bits from matching container queries. These are here + /// just for convenience mostly. + pub cascade_input_flags: ComputedValueFlags, + + /// The style of the originating element in order to evaluate @container + /// size queries affecting pseudo-elements. + pub originating_element_style: Option<&'a ComputedValues>, +} + impl ::selectors::SelectorImpl for SelectorImpl { type PseudoElement = PseudoElement; type NonTSPseudoClass = NonTSPseudoClass; - type ExtraMatchingData = InvalidationMatchingData; - type AttrValue = String; - type Identifier = Atom; - type ClassName = Atom; - type PartName = Atom; + type ExtraMatchingData<'a> = ExtraMatchingData<'a>; + type AttrValue = AtomString; + type Identifier = AtomIdent; type LocalName = LocalName; type NamespacePrefix = Prefix; type NamespaceUrl = Namespace; - type BorrowedLocalName = LocalName; - type BorrowedNamespaceUrl = Namespace; + type BorrowedLocalName = markup5ever::LocalName; + type BorrowedNamespaceUrl = markup5ever::Namespace; } impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { type Impl = SelectorImpl; type Error = StyleParseErrorKind<'i>; + #[inline] + fn parse_nth_child_of(&self) -> bool { + false + } + + #[inline] + fn parse_is_and_where(&self) -> bool { + true + } + + #[inline] + fn parse_has(&self) -> bool { + false + } + + #[inline] + fn parse_parent_selector(&self) -> bool { + false + } + + #[inline] + fn allow_forgiving_selectors(&self) -> bool { + !self.for_supports_rule + } + fn parse_non_ts_pseudo_class( &self, location: SourceLocation, name: CowRcStr<'i>, ) -> Result> { - use self::NonTSPseudoClass::*; let pseudo_class = match_ignore_ascii_case! { &name, - "active" => Active, - "any-link" => AnyLink, - "checked" => Checked, - "defined" => Defined, - "disabled" => Disabled, - "enabled" => Enabled, - "focus" => Focus, - "fullscreen" => Fullscreen, - "hover" => Hover, - "indeterminate" => Indeterminate, - "-moz-inert" => MozInert, - "link" => Link, - "placeholder-shown" => PlaceholderShown, - "read-write" => ReadWrite, - "read-only" => ReadOnly, - "target" => Target, - "visited" => Visited, + "active" => NonTSPseudoClass::Active, + "any-link" => NonTSPseudoClass::AnyLink, + "autofill" => NonTSPseudoClass::Autofill, + "checked" => NonTSPseudoClass::Checked, + "default" => NonTSPseudoClass::Default, + "defined" => NonTSPseudoClass::Defined, + "disabled" => NonTSPseudoClass::Disabled, + "enabled" => NonTSPseudoClass::Enabled, + "focus" => NonTSPseudoClass::Focus, + "focus-visible" => NonTSPseudoClass::FocusVisible, + "focus-within" => NonTSPseudoClass::FocusWithin, + "fullscreen" => NonTSPseudoClass::Fullscreen, + "hover" => NonTSPseudoClass::Hover, + "indeterminate" => NonTSPseudoClass::Indeterminate, + "invalid" => NonTSPseudoClass::Invalid, + "link" => NonTSPseudoClass::Link, + "optional" => NonTSPseudoClass::Optional, + "out-of-range" => NonTSPseudoClass::OutOfRange, + "placeholder-shown" => NonTSPseudoClass::PlaceholderShown, + "popover-open" => NonTSPseudoClass::PopoverOpen, + "read-only" => NonTSPseudoClass::ReadOnly, + "read-write" => NonTSPseudoClass::ReadWrite, + "required" => NonTSPseudoClass::Required, + "target" => NonTSPseudoClass::Target, + "user-invalid" => NonTSPseudoClass::UserInvalid, + "user-valid" => NonTSPseudoClass::UserValid, + "valid" => NonTSPseudoClass::Valid, + "visited" => NonTSPseudoClass::Visited, "-servo-nonzero-border" => { if !self.in_user_agent_stylesheet() { return Err(location.new_custom_error( SelectorParseErrorKind::UnexpectedIdent("-servo-nonzero-border".into()) )) } - ServoNonZeroBorder + NonTSPseudoClass::ServoNonZeroBorder }, _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))), }; @@ -459,10 +574,11 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { &self, name: CowRcStr<'i>, parser: &mut CssParser<'i, 't>, + after_part: bool, ) -> Result> { use self::NonTSPseudoClass::*; let pseudo_class = match_ignore_ascii_case! { &name, - "lang" => { + "lang" if !after_part => { Lang(parser.expect_ident_or_string()?.as_ref().into()) }, _ => return Err(parser.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))), @@ -481,6 +597,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { "before" => Before, "after" => After, "selection" => Selection, + "backdrop" => Backdrop, "-servo-details-summary" => { if !self.in_user_agent_stylesheet() { return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) @@ -493,29 +610,41 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { } DetailsContent }, - "-servo-text" => { + "-servo-anonymous-box" => { if !self.in_user_agent_stylesheet() { return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) } - ServoText + ServoAnonymousBox }, - "-servo-input-text" => { + "-servo-legacy-text" => { if !self.in_user_agent_stylesheet() { return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) } - ServoInputText + ServoLegacyText }, - "-servo-table-wrapper" => { + "-servo-legacy-input-text" => { if !self.in_user_agent_stylesheet() { return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) } - ServoTableWrapper + ServoLegacyInputText + }, + "-servo-legacy-table-wrapper" => { + if !self.in_user_agent_stylesheet() { + return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) + } + ServoLegacyTableWrapper }, - "-servo-anonymous-table-wrapper" => { + "-servo-legacy-anonymous-table-wrapper" => { if !self.in_user_agent_stylesheet() { return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) } - ServoAnonymousTableWrapper + ServoLegacyAnonymousTableWrapper + }, + "-servo-legacy-anonymous-table" => { + if !self.in_user_agent_stylesheet() { + return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) + } + ServoLegacyAnonymousTable }, "-servo-anonymous-table" => { if !self.in_user_agent_stylesheet() { @@ -535,23 +664,35 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { } ServoAnonymousTableCell }, - "-servo-anonymous-block" => { + "-servo-legacy-anonymous-block" => { + if !self.in_user_agent_stylesheet() { + return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) + } + ServoLegacyAnonymousBlock + }, + "-servo-legacy-inline-block-wrapper" => { + if !self.in_user_agent_stylesheet() { + return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) + } + ServoLegacyInlineBlockWrapper + }, + "-servo-legacy-inline-absolute" => { if !self.in_user_agent_stylesheet() { return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) } - ServoAnonymousBlock + ServoLegacyInlineAbsolute }, - "-servo-inline-block-wrapper" => { + "-servo-table-grid" => { if !self.in_user_agent_stylesheet() { return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) } - ServoInlineBlockWrapper + ServoTableGrid }, - "-servo-inline-absolute" => { + "-servo-table-wrapper" => { if !self.in_user_agent_stylesheet() { return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) } - ServoInlineAbsolute + ServoTableWrapper }, _ => return Err(location.new_custom_error(SelectorParseErrorKind::UnexpectedIdent(name.clone()))) @@ -567,6 +708,10 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { fn namespace_for_prefix(&self, prefix: &Prefix) -> Option { self.namespaces.prefixes.get(prefix).cloned() } + + fn parse_host(&self) -> bool { + true + } } impl SelectorImpl { @@ -730,6 +875,25 @@ impl ElementSnapshot for ServoElementSnapshot { .or_else(|| self.get_attr(&ns!(), &local_name!("lang"))) .map(|v| SelectorAttrValue::from(v as &str)) } + + /// Returns true if the snapshot has stored state for custom states + #[inline] + fn has_custom_states(&self) -> bool { + false + } + + /// Returns true if the snapshot has a given CustomState + #[inline] + fn has_custom_state(&self, _state: &AtomIdent) -> bool { + false + } + + #[inline] + fn each_custom_state(&self, mut _callback: F) + where + F: FnMut(&AtomIdent), + { + } } impl ServoElementSnapshot { diff --git a/style/str.rs b/style/str.rs index fe598f546e..ad706746fe 100644 --- a/style/str.rs +++ b/style/str.rs @@ -178,3 +178,11 @@ pub type CssStringWriter = ::nsstring::nsACString; /// needs to allocate a temporary string. #[cfg(feature = "gecko")] pub type CssString = ::nsstring::nsCString; + +/// String. The comments for the Gecko types explain the need for this abstraction. +#[cfg(feature = "servo")] +pub type CssStringWriter = String; + +/// String. The comments for the Gecko types explain the need for this abstraction. +#[cfg(feature = "servo")] +pub type CssString = String; diff --git a/style/stylesheets/import_rule.rs b/style/stylesheets/import_rule.rs index d0e5528c18..477942ca0c 100644 --- a/style/stylesheets/import_rule.rs +++ b/style/stylesheets/import_rule.rs @@ -108,18 +108,45 @@ impl DeepCloneWithLock for ImportSheet { /// A sheet that is held from an import rule. #[cfg(feature = "servo")] #[derive(Debug)] -pub struct ImportSheet(pub ::servo_arc::Arc); +pub enum ImportSheet { + /// A bonafide stylesheet. + Sheet(::servo_arc::Arc), + + /// An @import created with a false , so will never be fetched. + Refused, +} #[cfg(feature = "servo")] impl ImportSheet { + /// Creates a new ImportSheet from a stylesheet. + pub fn new(sheet: ::servo_arc::Arc) -> Self { + ImportSheet::Sheet(sheet) + } + + /// Creates a refused ImportSheet for a load that will not happen. + pub fn new_refused() -> Self { + ImportSheet::Refused + } + + /// Returns a reference to the stylesheet in this ImportSheet, if it exists. + pub fn as_sheet(&self) -> Option<&::servo_arc::Arc> { + match *self { + ImportSheet::Sheet(ref s) => Some(s), + ImportSheet::Refused => None, + } + } + /// Returns the media list for this import rule. pub fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> { - self.0.media(guard) + self.as_sheet().and_then(|s| s.media(guard)) } /// Returns the rules for this import rule. pub fn rules<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> &'a [CssRule] { - self.0.rules() + match self.as_sheet() { + Some(s) => s.rules(guard), + None => &[], + } } } @@ -130,8 +157,13 @@ impl DeepCloneWithLock for ImportSheet { _lock: &SharedRwLock, _guard: &SharedRwLockReadGuard, ) -> Self { - use servo_arc::Arc; - ImportSheet(Arc::new((&*self.0).clone())) + match *self { + ImportSheet::Sheet(ref s) => { + use servo_arc::Arc; + ImportSheet::Sheet(Arc::new((&**s).clone())) + }, + ImportSheet::Refused => ImportSheet::Refused, + } } } diff --git a/style/stylesheets/mod.rs b/style/stylesheets/mod.rs index 8af13ef15c..1871f1037d 100644 --- a/style/stylesheets/mod.rs +++ b/style/stylesheets/mod.rs @@ -115,8 +115,8 @@ pub struct UrlExtraData(usize); /// Extra data that the backend may need to resolve url values. #[cfg(feature = "servo")] -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct UrlExtraData(pub Arc<::url::Url>); +#[derive(Clone, Debug, Eq, MallocSizeOf, PartialEq)] +pub struct UrlExtraData(#[ignore_malloc_size_of = "Arc"] pub Arc<::url::Url>); #[cfg(feature = "servo")] impl UrlExtraData { diff --git a/style/stylesheets/position_try_rule.rs b/style/stylesheets/position_try_rule.rs index 3efab5ee5a..584d4122c4 100644 --- a/style/stylesheets/position_try_rule.rs +++ b/style/stylesheets/position_try_rule.rs @@ -15,6 +15,7 @@ use crate::shared_lock::{ use crate::str::CssStringWriter; use crate::values::DashedIdent; use cssparser::SourceLocation; +#[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOf, MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; use servo_arc::Arc; use style_traits::{CssWriter, ToCss}; diff --git a/style/stylesheets/stylesheet.rs b/style/stylesheets/stylesheet.rs index 64d78fae46..e34e8d8e93 100644 --- a/style/stylesheets/stylesheet.rs +++ b/style/stylesheets/stylesheet.rs @@ -122,13 +122,12 @@ impl StylesheetContents { /// An empty namespace map should be fine, as it is only used for parsing, /// not serialization of existing selectors. Since UA sheets are read only, /// we should never need the namespace map. - pub fn from_shared_data( + pub fn from_data( rules: Arc>, origin: Origin, url_data: UrlExtraData, quirks_mode: QuirksMode, ) -> Arc { - debug_assert!(rules.is_static()); Arc::new(Self { rules, origin, @@ -141,6 +140,17 @@ impl StylesheetContents { }) } + /// Same as above, but ensuring that the rules are static. + pub fn from_shared_data( + rules: Arc>, + origin: Origin, + url_data: UrlExtraData, + quirks_mode: QuirksMode, + ) -> Arc { + debug_assert!(rules.is_static()); + Self::from_data(rules, origin, url_data, quirks_mode) + } + /// Returns a reference to the list of rules. #[inline] pub fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] { diff --git a/style/stylist.rs b/style/stylist.rs index 9801b53965..d4950c5e22 100644 --- a/style/stylist.rs +++ b/style/stylist.rs @@ -47,9 +47,10 @@ use crate::stylesheets::scope_rule::{ #[cfg(feature = "gecko")] use crate::stylesheets::{ CounterStyleRule, FontFaceRule, FontFeatureValuesRule, FontPaletteValuesRule, + PagePseudoClassFlags, }; use crate::stylesheets::{ - CssRule, EffectiveRulesIterator, Origin, OriginSet, PagePseudoClassFlags, PageRule, PerOrigin, + CssRule, EffectiveRulesIterator, Origin, OriginSet, PageRule, PerOrigin, PerOriginIter, StylesheetContents, StylesheetInDocument, }; use crate::values::{computed, AtomIdent}; @@ -57,9 +58,9 @@ use crate::AllocErr; use crate::{Atom, LocalName, Namespace, ShrinkIfNeeded, WeakAtom}; use dom::{DocumentState, ElementState}; use fxhash::FxHashMap; -use malloc_size_of::MallocSizeOf; +use malloc_size_of::{MallocSizeOf, MallocShallowSizeOf, MallocSizeOfOps}; #[cfg(feature = "gecko")] -use malloc_size_of::{MallocShallowSizeOf, MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; +use malloc_size_of::MallocUnconditionalShallowSizeOf; use selectors::attr::{CaseSensitivity, NamespaceConstraint}; use selectors::bloom::BloomFilter; use selectors::matching::{ @@ -542,6 +543,7 @@ pub struct Stylist { stylesheets: StylistStylesheetSet, /// A cache of CascadeDatas for AuthorStylesheetSets (i.e., shadow DOM). + #[cfg_attr(feature = "servo", ignore_malloc_size_of = "XXX: how to handle this?")] author_data_cache: CascadeDataCache, /// If true, the quirks-mode stylesheet is applied. @@ -564,6 +566,7 @@ pub struct Stylist { script_custom_properties: CustomPropertyScriptRegistry, /// Initial values for registered custom properties. + #[ignore_malloc_size_of = "Arc"] initial_values_for_custom_properties: ComputedCustomProperties, /// Flags set from computing registered custom property initial values. @@ -3188,7 +3191,9 @@ impl CascadeData { } } #[cfg(feature = "gecko")] - self.extra_data.sort_by_layer(&self.layers); + { + self.extra_data.sort_by_layer(&self.layers); + } self.animations .sort_with(&self.layers, compare_keyframes_in_same_layer); self.custom_property_registrations.sort(&self.layers) diff --git a/style/values/animated/effects.rs b/style/values/animated/effects.rs index 67557e54b7..e2b37cecbe 100644 --- a/style/values/animated/effects.rs +++ b/style/values/animated/effects.rs @@ -24,4 +24,4 @@ pub type AnimatedFilter = /// An animated value for a single `filter`. #[cfg(not(feature = "gecko"))] -pub type AnimatedFilter = GenericFilter; +pub type AnimatedFilter = GenericFilter; diff --git a/style/values/computed/effects.rs b/style/values/computed/effects.rs index b0a92024ca..ce3498d141 100644 --- a/style/values/computed/effects.rs +++ b/style/values/computed/effects.rs @@ -36,7 +36,7 @@ pub type Filter = GenericFilter< NonNegativeNumber, ZeroToOneNumber, NonNegativeLength, - Impossible, + SimpleShadow, Impossible, >; diff --git a/style/values/computed/font.rs b/style/values/computed/font.rs index 3219fb5815..0e5c128607 100644 --- a/style/values/computed/font.rs +++ b/style/values/computed/font.rs @@ -1205,7 +1205,7 @@ pub type FontStretchFixedPoint = FixedPoint; #[derive( Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, PartialOrd, ToResolvedValue, )] -#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] +#[cfg_attr(feature = "servo", derive(Deserialize, Hash, Serialize))] #[repr(C)] pub struct FontStretch(pub FontStretchFixedPoint); diff --git a/style/values/computed/image.rs b/style/values/computed/image.rs index 8a91d95313..4a317b4912 100644 --- a/style/values/computed/image.rs +++ b/style/values/computed/image.rs @@ -29,7 +29,7 @@ pub use specified::ImageRendering; pub type Image = generic::GenericImage; // Images should remain small, see https://github.com/servo/servo/pull/18430 -size_of_test!(Image, 16); +size_of_test!(Image, 40); /// Computed values for a CSS gradient. /// diff --git a/style/values/computed/length.rs b/style/values/computed/length.rs index bed8d37b74..b7e78183e7 100644 --- a/style/values/computed/length.rs +++ b/style/values/computed/length.rs @@ -19,7 +19,7 @@ use crate::values::{specified, CSSFloat}; use crate::Zero; use app_units::Au; use std::fmt::{self, Write}; -use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub}; +use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Neg, Sub, SubAssign}; use style_traits::{CSSPixel, CssWriter, ToCss}; pub use super::image::Image; @@ -182,8 +182,23 @@ impl MaxSize { #[inline] pub fn to_used_value(&self, percentage_basis: Au) -> Option { match *self { - GenericMaxSize::None => None, - GenericMaxSize::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)), + Self::None | Self::MinContent | Self::MaxContent | Self::FitContent | Self::Stretch => { + None + }, + Self::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)), + Self::AnchorSizeFunction(_) => unreachable!("anchor-size() should be disabled"), + } + } + + /// Convert the computed value into used value if there is enough information. + #[inline] + pub fn maybe_to_used_value(&self, percentage_basis: Option) -> Option { + match *self { + Self::None | Self::MinContent | Self::MaxContent | Self::FitContent | Self::Stretch => { + None + }, + Self::LengthPercentage(ref lp) => lp.maybe_to_used_value(percentage_basis), + Self::AnchorSizeFunction(_) => unreachable!("anchor-size() should be disabled"), } } } @@ -194,8 +209,24 @@ impl Size { #[cfg(feature = "servo")] pub fn to_used_value(&self, percentage_basis: Au) -> Option { match *self { - GenericSize::Auto => None, - GenericSize::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)), + Self::Auto | Self::MinContent | Self::MaxContent | Self::FitContent | Self::Stretch => { + None + }, + Self::LengthPercentage(ref lp) => Some(lp.to_used_value(percentage_basis)), + Self::AnchorSizeFunction(_) => unreachable!("anchor-size() should be disabled"), + } + } + + /// Convert the computed value into used value if there is enough information. + #[inline] + #[cfg(feature = "servo")] + pub fn maybe_to_used_value(&self, percentage_basis: Option) -> Option { + match *self { + Self::Auto | Self::MinContent | Self::MaxContent | Self::FitContent | Self::Stretch => { + None + }, + Self::LengthPercentage(ref lp) => lp.maybe_to_used_value(percentage_basis), + Self::AnchorSizeFunction(_) => unreachable!("anchor-size() should be disabled"), } } @@ -205,15 +236,13 @@ impl Size { match *self { Self::Auto => false, Self::LengthPercentage(ref lp) => lp.is_definitely_zero(), - #[cfg(feature = "gecko")] Self::MinContent | Self::MaxContent | Self::FitContent | - Self::MozAvailable | - Self::WebkitFillAvailable | Self::Stretch | - Self::FitContentFunction(_) | Self::AnchorSizeFunction(_) => false, + #[cfg(feature = "gecko")] + Self::MozAvailable | Self::WebkitFillAvailable | Self::FitContentFunction(_) => false, } } } @@ -457,6 +486,13 @@ impl Sub for CSSPixelLength { } } +impl SubAssign for CSSPixelLength { + #[inline] + fn sub_assign(&mut self, other: Self) { + self.0 -= other.0; + } +} + impl From for Au { #[inline] fn from(len: CSSPixelLength) -> Self { diff --git a/style/values/computed/length_percentage.rs b/style/values/computed/length_percentage.rs index ade53ce5e2..fed5c2a190 100644 --- a/style/values/computed/length_percentage.rs +++ b/style/values/computed/length_percentage.rs @@ -202,9 +202,12 @@ impl ToResolvedValue for LengthPercentage { /// An unpacked `` that borrows the `calc()` variant. #[derive(Clone, Debug, PartialEq, ToCss)] -enum Unpacked<'a> { +pub enum Unpacked<'a> { + /// A `calc()` value Calc(&'a CalcLengthPercentage), + /// A length value Length(Length), + /// A percentage value Percentage(Percentage), } @@ -397,8 +400,10 @@ impl LengthPercentage { } } + /// Unpack the tagged pointer representation of a length-percentage into an enum + /// representation with separate tag and value. #[inline] - fn unpack<'a>(&'a self) -> Unpacked<'a> { + pub fn unpack<'a>(&'a self) -> Unpacked<'a> { unsafe { match self.tag() { Tag::Calc => Unpacked::Calc(&*self.calc_ptr()), diff --git a/style/values/generics/image.rs b/style/values/generics/image.rs index ca1c716052..7865b4fc55 100644 --- a/style/values/generics/image.rs +++ b/style/values/generics/image.rs @@ -13,7 +13,6 @@ use crate::values::generics::Optional; use crate::values::serialize_atom_identifier; use crate::Atom; use crate::Zero; -use servo_arc::Arc; use std::fmt::{self, Write}; use style_traits::{CssWriter, ToCss}; @@ -368,10 +367,9 @@ pub struct PaintWorklet { pub name: Atom, /// The arguments for the worklet. /// TODO: store a parsed representation of the arguments. - #[cfg_attr(feature = "servo", ignore_malloc_size_of = "Arc")] #[compute(no_field_bound)] #[resolve(no_field_bound)] - pub arguments: Vec>, + pub arguments: Vec, } impl ::style_traits::SpecifiedValueInfo for PaintWorklet {} diff --git a/style/values/generics/length.rs b/style/values/generics/length.rs index 6512d7dd7f..73e804c62c 100644 --- a/style/values/generics/length.rs +++ b/style/values/generics/length.rs @@ -7,7 +7,6 @@ use crate::parser::{Parse, ParserContext}; use crate::values::generics::Optional; use crate::values::DashedIdent; -#[cfg(feature = "gecko")] use crate::Zero; use cssparser::Parser; use std::fmt::Write; @@ -75,13 +74,13 @@ impl LengthPercentageOrAuto { } } -impl LengthPercentageOrAuto +impl LengthPercentageOrAuto where - LengthPercentage: Clone, + T: Clone, { /// Resolves `auto` values by calling `f`. #[inline] - pub fn auto_is(&self, f: impl FnOnce() -> LengthPercentage) -> LengthPercentage { + pub fn auto_is(&self, f: impl FnOnce() -> T) -> T { match self { LengthPercentageOrAuto::LengthPercentage(length) => length.clone(), LengthPercentageOrAuto::Auto => f(), @@ -90,7 +89,7 @@ where /// Returns the non-`auto` value, if any. #[inline] - pub fn non_auto(&self) -> Option { + pub fn non_auto(&self) -> Option { match self { LengthPercentageOrAuto::LengthPercentage(length) => Some(length.clone()), LengthPercentageOrAuto::Auto => None, @@ -98,7 +97,7 @@ where } /// Maps the length of this value. - pub fn map(&self, f: impl FnOnce(LengthPercentage) -> T) -> LengthPercentageOrAuto { + pub fn map(&self, f: impl FnOnce(T) -> U) -> LengthPercentageOrAuto { match self { LengthPercentageOrAuto::LengthPercentage(l) => { LengthPercentageOrAuto::LengthPercentage(f(l.clone())) @@ -155,13 +154,10 @@ impl Parse for LengthPercentageOrAuto pub enum GenericSize { LengthPercentage(LengthPercent), Auto, - #[cfg(feature = "gecko")] #[animation(error)] MaxContent, - #[cfg(feature = "gecko")] #[animation(error)] MinContent, - #[cfg(feature = "gecko")] #[animation(error)] FitContent, #[cfg(feature = "gecko")] @@ -172,6 +168,7 @@ pub enum GenericSize { WebkitFillAvailable, #[animation(error)] Stretch, + #[cfg(feature = "gecko")] #[animation(error)] #[css(function = "fit-content")] FitContentFunction(LengthPercent), @@ -234,13 +231,10 @@ impl Size { pub enum GenericMaxSize { LengthPercentage(LengthPercent), None, - #[cfg(feature = "gecko")] #[animation(error)] MaxContent, - #[cfg(feature = "gecko")] #[animation(error)] MinContent, - #[cfg(feature = "gecko")] #[animation(error)] FitContent, #[cfg(feature = "gecko")] @@ -251,6 +245,7 @@ pub enum GenericMaxSize { WebkitFillAvailable, #[animation(error)] Stretch, + #[cfg(feature = "gecko")] #[animation(error)] #[css(function = "fit-content")] FitContentFunction(LengthPercent), @@ -560,6 +555,27 @@ pub enum GenericMargin { ), } +#[cfg(feature = "servo")] +impl GenericMargin { + /// Return true if it is 'auto'. + #[inline] + pub fn is_auto(&self) -> bool { + matches!(self, Self::Auto) + } +} + +#[cfg(feature = "servo")] +impl GenericMargin { + /// Returns true if the computed value is absolute 0 or 0%. + #[inline] + pub fn is_definitely_zero(&self) -> bool { + match self { + Self::LengthPercentage(lp) => lp.is_definitely_zero(), + _ => false, + } + } +} + impl SpecifiedValueInfo for GenericMargin where LP: SpecifiedValueInfo, diff --git a/style/values/generics/position.rs b/style/values/generics/position.rs index c895fc09b3..ab75d9ad9a 100644 --- a/style/values/generics/position.rs +++ b/style/values/generics/position.rs @@ -314,6 +314,13 @@ impl GenericInset { pub fn auto() -> Self { Self::Auto } + + /// Return true if it is 'auto'. + #[inline] + #[cfg(feature = "servo")] + pub fn is_auto(&self) -> bool { + matches!(self, Self::Auto) + } } pub use self::GenericInset as Inset; diff --git a/style/values/generics/transform.rs b/style/values/generics/transform.rs index 0d26dc2c9e..70dba82a05 100644 --- a/style/values/generics/transform.rs +++ b/style/values/generics/transform.rs @@ -604,6 +604,13 @@ impl Transform { } /// Same as Transform::to_transform_3d_matrix but a f64 version. + pub fn to_transform_3d_matrix_f64( + &self, + reference_box: Option<&Rect> + ) -> Result<(Transform3D, bool), ()> { + Self::components_to_transform_3d_matrix_f64(&self.0, reference_box) + } + fn components_to_transform_3d_matrix_f64( ops: &[T], reference_box: Option<&Rect>, diff --git a/style/values/mod.rs b/style/values/mod.rs index ef0ed1fb0c..ad416b09d8 100644 --- a/style/values/mod.rs +++ b/style/values/mod.rs @@ -297,6 +297,16 @@ impl cssparser::ToCss for GenericAtomIdent style_traits::ToCss for GenericAtomIdent { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + serialize_atom_identifier(&self.0, dest) + } +} + #[cfg(feature = "servo")] impl PrecomputedHash for GenericAtomIdent { #[inline] @@ -718,7 +728,7 @@ impl ToCss for KeyframesName { return dest.write_str("none"); } - let serialize = |string: &_| { + let mut serialize = |string: &_| { if CustomIdent::is_valid(string, &["none"]) { serialize_identifier(string, dest) } else { diff --git a/style/values/specified/animation.rs b/style/values/specified/animation.rs index fd12e43c14..a711661609 100644 --- a/style/values/specified/animation.rs +++ b/style/values/specified/animation.rs @@ -311,7 +311,7 @@ impl AnimationPlayState { #[cfg(feature = "gecko")] return name.with_str(|n| Self::from_ident(n).is_ok()); #[cfg(feature = "servo")] - return Self::from_ident(atom).is_ok(); + return Self::from_ident(name).is_ok(); } false } @@ -346,7 +346,10 @@ impl AnimationFillMode { #[inline] pub fn match_keywords(name: &AnimationName) -> bool { if let Some(atom) = name.as_atom() { + #[cfg(feature = "gecko")] return !name.is_none() && atom.with_str(|n| Self::from_ident(n).is_ok()); + #[cfg(feature = "servo")] + return !name.is_none() && Self::from_ident(atom).is_ok(); } false } diff --git a/style/values/specified/box.rs b/style/values/specified/box.rs index 93f26b6325..f227ad61d4 100644 --- a/style/values/specified/box.rs +++ b/style/values/specified/box.rs @@ -5,7 +5,6 @@ //! Specified types for box properties. use crate::parser::{Parse, ParserContext}; -#[cfg(feature = "gecko")] use crate::properties::{LonghandId, PropertyDeclarationId, PropertyId}; use crate::values::generics::box_::{ GenericContainIntrinsicSize, GenericLineClamp, GenericPerspective, GenericVerticalAlign, @@ -16,7 +15,7 @@ use crate::values::specified::{AllowQuirks, Integer, NonNegativeNumberOrPercenta use crate::values::CustomIdent; use cssparser::Parser; use num_traits::FromPrimitive; -use std::fmt::{self, Write}; +use std::fmt::{self, Debug, Write}; use style_traits::{CssWriter, KeywordsCollectFn, ParseError}; use style_traits::{SpecifiedValueInfo, StyleParseErrorKind, ToCss}; @@ -31,10 +30,7 @@ fn grid_enabled() -> bool { #[cfg(feature = "servo")] fn flexbox_enabled() -> bool { - servo_config::prefs::pref_map() - .get("layout.flexbox.enabled") - .as_bool() - .unwrap_or(false) + style_config::get_bool("layout.flexbox.enabled") } #[cfg(feature = "servo")] fn grid_enabled() -> bool { @@ -1025,7 +1021,7 @@ bitflags! { } #[cfg(feature="servo")] -fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits { WillChangeBits::empty() } +fn change_bits_for_longhand(_longhand: LonghandId) -> WillChangeBits { WillChangeBits::empty() } #[cfg(feature = "gecko")] fn change_bits_for_longhand(longhand: LonghandId) -> WillChangeBits { diff --git a/style/values/specified/effects.rs b/style/values/specified/effects.rs index 64e7ed290c..8ed38b739c 100644 --- a/style/values/specified/effects.rs +++ b/style/values/specified/effects.rs @@ -53,7 +53,7 @@ pub type SpecifiedFilter = GenericFilter< NonNegativeFactor, ZeroToOneFactor, NonNegativeLength, - Impossible, + SimpleShadow, Impossible, >; diff --git a/style/values/specified/font.rs b/style/values/specified/font.rs index ee01506248..0457ee8f4d 100644 --- a/style/values/specified/font.rs +++ b/style/values/specified/font.rs @@ -771,48 +771,41 @@ pub const FONT_MEDIUM_PX: f32 = 16.0; pub const FONT_MEDIUM_LINE_HEIGHT_PX: f32 = FONT_MEDIUM_PX * 1.2; impl FontSizeKeyword { - #[inline] - #[cfg(feature = "servo")] - fn to_length(&self, _: &Context) -> NonNegativeLength { - let medium = Length::new(FONT_MEDIUM_PX); - // https://drafts.csswg.org/css-fonts-3/#font-size-prop - NonNegative(match *self { - FontSizeKeyword::XXSmall => medium * 3.0 / 5.0, - FontSizeKeyword::XSmall => medium * 3.0 / 4.0, - FontSizeKeyword::Small => medium * 8.0 / 9.0, - FontSizeKeyword::Medium => medium, - FontSizeKeyword::Large => medium * 6.0 / 5.0, - FontSizeKeyword::XLarge => medium * 3.0 / 2.0, - FontSizeKeyword::XXLarge => medium * 2.0, - FontSizeKeyword::XXXLarge => medium * 3.0, - FontSizeKeyword::Math | FontSizeKeyword::None => unreachable!(), - }) - } - - #[cfg(feature = "gecko")] #[inline] fn to_length(&self, cx: &Context) -> NonNegativeLength { let font = cx.style().get_font(); + + #[cfg(feature = "servo")] + let family = &font.font_family.families; + + #[cfg(feature = "gecko")] let family = &font.mFont.family.families; + let generic = family .single_generic() .unwrap_or(computed::GenericFontFamily::None); + + #[cfg(feature = "gecko")] let base_size = unsafe { Atom::with(font.mLanguage.mRawPtr, |language| { cx.device().base_size_for_generic(language, generic) }) }; + + #[cfg(feature = "servo")] + let base_size = cx.device().base_size_for_generic(generic); + self.to_length_without_context(cx.quirks_mode, base_size) } /// Resolve a keyword length without any context, with explicit arguments. - #[cfg(feature = "gecko")] #[inline] pub fn to_length_without_context( &self, quirks_mode: QuirksMode, base_size: Length, ) -> NonNegativeLength { + #[cfg(feature = "gecko")] debug_assert_ne!(*self, FontSizeKeyword::Math); // The tables in this function are originally from // nsRuleNode::CalcFontPointSize in Gecko: @@ -932,6 +925,7 @@ impl FontSize { calc.resolve(base_size.resolve(context).computed_size()) }, FontSize::Keyword(i) => { + #[cfg(feature="gecko")] if i.kw == FontSizeKeyword::Math { // Scaling is done in recompute_math_font_size_if_needed(). info = compose_keyword(1.); @@ -946,6 +940,11 @@ impl FontSize { info = i; i.to_computed_value(context).clamp_to_non_negative() } + #[cfg(feature="servo")] { + // As a specified keyword, this is keyword derived + info = i; + i.to_computed_value(context).clamp_to_non_negative() + } }, FontSize::Smaller => { info = compose_keyword(1. / LARGER_FONT_SIZE_RATIO); diff --git a/style/values/specified/image.rs b/style/values/specified/image.rs index 5d494a44c7..8ce05399f6 100644 --- a/style/values/specified/image.rs +++ b/style/values/specified/image.rs @@ -43,7 +43,7 @@ fn gradient_color_interpolation_method_enabled() -> bool { pub type Image = generic::Image; // Images should remain small, see https://github.com/servo/servo/pull/18430 -size_of_test!(Image, 16); +size_of_test!(Image, 40); /// Specified values for a CSS gradient. /// @@ -1274,13 +1274,13 @@ impl generic::ColorStop { impl PaintWorklet { #[cfg(feature = "servo")] - fn parse_args<'i>(input: &mut Parser<'i, '_>) -> Result> { + fn parse_args<'i>(context: &ParserContext, input: &mut Parser<'i, '_>) -> Result> { use crate::custom_properties::SpecifiedValue; let name = Atom::from(&**input.expect_ident()?); let arguments = input .try_parse(|input| { input.expect_comma()?; - input.parse_comma_separated(SpecifiedValue::parse) + input.parse_comma_separated(|input| SpecifiedValue::parse(input, &context.url_data)) }) .unwrap_or_default(); Ok(Self { name, arguments }) diff --git a/style/values/specified/length.rs b/style/values/specified/length.rs index 352c14314c..95951ec5ce 100644 --- a/style/values/specified/length.rs +++ b/style/values/specified/length.rs @@ -1231,6 +1231,7 @@ impl NoCalcLength { /// absolute or (if a font metrics getter is provided) font-relative units. #[cfg(feature = "gecko")] #[inline] + #[cfg(feature = "gecko")] pub fn to_computed_pixel_length_with_font_metrics( &self, get_font_metrics: Option GeckoFontMetrics>, @@ -2008,17 +2009,13 @@ macro_rules! parse_size_non_length { ($size:ident, $input:expr, $auto_or_none:expr => $auto_or_none_ident:ident) => {{ let size = $input.try_parse(|input| { Ok(try_match_ident_ignore_ascii_case! { input, - #[cfg(feature = "gecko")] "min-content" | "-moz-min-content" => $size::MinContent, - #[cfg(feature = "gecko")] "max-content" | "-moz-max-content" => $size::MaxContent, - #[cfg(feature = "gecko")] "fit-content" | "-moz-fit-content" => $size::FitContent, #[cfg(feature = "gecko")] "-moz-available" => $size::MozAvailable, #[cfg(feature = "gecko")] "-webkit-fill-available" if is_webkit_fill_available_keyword_enabled() => $size::WebkitFillAvailable, - #[cfg(feature = "gecko")] "stretch" if is_stretch_enabled() => $size::Stretch, $auto_or_none => $size::$auto_or_none_ident, }) @@ -2033,7 +2030,6 @@ macro_rules! parse_size_non_length { fn is_webkit_fill_available_keyword_enabled() -> bool { static_prefs::pref!("layout.css.webkit-fill-available.enabled") } -#[cfg(feature = "gecko")] fn is_stretch_enabled() -> bool { static_prefs::pref!("layout.css.stretch-size-keyword.enabled") } @@ -2042,11 +2038,8 @@ fn is_stretch_enabled() -> bool { fn is_fit_content_function_enabled() -> bool { static_prefs::pref!("layout.css.fit-content-function.enabled") } -#[cfg(feature = "servo")] -fn is_fit_content_function_enabled() -> bool { - false -} +#[cfg(feature = "gecko")] macro_rules! parse_fit_content_function { ($size:ident, $input:expr, $context:expr, $allow_quirks:expr) => { if is_fit_content_function_enabled() { @@ -2070,6 +2063,7 @@ 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); if let Ok(length) = @@ -2109,6 +2103,7 @@ 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); if let Ok(length) = diff --git a/style/values/specified/list.rs b/style/values/specified/list.rs index d085bcf189..d10aaa96d8 100644 --- a/style/values/specified/list.rs +++ b/style/values/specified/list.rs @@ -7,6 +7,8 @@ #[cfg(feature = "gecko")] use crate::counter_style::{CounterStyle, CounterStyleParsingFlags}; use crate::parser::{Parse, ParserContext}; +#[cfg(feature = "servo")] +use crate::properties::longhands::list_style_type::SpecifiedValue as ListStyleType; use cssparser::{Parser, Token}; use style_traits::{ParseError, StyleParseErrorKind}; diff --git a/style/values/specified/text.rs b/style/values/specified/text.rs index 1e9c7a6819..b99d8fd1c2 100644 --- a/style/values/specified/text.rs +++ b/style/values/specified/text.rs @@ -365,10 +365,15 @@ bitflags! { /// Capitalize each word. const CAPITALIZE = 1 << 2; /// Automatic italicization of math variables. + #[cfg(feature = "gecko")] const MATH_AUTO = 1 << 3; /// All the case transforms, which are exclusive with each other. + #[cfg(feature = "gecko")] const CASE_TRANSFORMS = Self::UPPERCASE.0 | Self::LOWERCASE.0 | Self::CAPITALIZE.0 | Self::MATH_AUTO.0; + /// All the case transforms, which are exclusive with each other. + #[cfg(feature = "servo")] + const CASE_TRANSFORMS = Self::UPPERCASE.0 | Self::LOWERCASE.0 | Self::CAPITALIZE.0; /// full-width const FULL_WIDTH = 1 << 4; @@ -395,6 +400,19 @@ impl TextTransform { // Case bits are exclusive with each other. case.is_empty() || case.bits().is_power_of_two() } + + /// Returns the corresponding TextTransformCase. + pub fn case(&self) -> TextTransformCase { + match *self & Self::CASE_TRANSFORMS { + Self::NONE => TextTransformCase::None, + Self::UPPERCASE => TextTransformCase::Uppercase, + Self::LOWERCASE => TextTransformCase::Lowercase, + Self::CAPITALIZE => TextTransformCase::Capitalize, + #[cfg(feature = "gecko")] + Self::MATH_AUTO => TextTransformCase::MathAuto, + _ => unreachable!("Case bits are exclusive with each other"), + } + } } /// Specified and computed value of text-align-last. @@ -483,7 +501,6 @@ pub enum TextAlign { /// Since selectors can't depend on the ancestor styles, we implement it with a /// magic value that computes to the right thing. Since this is an /// implementation detail, it shouldn't be exposed to web content. - #[cfg(feature = "gecko")] #[parse(condition = "ParserContext::chrome_rules_enabled")] MozCenterOrInherit, } @@ -519,7 +536,6 @@ impl ToComputedValue for TextAlign { _ => parent, } }, - #[cfg(feature = "gecko")] TextAlign::MozCenterOrInherit => { let parent = _context .builder diff --git a/style_config/Cargo.toml b/style_config/Cargo.toml new file mode 100644 index 0000000000..0076dbf6e8 --- /dev/null +++ b/style_config/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "style_config" +version = "0.0.1" +authors = ["The Servo Project Developers"] +license = "MPL-2.0" +edition = "2021" +publish = false + +[lib] +name = "style_config" +path = "lib.rs" + +[dependencies] +lazy_static = "1.4" diff --git a/style_config/lib.rs b/style_config/lib.rs new file mode 100644 index 0000000000..4881ea6df6 --- /dev/null +++ b/style_config/lib.rs @@ -0,0 +1,95 @@ +/* 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/. */ + +use std::collections::HashMap; +use std::sync::RwLock; + +use lazy_static::lazy_static; + +lazy_static! { + static ref PREFS: Preferences = Preferences::default(); +} + +#[derive(Debug, Default)] +pub struct Preferences { + bool_prefs: RwLock>, + i32_prefs: RwLock>, +} + +impl Preferences { + pub fn get_bool(&self, key: &str) -> bool { + let prefs = self.bool_prefs.read().expect("RwLock is poisoned"); + *prefs.get(key).unwrap_or(&false) + } + + pub fn get_i32(&self, key: &str) -> i32 { + let prefs = self.i32_prefs.read().expect("RwLock is poisoned"); + *prefs.get(key).unwrap_or(&0) + } + + pub fn set_bool(&self, key: &str, value: bool) { + let mut prefs = self.bool_prefs.write().expect("RwLock is poisoned"); + + // Avoid cloning the key if it exists. + if let Some(pref) = prefs.get_mut(key) { + *pref = value; + } else { + prefs.insert(key.to_owned(), value); + } + } + + pub fn set_i32(&self, key: &str, value: i32) { + let mut prefs = self.i32_prefs.write().expect("RwLock is poisoned"); + + // Avoid cloning the key if it exists. + if let Some(pref) = prefs.get_mut(key) { + *pref = value; + } else { + prefs.insert(key.to_owned(), value); + } + } +} + +pub fn get_bool(key: &str) -> bool { + PREFS.get_bool(key) +} + +pub fn get_i32(key: &str) -> i32 { + PREFS.get_i32(key) +} + +pub fn set_bool(key: &str, value: bool) { + PREFS.set_bool(key, value) +} + +pub fn set_i32(key: &str, value: i32) { + PREFS.set_i32(key, value) +} + +#[test] +fn test() { + let prefs = Preferences::default(); + + // Prefs have default values when unset. + assert_eq!(prefs.get_bool("foo"), false); + assert_eq!(prefs.get_i32("bar"), 0); + + // Prefs can be set and retrieved. + prefs.set_bool("foo", true); + prefs.set_i32("bar", 1); + assert_eq!(prefs.get_bool("foo"), true); + assert_eq!(prefs.get_i32("bar"), 1); + prefs.set_bool("foo", false); + prefs.set_i32("bar", 2); + assert_eq!(prefs.get_bool("foo"), false); + assert_eq!(prefs.get_i32("bar"), 2); + + // Each value type currently has an independent namespace. + prefs.set_i32("foo", 3); + prefs.set_bool("bar", true); + assert_eq!(prefs.get_i32("foo"), 3); + assert_eq!(prefs.get_bool("foo"), false); + assert_eq!(prefs.get_bool("bar"), true); + assert_eq!(prefs.get_i32("bar"), 2); +} diff --git a/style_static_prefs/Cargo.toml b/style_static_prefs/Cargo.toml new file mode 100644 index 0000000000..7a42225d8e --- /dev/null +++ b/style_static_prefs/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "static_prefs" +version = "0.1.0" +edition = "2021" +authors = ["The Servo Project Developers"] +license = "MPL-2.0" +publish = false diff --git a/style_static_prefs/src/lib.rs b/style_static_prefs/src/lib.rs new file mode 100644 index 0000000000..97c27c3ac9 --- /dev/null +++ b/style_static_prefs/src/lib.rs @@ -0,0 +1,39 @@ +/* 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/. */ + +//! A list of static preferences exposed to the style crate. These should +//! be kept sync with the preferences used by the style. +#[macro_export] +macro_rules! pref { + ("layout.css.stylo-local-work-queue.in-main-thread") => { + 32 + }; + ("layout.css.stylo-work-unit-size") => { + 16 + }; + ("layout.css.stylo-local-work-queue.in-worker") => { + 0 + }; + ("layout.css.system-ui.enabled") => { + true + }; + ("layout.css.basic-shape-rect.enabled") => { + true + }; + ("layout.css.basic-shape-xywh.enabled") => { + true + }; + ("layout.css.relative-color-syntax.enabled") => { + true + }; + ("layout.css.stretch-size-keyword.enabled") => { + true + }; + ("layout.css.transition-behavior.enabled") => { + true + }; + ($string:literal) => { + false + }; +} diff --git a/style_traits/Cargo.toml b/style_traits/Cargo.toml index 6e456c80e4..f558c4cef2 100644 --- a/style_traits/Cargo.toml +++ b/style_traits/Cargo.toml @@ -12,7 +12,7 @@ path = "lib.rs" [features] servo = ["servo_atoms", "cssparser/serde", "url", "euclid/serde"] -gecko = ["nsstring"] +gecko = [] [dependencies] app_units = "0.7" @@ -21,8 +21,7 @@ cssparser = "0.34" euclid = "0.22" lazy_static = "1" malloc_size_of = { path = "../malloc_size_of" } -malloc_size_of_derive = { path = "../../../xpcom/rust/malloc_size_of_derive" } -nsstring = {path = "../../../xpcom/rust/nsstring/", optional = true} +malloc_size_of_derive = "0.1" selectors = { path = "../selectors" } serde = "1.0" servo_arc = { path = "../servo_arc" } diff --git a/style_traits/lib.rs b/style_traits/lib.rs index 7e155538fb..66a07884d2 100644 --- a/style_traits/lib.rs +++ b/style_traits/lib.rs @@ -28,12 +28,12 @@ extern crate serde; extern crate servo_arc; #[cfg(feature = "servo")] extern crate servo_atoms; -#[cfg(feature = "servo")] -extern crate servo_url; extern crate thin_vec; extern crate to_shmem; #[macro_use] extern crate to_shmem_derive; +#[cfg(feature = "servo")] +extern crate url; use cssparser::{CowRcStr, Token}; use selectors::parser::SelectorParseErrorKind; diff --git a/style_traits/values.rs b/style_traits/values.rs index e406bb7b1c..e2b87b0229 100644 --- a/style_traits/values.rs +++ b/style_traits/values.rs @@ -100,6 +100,7 @@ pub trait ToCss { /// /// (This is a convenience wrapper for `to_css` and probably should not be overridden.) #[inline] + #[cfg(feature = "gecko")] fn to_css_nscstring(&self) -> nsCString { let mut s = nsCString::new(); self.to_css(&mut CssWriter::new(&mut s)).unwrap();