diff --git a/Cargo.lock b/Cargo.lock index 2afabed30bf..441a6b5cd61 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1716,6 +1716,7 @@ dependencies = [ "canvas_traits 0.0.1", "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "gfx 0.0.1", "gfx_traits 0.0.1", "html5ever 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1759,6 +1760,7 @@ dependencies = [ "embedder_traits 0.0.1", "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "gfx 0.0.1", "gfx_traits 0.0.1", "histogram 0.6.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1984,6 +1986,7 @@ dependencies = [ "smallbitvec 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", "string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", + "thin-slice 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3088,6 +3091,7 @@ dependencies = [ "bitflags 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "cssparser 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "phf 0.7.21 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3095,6 +3099,7 @@ dependencies = [ "precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "servo_arc 0.1.1", "smallvec 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)", + "thin-slice 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -3498,7 +3503,7 @@ dependencies = [ "encoding_rs 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", "fallible 0.0.1", - "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", + "fxhash 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "hashglobe 0.1.0", "html5ever 0.22.3 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3530,6 +3535,7 @@ dependencies = [ "string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)", "style_derive 0.0.1", "style_traits 0.0.1", + "thin-slice 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "uluru 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3680,6 +3686,11 @@ dependencies = [ "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "thin-slice" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "thread_local" version = "0.3.5" @@ -4528,6 +4539,7 @@ dependencies = [ "checksum termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" +"checksum thin-slice 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8eaa81235c7058867fa8c0e7314f33dcce9c215f535d1913822a2b3f5e289f3c" "checksum thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "279ef31c19ededf577bfd12dfae728040a21f635b06a24cd670ff510edd38963" "checksum thread_profiler 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "5920e77802b177479ab5795767fa48e68f61b2f516c2ac0041e2978dd8efe483" "checksum threadpool 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "59f6d3eff89920113dac9db44dde461d71d01e88a5b57b258a0466c32b5d7fe1" diff --git a/components/layout/Cargo.toml b/components/layout/Cargo.toml index d0604de2c48..ac387d59585 100644 --- a/components/layout/Cargo.toml +++ b/components/layout/Cargo.toml @@ -18,6 +18,7 @@ bitflags = "1.0" canvas_traits = {path = "../canvas_traits"} euclid = "0.19" fnv = "1.0" +fxhash = "0.2" gfx = {path = "../gfx"} gfx_traits = {path = "../gfx_traits"} html5ever = "0.22" diff --git a/components/layout/animation.rs b/components/layout/animation.rs index 05718dda00a..3a09094b2a9 100644 --- a/components/layout/animation.rs +++ b/components/layout/animation.rs @@ -7,7 +7,7 @@ use context::LayoutContext; use display_list::items::OpaqueNode; use flow::{Flow, GetBaseFlow}; -use fnv::FnvHashMap; +use fxhash::FxHashMap; use ipc_channel::ipc::IpcSender; use msg::constellation_msg::PipelineId; use opaque_node::OpaqueNodeMethods; @@ -26,8 +26,8 @@ use style::timer::Timer; pub fn update_animation_state( constellation_chan: &IpcSender, script_chan: &IpcSender, - running_animations: &mut FnvHashMap>, - expired_animations: &mut FnvHashMap>, + running_animations: &mut FxHashMap>, + expired_animations: &mut FxHashMap>, mut newly_transitioning_nodes: Option<&mut Vec>, new_animations_receiver: &Receiver, pipeline_id: PipelineId, @@ -153,7 +153,7 @@ where pub fn recalc_style_for_animations( context: &LayoutContext, flow: &mut Flow, - animations: &FnvHashMap>, + animations: &FxHashMap>, ) where E: TElement, diff --git a/components/layout/lib.rs b/components/layout/lib.rs index bb3a3dc20b2..c0c5633ca24 100644 --- a/components/layout/lib.rs +++ b/components/layout/lib.rs @@ -11,6 +11,7 @@ extern crate bitflags; extern crate canvas_traits; extern crate euclid; extern crate fnv; +extern crate fxhash; extern crate gfx; extern crate gfx_traits; #[macro_use] extern crate html5ever; diff --git a/components/layout_thread/Cargo.toml b/components/layout_thread/Cargo.toml index 125c57946f6..a17f90e899e 100644 --- a/components/layout_thread/Cargo.toml +++ b/components/layout_thread/Cargo.toml @@ -18,6 +18,7 @@ atomic_refcell = "0.1" embedder_traits = {path = "../embedder_traits"} euclid = "0.19" fnv = "1.0" +fxhash = "0.2" gfx = {path = "../gfx"} gfx_traits = {path = "../gfx_traits"} histogram = "0.6.8" diff --git a/components/layout_thread/dom_wrapper.rs b/components/layout_thread/dom_wrapper.rs index 89a21ec64d5..7b2892f7c9a 100644 --- a/components/layout_thread/dom_wrapper.rs +++ b/components/layout_thread/dom_wrapper.rs @@ -675,14 +675,6 @@ impl<'le> ::selectors::Element for ServoLayoutElement<'le> { None } - fn first_child_element(&self) -> Option> { - self.as_node().dom_children().filter_map(|n| n.as_element()).next() - } - - fn last_child_element(&self) -> Option> { - self.as_node().rev_children().filter_map(|n| n.as_element()).next() - } - fn prev_sibling_element(&self) -> Option> { let mut node = self.as_node(); while let Some(sibling) = node.prev_sibling() { @@ -1223,17 +1215,6 @@ impl<'le> ::selectors::Element for ServoThreadSafeLayoutElement<'le> { None } - fn first_child_element(&self) -> Option { - warn!("ServoThreadSafeLayoutElement::first_child_element called"); - None - } - - // Skips non-element nodes - fn last_child_element(&self) -> Option { - warn!("ServoThreadSafeLayoutElement::last_child_element called"); - None - } - // Skips non-element nodes fn prev_sibling_element(&self) -> Option { warn!("ServoThreadSafeLayoutElement::prev_sibling_element called"); diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 3c39b95a28b..1d5676b4b54 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -12,6 +12,7 @@ extern crate atomic_refcell; extern crate embedder_traits; extern crate euclid; extern crate fnv; +extern crate fxhash; extern crate gfx; extern crate gfx_traits; extern crate histogram; @@ -59,6 +60,7 @@ use dom_wrapper::drop_style_and_layout_data; use embedder_traits::resources::{self, Resource}; use euclid::{Point2D, Rect, Size2D, TypedScale, TypedSize2D}; use fnv::FnvHashMap; +use fxhash::FxHashMap; use gfx::font; use gfx::font_cache_thread::FontCacheThread; use gfx::font_context; @@ -223,10 +225,10 @@ pub struct LayoutThread { document_shared_lock: Option, /// The list of currently-running animations. - running_animations: ServoArc>>>, + running_animations: ServoArc>>>, /// The list of animations that have expired since the last style recalculation. - expired_animations: ServoArc>>>, + expired_animations: ServoArc>>>, /// A counter for epoch messages epoch: Cell, @@ -503,7 +505,7 @@ impl LayoutThread { constellation_chan: constellation_chan.clone(), time_profiler_chan: time_profiler_chan, mem_profiler_chan: mem_profiler_chan, - registered_painters: RegisteredPaintersImpl(FnvHashMap::default()), + registered_painters: RegisteredPaintersImpl(Default::default()), image_cache: image_cache.clone(), font_cache_thread: font_cache_thread, first_reflow: Cell::new(true), @@ -517,8 +519,8 @@ impl LayoutThread { outstanding_web_fonts: Arc::new(AtomicUsize::new(0)), root_flow: RefCell::new(None), document_shared_lock: None, - running_animations: ServoArc::new(RwLock::new(FnvHashMap::default())), - expired_animations: ServoArc::new(RwLock::new(FnvHashMap::default())), + running_animations: ServoArc::new(RwLock::new(Default::default())), + expired_animations: ServoArc::new(RwLock::new(Default::default())), epoch: Cell::new(Epoch(0)), viewport_size: Size2D::new(Au(0), Au(0)), webrender_api: webrender_api_sender.create_api(), @@ -1813,7 +1815,8 @@ lazy_static! { struct RegisteredPainterImpl { painter: Box, name: Atom, - properties: FnvHashMap, + // FIXME: Should be a PrecomputedHashMap. + properties: FxHashMap, } impl SpeculativePainter for RegisteredPainterImpl { @@ -1823,7 +1826,7 @@ impl SpeculativePainter for RegisteredPainterImpl { } impl RegisteredSpeculativePainter for RegisteredPainterImpl { - fn properties(&self) -> &FnvHashMap { + fn properties(&self) -> &FxHashMap { &self.properties } fn name(&self) -> Atom { diff --git a/components/malloc_size_of/Cargo.toml b/components/malloc_size_of/Cargo.toml index 746b1566b14..dafeb9381ec 100644 --- a/components/malloc_size_of/Cargo.toml +++ b/components/malloc_size_of/Cargo.toml @@ -37,6 +37,7 @@ servo_arc = { path = "../servo_arc" } smallbitvec = "2.1.0" smallvec = "0.6" string_cache = { version = "0.7", optional = true } +thin-slice = "0.1.0" time = { version = "0.1.17", optional = true } url = { version = "1.2", optional = true } webrender_api = { git = "https://github.com/servo/webrender", features = ["ipc"], optional = true } diff --git a/components/malloc_size_of/lib.rs b/components/malloc_size_of/lib.rs index 641d276483e..f4774d66a3e 100644 --- a/components/malloc_size_of/lib.rs +++ b/components/malloc_size_of/lib.rs @@ -63,6 +63,7 @@ extern crate smallbitvec; extern crate smallvec; #[cfg(feature = "servo")] extern crate string_cache; +extern crate thin_slice; #[cfg(feature = "servo")] extern crate time; #[cfg(feature = "url")] @@ -231,6 +232,24 @@ impl MallocSizeOf for Box { } } +impl MallocShallowSizeOf for thin_slice::ThinBoxedSlice { + fn shallow_size_of(&self, ops: &mut MallocSizeOfOps) -> usize { + let mut n = 0; + unsafe { + n += thin_slice::ThinBoxedSlice::spilled_storage(self) + .map_or(0, |ptr| ops.malloc_size_of(ptr)); + n += ops.malloc_size_of(&**self); + } + n + } +} + +impl MallocSizeOf for thin_slice::ThinBoxedSlice { + fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize { + self.shallow_size_of(ops) + (**self).size_of(ops) + } +} + impl MallocSizeOf for () { fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { 0 @@ -770,7 +789,7 @@ where } impl MallocSizeOf - for selectors::attr::AttrSelectorWithNamespace + for selectors::attr::AttrSelectorWithOptionalNamespace { fn size_of(&self, _ops: &mut MallocSizeOfOps) -> usize { 0 diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 547367e2f36..af24036efd2 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -2640,14 +2640,6 @@ impl<'a> SelectorsElement for DomRoot { false } - fn first_child_element(&self) -> Option> { - self.node.child_elements().next() - } - - fn last_child_element(&self) -> Option> { - self.node.rev_children().filter_map(DomRoot::downcast).next() - } - fn prev_sibling_element(&self) -> Option> { self.node.preceding_siblings().filter_map(DomRoot::downcast).next() } diff --git a/components/selectors/Cargo.toml b/components/selectors/Cargo.toml index e8b4fbe2fe3..20ca61fb784 100644 --- a/components/selectors/Cargo.toml +++ b/components/selectors/Cargo.toml @@ -25,10 +25,12 @@ matches = "0.1" cssparser = "0.24.0" log = "0.4" fnv = "1.0" +fxhash = "0.2" phf = "0.7.18" precomputed-hash = "0.1" servo_arc = { version = "0.1", path = "../servo_arc" } smallvec = "0.6.2" +thin-slice = "0.1.0" [build-dependencies] phf_codegen = "0.7.18" diff --git a/components/selectors/attr.rs b/components/selectors/attr.rs index b36be8a0a68..c0f5fe73185 100644 --- a/components/selectors/attr.rs +++ b/components/selectors/attr.rs @@ -7,20 +7,20 @@ use parser::SelectorImpl; use std::fmt; #[derive(Clone, Eq, PartialEq)] -pub struct AttrSelectorWithNamespace { - pub namespace: NamespaceConstraint<(Impl::NamespacePrefix, Impl::NamespaceUrl)>, +pub struct AttrSelectorWithOptionalNamespace { + pub namespace: Option>, pub local_name: Impl::LocalName, pub local_name_lower: Impl::LocalName, pub operation: ParsedAttrSelectorOperation, pub never_matches: bool, } -impl AttrSelectorWithNamespace { - pub fn namespace(&self) -> NamespaceConstraint<&Impl::NamespaceUrl> { - match self.namespace { +impl AttrSelectorWithOptionalNamespace { + pub fn namespace(&self) -> Option> { + self.namespace.as_ref().map(|ns| match ns { NamespaceConstraint::Any => NamespaceConstraint::Any, NamespaceConstraint::Specific((_, ref url)) => NamespaceConstraint::Specific(url), - } + }) } } diff --git a/components/selectors/bloom.rs b/components/selectors/bloom.rs index e4cd0a77f98..16dabf498f9 100644 --- a/components/selectors/bloom.rs +++ b/components/selectors/bloom.rs @@ -297,6 +297,9 @@ impl Clone for BloomStorageBool { } fn hash(elem: &T) -> u32 { + // We generally use FxHasher in Stylo because it's faster than FnvHasher, + // but the increased collision rate has outsized effect on the bloom + // filter, so we use FnvHasher instead here. let mut hasher = FnvHasher::default(); elem.hash(&mut hasher); let hash: u64 = hasher.finish(); diff --git a/components/selectors/lib.rs b/components/selectors/lib.rs index eb73192daee..aa30604bbd1 100644 --- a/components/selectors/lib.rs +++ b/components/selectors/lib.rs @@ -10,6 +10,7 @@ extern crate bitflags; #[macro_use] extern crate cssparser; extern crate fnv; +extern crate fxhash; #[macro_use] extern crate log; #[macro_use] @@ -18,6 +19,7 @@ extern crate phf; extern crate precomputed_hash; extern crate servo_arc; extern crate smallvec; +extern crate thin_slice; pub mod attr; pub mod bloom; diff --git a/components/selectors/matching.rs b/components/selectors/matching.rs index fd4765c2dff..1fe54b217d2 100644 --- a/components/selectors/matching.rs +++ b/components/selectors/matching.rs @@ -699,7 +699,6 @@ where }, Component::AttributeInNoNamespace { ref local_name, - ref local_name_lower, ref value, operator, case_sensitivity, @@ -711,7 +710,7 @@ where let is_html = element.is_html_element_in_html_document(); element.attr_matches( &NamespaceConstraint::Specific(&::parser::namespace_empty_string::()), - select_name(is_html, local_name, local_name_lower), + local_name, &AttrSelectorOperation::WithValue { operator: operator, case_sensitivity: case_sensitivity.to_unconditional(is_html), @@ -724,8 +723,16 @@ where return false; } let is_html = element.is_html_element_in_html_document(); + let empty_string; + let namespace = match attr_sel.namespace() { + Some(ns) => ns, + None => { + empty_string = ::parser::namespace_empty_string::(); + NamespaceConstraint::Specific(&empty_string) + } + }; element.attr_matches( - &attr_sel.namespace(), + &namespace, select_name(is_html, &attr_sel.local_name, &attr_sel.local_name_lower), &match attr_sel.operation { ParsedAttrSelectorOperation::Exists => AttrSelectorOperation::Exists, diff --git a/components/selectors/nth_index_cache.rs b/components/selectors/nth_index_cache.rs index c334365c60d..d1de6b0dc02 100644 --- a/components/selectors/nth_index_cache.rs +++ b/components/selectors/nth_index_cache.rs @@ -2,7 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use fnv::FnvHashMap; +use fxhash::FxHashMap; use tree::OpaqueElement; /// A cache to speed up matching of nth-index-like selectors. @@ -32,7 +32,7 @@ impl NthIndexCache { /// The concrete per-pseudo-class cache. #[derive(Default)] -pub struct NthIndexCacheInner(FnvHashMap); +pub struct NthIndexCacheInner(FxHashMap); impl NthIndexCacheInner { /// Does a lookup for a given element in the cache. diff --git a/components/selectors/parser.rs b/components/selectors/parser.rs index 399f21e6361..d0f98cde57b 100644 --- a/components/selectors/parser.rs +++ b/components/selectors/parser.rs @@ -2,8 +2,9 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use attr::{AttrSelectorOperator, AttrSelectorWithNamespace, ParsedAttrSelectorOperation}; -use attr::{NamespaceConstraint, ParsedCaseSensitivity, SELECTOR_WHITESPACE}; +use attr::{AttrSelectorOperator, AttrSelectorWithOptionalNamespace}; +use attr::{NamespaceConstraint, ParsedAttrSelectorOperation}; +use attr::{ParsedCaseSensitivity, SELECTOR_WHITESPACE}; use bloom::BLOOM_HASH_MASK; use builder::{SelectorBuilder, SpecificityAndFlags}; use context::QuirksMode; @@ -19,6 +20,7 @@ use std::borrow::{Borrow, Cow}; use std::fmt::{self, Debug, Display, Write}; use std::iter::Rev; use std::slice; +use thin_slice::ThinBoxedSlice; pub use visitor::{SelectorVisitor, Visit}; /// A trait that represents a pseudo-element. @@ -45,6 +47,8 @@ pub trait NonTSPseudoClass: Sized + ToCss { fn is_active_or_hover(&self) -> bool; } +/// Returns a Cow::Borrowed if `s` is already ASCII lowercase, and a +/// Cow::Owned if `s` had to be converted into ASCII lowercase. fn to_ascii_lowercase(s: &str) -> Cow { if let Some(first_uppercase) = s.bytes().position(|byte| byte >= b'A' && byte <= b'Z') { let mut string = s.to_owned(); @@ -428,7 +432,6 @@ where }, AttributeInNoNamespace { ref local_name, - ref local_name_lower, never_matches, .. } if !never_matches => @@ -436,14 +439,22 @@ where if !visitor.visit_attribute_selector( &NamespaceConstraint::Specific(&namespace_empty_string::()), local_name, - local_name_lower, + local_name, ) { return false; } }, AttributeOther(ref attr_selector) if !attr_selector.never_matches => { + let empty_string; + let namespace = match attr_selector.namespace() { + Some(ns) => ns, + None => { + empty_string = ::parser::namespace_empty_string::(); + NamespaceConstraint::Specific(&empty_string) + } + }; if !visitor.visit_attribute_selector( - &attr_selector.namespace(), + &namespace, &attr_selector.local_name, &attr_selector.local_name_lower, ) { @@ -815,16 +826,16 @@ pub enum Component { local_name: Impl::LocalName, local_name_lower: Impl::LocalName, }, + // Used only when local_name is already lowercase. AttributeInNoNamespace { local_name: Impl::LocalName, - local_name_lower: Impl::LocalName, operator: AttrSelectorOperator, value: Impl::AttrValue, case_sensitivity: ParsedCaseSensitivity, never_matches: bool, }, // Use a Box in the less common cases with more data to keep size_of::() small. - AttributeOther(Box>), + AttributeOther(Box>), /// Pseudo-classes /// @@ -836,7 +847,7 @@ pub enum Component { /// need to think about how this should interact with /// visit_complex_selector, and what the consumers of those APIs should do /// about the presence of combinators in negation. - Negation(Box<[Component]>), + Negation(ThinBoxedSlice>), FirstChild, LastChild, OnlyChild, @@ -948,7 +959,7 @@ impl Debug for Component { self.to_css(f) } } -impl Debug for AttrSelectorWithNamespace { +impl Debug for AttrSelectorWithOptionalNamespace { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_css(f) } @@ -1238,18 +1249,19 @@ impl ToCss for Component { } } -impl ToCss for AttrSelectorWithNamespace { +impl ToCss for AttrSelectorWithOptionalNamespace { fn to_css(&self, dest: &mut W) -> fmt::Result where W: fmt::Write, { dest.write_char('[')?; match self.namespace { - NamespaceConstraint::Specific((ref prefix, _)) => { + Some(NamespaceConstraint::Specific((ref prefix, _))) => { display_to_css_identifier(prefix, dest)?; dest.write_char('|')? }, - NamespaceConstraint::Any => dest.write_str("*|")?, + Some(NamespaceConstraint::Any) => dest.write_str("*|")?, + None => {} } display_to_css_identifier(&self.local_name, dest)?; match self.operation { @@ -1628,8 +1640,8 @@ where let local_name = local_name.as_ref().into(); if let Some(namespace) = namespace { return Ok(Component::AttributeOther(Box::new( - AttrSelectorWithNamespace { - namespace: namespace, + AttrSelectorWithOptionalNamespace { + namespace: Some(namespace), local_name: local_name, local_name_lower: local_name_lower, operation: ParsedAttrSelectorOperation::Exists, @@ -1685,6 +1697,7 @@ where let value = value.as_ref().into(); let local_name_lower; + let local_name_is_ascii_lowercase; { let local_name_lower_cow = to_ascii_lowercase(&local_name); if let ParsedCaseSensitivity::CaseSensitive = case_sensitivity { @@ -1699,15 +1712,16 @@ where } } local_name_lower = local_name_lower_cow.as_ref().into(); + local_name_is_ascii_lowercase = matches!(local_name_lower_cow, Cow::Borrowed(..)); } let local_name = local_name.as_ref().into(); - if let Some(namespace) = namespace { + if namespace.is_some() || !local_name_is_ascii_lowercase { Ok(Component::AttributeOther(Box::new( - AttrSelectorWithNamespace { - namespace: namespace, - local_name: local_name, - local_name_lower: local_name_lower, - never_matches: never_matches, + AttrSelectorWithOptionalNamespace { + namespace, + local_name, + local_name_lower, + never_matches, operation: ParsedAttrSelectorOperation::WithValue { operator: operator, case_sensitivity: case_sensitivity, @@ -1718,7 +1732,6 @@ where } else { Ok(Component::AttributeInNoNamespace { local_name: local_name, - local_name_lower: local_name_lower, operator: operator, value: value, case_sensitivity: case_sensitivity, @@ -1785,7 +1798,7 @@ where } // Success. - Ok(Component::Negation(sequence.into_vec().into_boxed_slice())) + Ok(Component::Negation(sequence.into_vec().into_boxed_slice().into())) } /// simple_selector_sequence @@ -2625,7 +2638,7 @@ pub mod tests { vec![ Component::DefaultNamespace(MATHML.into()), Component::Negation( - vec![Component::Class(DummyAtom::from("cl"))].into_boxed_slice(), + vec![Component::Class(DummyAtom::from("cl"))].into_boxed_slice().into(), ), ], specificity(0, 1, 0), @@ -2642,7 +2655,7 @@ pub mod tests { vec![ Component::DefaultNamespace(MATHML.into()), Component::ExplicitUniversalType, - ].into_boxed_slice(), + ].into_boxed_slice().into(), ), ], specificity(0, 0, 0), @@ -2662,7 +2675,7 @@ pub mod tests { name: DummyAtom::from("e"), lower_name: DummyAtom::from("e"), }), - ].into_boxed_slice(), + ].into_boxed_slice().into(), ), ], specificity(0, 0, 1), @@ -2676,7 +2689,6 @@ pub mod tests { vec![ Component::AttributeInNoNamespace { local_name: DummyAtom::from("attr"), - local_name_lower: DummyAtom::from("attr"), operator: AttrSelectorOperator::DashMatch, value: DummyAtom::from("foo"), never_matches: false, @@ -2770,7 +2782,7 @@ pub mod tests { Selector::from_vec( vec![ Component::Negation( - vec![Component::ID(DummyAtom::from("provel"))].into_boxed_slice(), + vec![Component::ID(DummyAtom::from("provel"))].into_boxed_slice().into(), ), ], specificity(1, 0, 0), @@ -2789,7 +2801,7 @@ pub mod tests { name: DummyAtom::from("circle"), lower_name: DummyAtom::from("circle"), }), - ].into_boxed_slice(), + ].into_boxed_slice().into(), ), ], specificity(0, 0, 1), @@ -2803,7 +2815,7 @@ pub mod tests { Selector::from_vec( vec![ Component::Negation( - vec![Component::ExplicitUniversalType].into_boxed_slice(), + vec![Component::ExplicitUniversalType].into_boxed_slice().into(), ), ], specificity(0, 0, 0), @@ -2819,7 +2831,7 @@ pub mod tests { vec![ Component::ExplicitNoNamespace, Component::ExplicitUniversalType, - ].into_boxed_slice(), + ].into_boxed_slice().into(), ), ], specificity(0, 0, 0), @@ -2834,7 +2846,7 @@ pub mod tests { Selector::from_vec( vec![ Component::Negation( - vec![Component::ExplicitUniversalType].into_boxed_slice(), + vec![Component::ExplicitUniversalType].into_boxed_slice().into(), ), ], specificity(0, 0, 0), @@ -2850,7 +2862,7 @@ pub mod tests { vec![ Component::Namespace(DummyAtom("svg".into()), SVG.into()), Component::ExplicitUniversalType, - ].into_boxed_slice(), + ].into_boxed_slice().into(), ), ], specificity(0, 0, 0), diff --git a/components/selectors/tree.rs b/components/selectors/tree.rs index ef544cc7e4c..112b02dbbd9 100644 --- a/components/selectors/tree.rs +++ b/components/selectors/tree.rs @@ -45,12 +45,6 @@ pub trait Element: Sized + Clone + Debug { self.parent_element() } - /// Skips non-element nodes - fn first_child_element(&self) -> Option; - - /// Skips non-element nodes - fn last_child_element(&self) -> Option; - /// Skips non-element nodes fn prev_sibling_element(&self) -> Option; diff --git a/components/style/Cargo.toml b/components/style/Cargo.toml index 6cf0f2a6e22..7fb11e86f52 100644 --- a/components/style/Cargo.toml +++ b/components/style/Cargo.toml @@ -36,7 +36,7 @@ new_debug_unreachable = "1.0" encoding_rs = {version = "0.7", optional = true} euclid = "0.19" fallible = { path = "../fallible" } -fnv = "1.0" +fxhash = "0.2" hashglobe = { path = "../hashglobe" } html5ever = {version = "0.22", optional = true} itertools = "0.7.6" @@ -65,6 +65,7 @@ string_cache = { version = "0.7", optional = true } style_derive = {path = "../style_derive"} style_traits = {path = "../style_traits"} servo_url = {path = "../url", optional = true} +thin-slice = "0.1.0" time = "0.1" uluru = "0.2" unicode-bidi = "0.3" diff --git a/components/style/build_gecko.rs b/components/style/build_gecko.rs index 68d160ab5e0..8387f6513a6 100644 --- a/components/style/build_gecko.rs +++ b/components/style/build_gecko.rs @@ -120,7 +120,6 @@ mod bindings { let mut file = File::open(&path).unwrap(); let mut content = String::new(); file.read_to_string(&mut content).unwrap(); - println!("cargo:rerun-if-changed={}", path.to_str().unwrap()); added_paths.insert(path); // Find all includes and add them recursively for cap in INCLUDE_RE.captures_iter(&content) { @@ -286,6 +285,7 @@ mod bindings { ); }, }; + for fixup in fixups.iter() { result = Regex::new(&fixup.pat) .unwrap() @@ -602,6 +602,10 @@ mod bindings { generate_bindings(), generate_atoms(), } + + for path in ADDED_PATHS.lock().unwrap().iter() { + println!("cargo:rerun-if-changed={}", path.to_str().unwrap()); + } } } diff --git a/components/style/cbindgen.toml b/components/style/cbindgen.toml new file mode 100644 index 00000000000..86c3dddec4c --- /dev/null +++ b/components/style/cbindgen.toml @@ -0,0 +1,26 @@ +header = """/* 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 http://mozilla.org/MPL/2.0/. */""" +autogen_warning = """/* DO NOT MODIFY THIS MANUALLY! This file was generated using cbindgen. + * To generate this file: + * 1. Get the latest cbindgen using `cargo install --force cbindgen` + * a. Alternatively, you can clone `https://github.com/eqrion/cbindgen` and use a tagged release + * 2. Run `rustup run nightly cbindgen toolkit/library/rust/ --lockfile Cargo.lock --crate style -o layout/style/ServoStyleConsts.h` + */""" +include_version = true +braces = "SameLine" +line_length = 80 +tab_width = 2 +language = "C++" +namespaces = ["mozilla"] + +[struct] +derive_eq = true + +[enum] +derive_helper_methods = true + +[export] +prefix = "Style" +include = ["StyleDisplay", "StyleAppearance"] +item_types = ["enums"] diff --git a/components/style/context.rs b/components/style/context.rs index b35b3dbf489..16bba2a863f 100644 --- a/components/style/context.rs +++ b/components/style/context.rs @@ -14,8 +14,8 @@ use dom::{SendElement, TElement}; use dom::OpaqueNode; use euclid::Size2D; use euclid::TypedScale; -use fnv::FnvHashMap; use font_metrics::FontMetricsProvider; +use fxhash::FxHashMap; #[cfg(feature = "gecko")] use gecko_bindings::structs; use parallel::{STACK_SAFETY_MARGIN_KB, STYLE_THREAD_STACK_SIZE_KB}; @@ -173,11 +173,11 @@ pub struct SharedStyleContext<'a> { /// The animations that are currently running. #[cfg(feature = "servo")] - pub running_animations: Arc>>>, + pub running_animations: Arc>>>, /// The list of animations that have expired since the last style recalculation. #[cfg(feature = "servo")] - pub expired_animations: Arc>>>, + pub expired_animations: Arc>>>, /// Paint worklets #[cfg(feature = "servo")] @@ -570,7 +570,7 @@ type CacheItem = (SendElement, ElementSelectorFlags); /// flags until after the traversal. pub struct SelectorFlagsMap { /// The hashmap storing the flags to apply. - map: FnvHashMap, ElementSelectorFlags>, + map: FxHashMap, ElementSelectorFlags>, /// An LRU cache to avoid hashmap lookups, which can be slow if the map /// gets big. cache: LRUCache<[Entry>; 4 + 1]>, @@ -587,7 +587,7 @@ impl SelectorFlagsMap { /// Creates a new empty SelectorFlagsMap. pub fn new() -> Self { SelectorFlagsMap { - map: FnvHashMap::default(), + map: FxHashMap::default(), cache: LRUCache::default(), } } @@ -833,7 +833,7 @@ pub trait RegisteredSpeculativePainter: SpeculativePainter { /// The name it was registered with fn name(&self) -> Atom; /// The properties it was registered with - fn properties(&self) -> &FnvHashMap; + fn properties(&self) -> &FxHashMap; } /// A set of registered painters diff --git a/components/style/data.rs b/components/style/data.rs index 3df6e31c769..71a0ac23320 100644 --- a/components/style/data.rs +++ b/components/style/data.rs @@ -16,7 +16,6 @@ use selector_parser::{PseudoElement, RestyleDamage, EAGER_PSEUDO_COUNT}; use selectors::NthIndexCache; use servo_arc::Arc; use shared_lock::StylesheetGuards; -use smallvec::SmallVec; use std::fmt; use std::mem; use std::ops::{Deref, DerefMut}; @@ -273,16 +272,8 @@ impl ElementData { return InvalidationResult::empty(); } - let mut non_document_styles = SmallVec::<[_; 3]>::new(); - let matches_doc_author_rules = - element.each_applicable_non_document_style_rule_data(|data, quirks_mode, host| { - non_document_styles.push((data, quirks_mode, host.map(|h| h.opaque()))) - }); - let mut processor = StateAndAttrInvalidationProcessor::new( shared_context, - &non_document_styles, - matches_doc_author_rules, element, self, nth_index_cache, diff --git a/components/style/gecko/media_queries.rs b/components/style/gecko/media_queries.rs index 9c5ef4d71a1..ff4ca2b47cd 100644 --- a/components/style/gecko/media_queries.rs +++ b/components/style/gecko/media_queries.rs @@ -17,6 +17,7 @@ use gecko_bindings::structs::{nsCSSKTableEntry, nsCSSKeyword, nsCSSUnit, nsCSSVa use gecko_bindings::structs::{nsMediaFeature, nsMediaFeature_RangeType}; use gecko_bindings::structs::{nsMediaFeature_ValueType, nsPresContext}; use gecko_bindings::structs::RawGeckoPresContextOwned; +use gecko_bindings::structs::nsCSSKeywordAndBoolTableEntry; use media_queries::MediaType; use parser::{Parse, ParserContext}; use properties::ComputedValues; @@ -374,6 +375,9 @@ pub enum MediaExpressionValue { /// An enumerated value, defined by the variant keyword table in the /// feature's `mData` member. Enumerated(i16), + /// Similar to the above Enumerated but the variant keyword table has an + /// additional field what the keyword value means in the Boolean Context. + BoolEnumerated(i16), /// An identifier. Ident(Atom), } @@ -420,6 +424,10 @@ impl MediaExpressionValue { let value = css_value.integer_unchecked() as i16; Some(MediaExpressionValue::Enumerated(value)) }, + nsMediaFeature_ValueType::eBoolEnumerated => { + let value = css_value.integer_unchecked() as i16; + Some(MediaExpressionValue::BoolEnumerated(value)) + }, nsMediaFeature_ValueType::eIdent => { debug_assert_eq!(css_value.mUnit, nsCSSUnit::eCSSUnit_AtomIdent); Some(MediaExpressionValue::Ident(unsafe { @@ -457,25 +465,43 @@ impl MediaExpressionValue { MediaExpressionValue::Resolution(ref r) => r.to_css(dest), MediaExpressionValue::Ident(ref ident) => serialize_atom_identifier(ident, dest), MediaExpressionValue::Enumerated(value) => unsafe { - use std::{slice, str}; - use std::os::raw::c_char; - - // NB: All the keywords on nsMediaFeatures are static, - // well-formed utf-8. - let mut length = 0; - - let (keyword, _value) = find_in_table( + let keyword = find_in_table( *for_expr.feature.mData.mKeywordTable.as_ref(), |_kw, val| val == value, + |e| e.keyword(), ).expect("Value not found in the keyword table?"); - let buffer: *const c_char = bindings::Gecko_CSSKeywordString(keyword, &mut length); - let buffer = slice::from_raw_parts(buffer as *const u8, length as usize); - - let string = str::from_utf8_unchecked(buffer); - - dest.write_str(string) + MediaExpressionValue::keyword_to_css(keyword, dest) }, + MediaExpressionValue::BoolEnumerated(value) => unsafe { + let keyword = find_in_table( + *for_expr.feature.mData.mKeywordAndBoolTable.as_ref(), + |_kw, val| val == value, + |e| e.keyword(), + ).expect("Value not found in the keyword table?"); + + MediaExpressionValue::keyword_to_css(keyword, dest) + } + } + } + + fn keyword_to_css(keyword: nsCSSKeyword, dest: &mut CssWriter) -> fmt::Result + where + W: fmt::Write, + { + use std::{slice, str}; + use std::os::raw::c_char; + + // NB: All the keywords on nsMediaFeatures are static, + // well-formed utf-8. + let mut length = 0; + unsafe { + let buffer: *const c_char = bindings::Gecko_CSSKeywordString(keyword, &mut length); + let buffer = slice::from_raw_parts(buffer as *const u8, length as usize); + + let string = str::from_utf8_unchecked(buffer); + + dest.write_str(string) } } } @@ -496,23 +522,51 @@ where None } -unsafe fn find_in_table( - mut current_entry: *const nsCSSKTableEntry, - mut f: F, -) -> Option<(nsCSSKeyword, i16)> +trait TableEntry { + fn value(&self) -> i16; + fn keyword(&self) -> nsCSSKeyword; +} + +impl TableEntry for nsCSSKTableEntry { + fn value(&self) -> i16 { + self.mValue + } + fn keyword(&self) -> nsCSSKeyword { + self.mKeyword + } +} + +impl TableEntry for nsCSSKeywordAndBoolTableEntry { + fn value(&self) -> i16 { + self._base.mValue + } + fn keyword(&self) -> nsCSSKeyword { + self._base.mKeyword + } +} + +unsafe fn find_in_table( + current_entry: *const T, + find: FindFunc, + result_func: ResultFunc, +) -> Option where - F: FnMut(nsCSSKeyword, i16) -> bool, + T: TableEntry, + FindFunc: Fn(nsCSSKeyword, i16) -> bool, + ResultFunc: Fn(&T) -> R, { + let mut current_entry = current_entry; + loop { - let value = (*current_entry).mValue; - let keyword = (*current_entry).mKeyword; + let value = (*current_entry).value(); + let keyword = (*current_entry).keyword(); if value == -1 { return None; // End of the table. } - if f(keyword, value) { - return Some((keyword, value)); + if find(keyword, value) { + return Some(result_func(&*current_entry)); } current_entry = current_entry.offset(1); @@ -556,24 +610,21 @@ fn parse_feature_value<'i, 't>( MediaExpressionValue::Resolution(Resolution::parse(context, input)?) }, nsMediaFeature_ValueType::eEnumerated => { - let location = input.current_source_location(); - let keyword = input.expect_ident()?; - let keyword = unsafe { - bindings::Gecko_LookupCSSKeyword(keyword.as_bytes().as_ptr(), keyword.len() as u32) - }; - let first_table_entry: *const nsCSSKTableEntry = unsafe { *feature.mData.mKeywordTable.as_ref() }; - let value = match unsafe { find_in_table(first_table_entry, |kw, _| kw == keyword) } { - Some((_kw, value)) => value, - None => { - return Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)) - }, - }; + let value = parse_keyword(input, first_table_entry)?; MediaExpressionValue::Enumerated(value) }, + nsMediaFeature_ValueType::eBoolEnumerated => { + let first_table_entry: *const nsCSSKeywordAndBoolTableEntry = + unsafe { *feature.mData.mKeywordAndBoolTable.as_ref() }; + + let value = parse_keyword(input, first_table_entry)?; + + MediaExpressionValue::BoolEnumerated(value) + }, nsMediaFeature_ValueType::eIdent => { MediaExpressionValue::Ident(Atom::from(input.expect_ident()?.as_ref())) }, @@ -582,6 +633,30 @@ fn parse_feature_value<'i, 't>( Ok(value) } +/// Parse a keyword and returns the corresponding i16 value. +fn parse_keyword<'i, 't, T>( + input: &mut Parser<'i, 't>, + first_table_entry: *const T, +) -> Result> +where + T: TableEntry, +{ + let location = input.current_source_location(); + let keyword = input.expect_ident()?; + let keyword = unsafe { + bindings::Gecko_LookupCSSKeyword(keyword.as_bytes().as_ptr(), keyword.len() as u32) + }; + + let value = unsafe { + find_in_table(first_table_entry, |kw, _| kw == keyword, |e| e.value()) + }; + + match value { + Some(value) => Ok(value), + None => Err(location.new_custom_error(StyleParseErrorKind::UnspecifiedError)), + } +} + /// Consumes an operation or a colon, or returns an error. fn consume_operation_or_colon( input: &mut Parser, @@ -834,6 +909,16 @@ impl MediaFeatureExpression { quirks_mode, |context| l.to_computed_value(&context).px() != 0., ), + BoolEnumerated(value) => { + let value = unsafe { + find_in_table( + *self.feature.mData.mKeywordAndBoolTable.as_ref(), + |_kw, val| val == value, + |e| e.mValueInBooleanContext, + ) + }; + value.expect("Value not found in the keyword table?") + }, _ => true, }; }, @@ -880,6 +965,13 @@ impl MediaFeatureExpression { ); return one == other; }, + (&BoolEnumerated(one), &BoolEnumerated(other)) => { + debug_assert_ne!( + self.feature.mRangeType, + nsMediaFeature_RangeType::eMinMaxAllowed + ); + return one == other; + }, _ => unreachable!(), }; diff --git a/components/style/gecko/pseudo_element.rs b/components/style/gecko/pseudo_element.rs index df1b873a1b7..a08da8d40f1 100644 --- a/components/style/gecko/pseudo_element.rs +++ b/components/style/gecko/pseudo_element.rs @@ -15,6 +15,7 @@ use properties::longhands::display::computed_value::T as Display; use selector_parser::{NonTSPseudoClass, PseudoElementCascadeType, SelectorImpl}; use std::fmt; use string_cache::Atom; +use thin_slice::ThinBoxedSlice; use values::serialize_atom_identifier; include!(concat!( @@ -161,15 +162,6 @@ impl PseudoElement { self.is_anon_box() && !self.is_tree_pseudo_element() } - /// Covert non-canonical pseudo-element to canonical one, and keep a - /// canonical one as it is. - pub fn canonical(&self) -> PseudoElement { - match *self { - PseudoElement::MozPlaceholder => PseudoElement::Placeholder, - _ => self.clone(), - } - } - /// Property flag that properties must have to apply to this pseudo-element. #[inline] pub fn property_restriction(&self) -> Option { diff --git a/components/style/gecko/pseudo_element_definition.mako.rs b/components/style/gecko/pseudo_element_definition.mako.rs index 216650c9295..b507fafd865 100644 --- a/components/style/gecko/pseudo_element_definition.mako.rs +++ b/components/style/gecko/pseudo_element_definition.mako.rs @@ -8,7 +8,7 @@ pub enum PseudoElement { % for pseudo in PSEUDOS: /// ${pseudo.value} % if pseudo.is_tree_pseudo_element(): - ${pseudo.capitalized()}(Box<[Atom]>), + ${pseudo.capitalized()}(ThinBoxedSlice), % else: ${pseudo.capitalized()}, % endif @@ -112,7 +112,11 @@ impl PseudoElement { % for pseudo in PSEUDOS: ${pseudo_element_variant(pseudo)} => % if pseudo.is_tree_pseudo_element(): - 0, + if unsafe { structs::StaticPrefs_sVarCache_layout_css_xul_tree_pseudos_content_enabled } { + 0 + } else { + structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS_AND_CHROME + }, % elif pseudo.is_anon_box(): structs::CSS_PSEUDO_ELEMENT_ENABLED_IN_UA_SHEETS, % else: @@ -209,7 +213,7 @@ impl PseudoElement { % for pseudo in PSEUDOS: % if pseudo.is_tree_pseudo_element(): if atom == &atom!("${pseudo.value}") { - return Some(PseudoElement::${pseudo.capitalized()}(args)); + return Some(PseudoElement::${pseudo.capitalized()}(args.into())); } % endif % endfor @@ -234,6 +238,9 @@ impl PseudoElement { "-moz-selection" => { return Some(PseudoElement::Selection); } + "-moz-placeholder" => { + return Some(PseudoElement::Placeholder); + } _ => { // FIXME: -moz-tree check should probably be // ascii-case-insensitive. @@ -256,7 +263,7 @@ impl PseudoElement { let tree_part = &name[10..]; % for pseudo in TREE_PSEUDOS: if tree_part.eq_ignore_ascii_case("${pseudo.value[11:]}") { - return Some(${pseudo_element_variant(pseudo, "args")}); + return Some(${pseudo_element_variant(pseudo, "args.into()")}); } % endfor None diff --git a/components/style/gecko/selector_parser.rs b/components/style/gecko/selector_parser.rs index 45f42acc09d..fa514835e8c 100644 --- a/components/style/gecko/selector_parser.rs +++ b/components/style/gecko/selector_parser.rs @@ -13,11 +13,13 @@ use gecko_bindings::sugar::ownership::{HasBoxFFI, HasFFI, HasSimpleFFI}; use invalidation::element::document_state::InvalidationMatchingData; use selector_parser::{Direction, SelectorParser}; use selectors::SelectorList; -use selectors::parser::{self as selector_parser, Selector, SelectorParseErrorKind, Visit}; +use selectors::parser::{SelectorParseErrorKind, Visit}; +use selectors::parser::{self as selector_parser, Selector}; use selectors::visitor::SelectorVisitor; use std::fmt; use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace}; use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss as ToCss_}; +use thin_slice::ThinBoxedSlice; pub use gecko::pseudo_element::{PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT, PSEUDO_COUNT}; pub use gecko::snapshot::SnapshotMap; @@ -34,7 +36,7 @@ bitflags! { } /// The type used for storing pseudo-class string arguments. -pub type PseudoClassStringArg = Box<[u16]>; +pub type PseudoClassStringArg = ThinBoxedSlice; macro_rules! pseudo_class_name { (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*], @@ -56,7 +58,7 @@ macro_rules! pseudo_class_name { /// /// TODO(emilio): We disallow combinators and pseudos here, so we /// should use SimpleSelector instead - MozAny(Box<[Selector]>), + MozAny(ThinBoxedSlice>), /// The non-standard `:-moz-locale-dir` pseudo-class. MozLocaleDir(Box), } @@ -405,7 +407,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { // convert to null terminated utf16 string // since that's what Gecko deals with let utf16: Vec = name.encode_utf16().chain(Some(0u16)).collect(); - NonTSPseudoClass::$s_name(utf16.into_boxed_slice()) + NonTSPseudoClass::$s_name(utf16.into_boxed_slice().into()) }, )* "-moz-locale-dir" => { NonTSPseudoClass::MozLocaleDir( @@ -422,7 +424,7 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { selector_parser::parse_compound_selector_list( self, parser, - )? + )?.into() ) } _ => return Err(parser.new_custom_error( @@ -486,7 +488,9 @@ impl<'a, 'i> ::selectors::Parser<'i> for SelectorParser<'a> { } let args = args.into_boxed_slice(); if let Some(pseudo) = PseudoElement::tree_pseudo_element(&name, args) { - return Ok(pseudo); + if self.is_pseudo_element_enabled(&pseudo) { + return Ok(pseudo); + } } } Err( diff --git a/components/style/gecko/snapshot.rs b/components/style/gecko/snapshot.rs index bb0e4a99116..d70c6259bbf 100644 --- a/components/style/gecko/snapshot.rs +++ b/components/style/gecko/snapshot.rs @@ -156,6 +156,15 @@ impl GeckoElementSnapshot { } impl ElementSnapshot for GeckoElementSnapshot { + fn debug_list_attributes(&self) -> String { + use nsstring::nsCString; + let mut string = nsCString::new(); + unsafe { + bindings::Gecko_Snapshot_DebugListAttributes(self, &mut string); + } + String::from_utf8_lossy(&*string).into_owned() + } + fn state(&self) -> Option { if self.has_any(Flags::State) { Some(ElementState::from_bits_truncate(self.mState)) diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 6cdcf1acd1e..cf1a2b56ab6 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -30,7 +30,7 @@ use gecko::selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl}; use gecko::snapshot_helpers; use gecko_bindings::bindings; use gecko_bindings::bindings::{Gecko_ElementState, Gecko_GetDocumentLWTheme}; -use gecko_bindings::bindings::{Gecko_GetLastChild, Gecko_GetNextStyleChild}; +use gecko_bindings::bindings::{Gecko_GetLastChild, Gecko_GetPreviousSibling, Gecko_GetNextStyleChild}; use gecko_bindings::bindings::{Gecko_SetNodeFlags, Gecko_UnsetNodeFlags}; use gecko_bindings::bindings::Gecko_ClassOrClassList; use gecko_bindings::bindings::Gecko_ElementHasAnimations; @@ -62,7 +62,7 @@ use gecko_bindings::structs::nsChangeHint; use gecko_bindings::structs::nsIDocument_DocumentTheme as DocumentTheme; use gecko_bindings::structs::nsRestyleHint; use gecko_bindings::sugar::ownership::{HasArcFFI, HasSimpleFFI}; -use hash::FnvHashMap; +use hash::FxHashMap; use logical_geometry::WritingMode; use media_queries::Device; use properties::{ComputedValues, LonghandId}; @@ -375,7 +375,12 @@ impl<'ln> TNode for GeckoNode<'ln> { #[inline] fn first_child(&self) -> Option { - unsafe { self.0.mFirstChild.as_ref().map(GeckoNode::from_content) } + unsafe { + self.0 + .mFirstChild.raw::() + .as_ref() + .map(GeckoNode::from_content) + } } #[inline] @@ -385,17 +390,17 @@ impl<'ln> TNode for GeckoNode<'ln> { #[inline] fn prev_sibling(&self) -> Option { - unsafe { - self.0 - .mPreviousSibling - .as_ref() - .map(GeckoNode::from_content) - } + unsafe { Gecko_GetPreviousSibling(self.0).map(GeckoNode) } } #[inline] fn next_sibling(&self) -> Option { - unsafe { self.0.mNextSibling.as_ref().map(GeckoNode::from_content) } + unsafe { + self.0 + .mNextSibling.raw::() + .as_ref() + .map(GeckoNode::from_content) + } } #[inline] @@ -562,28 +567,15 @@ pub struct GeckoElement<'le>(pub &'le RawGeckoElement); impl<'le> fmt::Debug for GeckoElement<'le> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use nsstring::nsCString; + write!(f, "<{}", self.local_name())?; - if let Some(id) = self.id() { - write!(f, " id={}", id)?; + + let mut attrs = nsCString::new(); + unsafe { + bindings::Gecko_Element_DebugListAttributes(self.0, &mut attrs); } - - let mut first = true; - let mut any = false; - self.each_class(|c| { - if first { - first = false; - any = true; - let _ = f.write_str(" class=\""); - } else { - let _ = f.write_str(" "); - } - let _ = write!(f, "{}", c); - }); - - if any { - f.write_str("\"")?; - } - + write!(f, "{}", attrs)?; write!(f, "> ({:#x})", self.as_node().opaque().0) } } @@ -867,12 +859,12 @@ impl<'le> GeckoElement<'le> { } } - fn css_transitions_info(&self) -> FnvHashMap> { + fn css_transitions_info(&self) -> FxHashMap> { use gecko_bindings::bindings::Gecko_ElementTransitions_EndValueAt; use gecko_bindings::bindings::Gecko_ElementTransitions_Length; let collection_length = unsafe { Gecko_ElementTransitions_Length(self.0) } as usize; - let mut map = FnvHashMap::with_capacity_and_hasher(collection_length, Default::default()); + let mut map = FxHashMap::with_capacity_and_hasher(collection_length, Default::default()); for i in 0..collection_length { let raw_end_value = unsafe { Gecko_ElementTransitions_EndValueAt(self.0, i) }; @@ -893,7 +885,7 @@ impl<'le> GeckoElement<'le> { combined_duration: f32, before_change_style: &ComputedValues, after_change_style: &ComputedValues, - existing_transitions: &FnvHashMap>, + existing_transitions: &FxHashMap>, ) -> bool { use values::animated::{Animate, Procedure}; debug_assert!(!longhand_id.is_logical()); @@ -1967,30 +1959,6 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { unsafe { Some(GeckoElement(&slot.as_ref()?._base._base._base._base)) } } - #[inline] - fn first_child_element(&self) -> Option { - let mut child = self.as_node().first_child(); - while let Some(child_node) = child { - if let Some(el) = child_node.as_element() { - return Some(el); - } - child = child_node.next_sibling(); - } - None - } - - #[inline] - fn last_child_element(&self) -> Option { - let mut child = self.as_node().last_child(); - while let Some(child_node) = child { - if let Some(el) = child_node.as_element() { - return Some(el); - } - child = child_node.prev_sibling(); - } - None - } - #[inline] fn prev_sibling_element(&self) -> Option { let mut sibling = self.as_node().prev_sibling(); @@ -2087,15 +2055,15 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { #[inline] fn is_root(&self) -> bool { - let parent_node = match self.as_node().parent_node() { - Some(parent_node) => parent_node, - None => return false, - }; - - if !parent_node.is_document() { + if self.as_node().get_bool_flag(nsINode_BooleanFlag::ParentIsContent) { return false; } + if !self.as_node().is_in_document() { + return false; + } + + debug_assert!(self.as_node().parent_node().map_or(false, |p| p.is_document())); unsafe { bindings::Gecko_IsRootElement(self.0) } } @@ -2280,7 +2248,7 @@ impl<'le> ::selectors::Element for GeckoElement<'le> { // match the proper pseudo-element, given how we rulehash the stuff // based on the pseudo. match self.implemented_pseudo_element() { - Some(ref pseudo) => *pseudo == pseudo_element.canonical(), + Some(ref pseudo) => *pseudo == *pseudo_element, None => false, } } diff --git a/components/style/hash.rs b/components/style/hash.rs index af1ce729a39..71c5b1c8a45 100644 --- a/components/style/hash.rs +++ b/components/style/hash.rs @@ -7,7 +7,7 @@ //! Can go away when the stdlib gets fallible collections //! https://github.com/rust-lang/rfcs/pull/2116 -use fnv; +use fxhash; #[cfg(feature = "gecko")] pub use hashglobe::hash_map::HashMap; @@ -25,7 +25,7 @@ pub mod map { pub use std::collections::hash_map::{Entry, Iter}; } -/// Hash map that uses the FNV hasher -pub type FnvHashMap = HashMap; -/// Hash set that uses the FNV hasher -pub type FnvHashSet = HashSet; +/// Hash map that uses the Fx hasher +pub type FxHashMap = HashMap; +/// Hash set that uses the Fx hasher +pub type FxHashSet = HashSet; diff --git a/components/style/invalidation/element/element_wrapper.rs b/components/style/invalidation/element/element_wrapper.rs index ecbc3b94b78..cb4f79e92e1 100644 --- a/components/style/invalidation/element/element_wrapper.rs +++ b/components/style/invalidation/element/element_wrapper.rs @@ -43,6 +43,11 @@ pub trait ElementSnapshot: Sized { /// If this snapshot contains attribute information. fn has_attrs(&self) -> bool; + /// Gets the attribute information of the snapshot as a string. + /// + /// Only for debugging purposes. + fn debug_list_attributes(&self) -> String { String::new() } + /// The ID attribute per this snapshot. Should only be called if /// `has_attrs()` returns true. fn id_attr(&self) -> Option<&WeakAtom>; @@ -278,16 +283,6 @@ where Some(Self::new(host, self.snapshot_map)) } - fn first_child_element(&self) -> Option { - let child = self.element.first_child_element()?; - Some(Self::new(child, self.snapshot_map)) - } - - fn last_child_element(&self) -> Option { - let child = self.element.last_child_element()?; - Some(Self::new(child, self.snapshot_map)) - } - fn prev_sibling_element(&self) -> Option { let sibling = self.element.prev_sibling_element()?; Some(Self::new(sibling, self.snapshot_map)) diff --git a/components/style/invalidation/element/state_and_attributes.rs b/components/style/invalidation/element/state_and_attributes.rs index 73d73282910..88b012a2524 100644 --- a/components/style/invalidation/element/state_and_attributes.rs +++ b/components/style/invalidation/element/state_and_attributes.rs @@ -6,7 +6,7 @@ //! changes. use {Atom, WeakAtom}; -use context::{QuirksMode, SharedStyleContext}; +use context::SharedStyleContext; use data::ElementData; use dom::TElement; use element_state::ElementState; @@ -18,13 +18,11 @@ use invalidation::element::restyle_hints::RestyleHint; use selector_map::SelectorMap; use selector_parser::Snapshot; use selectors::NthIndexCache; -use selectors::OpaqueElement; use selectors::attr::CaseSensitivity; use selectors::matching::{MatchingContext, MatchingMode, VisitedHandlingMode}; use selectors::matching::matches_selector; use smallvec::SmallVec; use stylesheets::origin::{Origin, OriginSet}; -use stylist::CascadeData; #[derive(Debug, PartialEq)] enum VisitedDependent { @@ -56,19 +54,15 @@ where /// changes. pub struct StateAndAttrInvalidationProcessor<'a, 'b: 'a, E: TElement> { shared_context: &'a SharedStyleContext<'b>, - shadow_rule_datas: &'a [(&'b CascadeData, QuirksMode, Option)], - matches_document_author_rules: bool, element: E, data: &'a mut ElementData, matching_context: MatchingContext<'a, E::Impl>, } -impl<'a, 'b: 'a, E: TElement> StateAndAttrInvalidationProcessor<'a, 'b, E> { +impl<'a, 'b: 'a, E: TElement + 'b> StateAndAttrInvalidationProcessor<'a, 'b, E> { /// Creates a new StateAndAttrInvalidationProcessor. pub fn new( shared_context: &'a SharedStyleContext<'b>, - shadow_rule_datas: &'a [(&'b CascadeData, QuirksMode, Option)], - matches_document_author_rules: bool, element: E, data: &'a mut ElementData, nth_index_cache: &'a mut NthIndexCache, @@ -83,8 +77,6 @@ impl<'a, 'b: 'a, E: TElement> StateAndAttrInvalidationProcessor<'a, 'b, E> { Self { shared_context, - shadow_rule_datas, - matches_document_author_rules, element, data, matching_context, @@ -157,6 +149,7 @@ where descendant_invalidations: &mut DescendantInvalidationLists<'a>, sibling_invalidations: &mut InvalidationVector<'a>, ) -> bool { + debug_assert_eq!(element, self.element); debug_assert!(element.has_snapshot(), "Why bothering?"); let wrapper = ElementWrapper::new(element, &*self.shared_context.snapshot_map); @@ -208,20 +201,32 @@ where } } - debug!("Collecting changes for: {:?}", element); - debug!(" > state: {:?}", state_changes); - debug!( - " > id changed: {:?} -> +{:?} -{:?}", - snapshot.id_changed(), - id_added, - id_removed - ); - debug!( - " > class changed: {:?} -> +{:?} -{:?}", - snapshot.class_changed(), - classes_added, - classes_removed - ); + if log_enabled!(::log::Level::Debug) { + debug!("Collecting changes for: {:?}", element); + if !state_changes.is_empty() { + debug!(" > state: {:?}", state_changes); + } + if snapshot.id_changed() { + debug!( + " > id changed: +{:?} -{:?}", + id_added, + id_removed + ); + } + if snapshot.class_changed() { + debug!( + " > class changed: +{:?} -{:?}", + classes_added, + classes_removed + ); + } + if snapshot.other_attr_changed() { + debug!( + " > attributes changed, old: {}", + snapshot.debug_list_attributes() + ) + } + } let lookup_element = if element.implemented_pseudo_element().is_some() { element.pseudo_element_originating_element().unwrap() @@ -229,6 +234,13 @@ where element }; + let mut shadow_rule_datas = SmallVec::<[_; 3]>::new(); + let matches_document_author_rules = + element.each_applicable_non_document_style_rule_data(|data, quirks_mode, host| { + shadow_rule_datas.push((data, quirks_mode, host.map(|h| h.opaque()))) + }); + + let invalidated_self = { let mut collector = Collector { wrapper, @@ -246,7 +258,7 @@ where invalidates_self: false, }; - let document_origins = if !self.matches_document_author_rules { + let document_origins = if !matches_document_author_rules { Origin::UserAgent.into() } else { OriginSet::all() @@ -259,7 +271,7 @@ where } } - for &(ref data, quirks_mode, ref host) in self.shadow_rule_datas { + for &(ref data, quirks_mode, ref host) in &shadow_rule_datas { // FIXME(emilio): Replace with assert / remove when we figure // out what to do with the quirks mode mismatches // (that is, when bug 1406875 is properly fixed). diff --git a/components/style/invalidation/media_queries.rs b/components/style/invalidation/media_queries.rs index 105c1bdb7cd..879540d683f 100644 --- a/components/style/invalidation/media_queries.rs +++ b/components/style/invalidation/media_queries.rs @@ -5,7 +5,7 @@ //! Code related to the invalidation of media-query-affected rules. use context::QuirksMode; -use fnv::FnvHashSet; +use fxhash::FxHashSet; use media_queries::Device; use shared_lock::SharedRwLockReadGuard; use stylesheets::{DocumentRule, ImportRule, MediaRule}; @@ -54,14 +54,14 @@ impl ToMediaListKey for MediaRule {} #[derive(Debug, MallocSizeOf, PartialEq)] pub struct EffectiveMediaQueryResults { /// The set of media lists that matched last time. - set: FnvHashSet, + set: FxHashSet, } impl EffectiveMediaQueryResults { /// Trivially constructs an empty `EffectiveMediaQueryResults`. pub fn new() -> Self { Self { - set: FnvHashSet::default(), + set: FxHashSet::default(), } } diff --git a/components/style/invalidation/stylesheets.rs b/components/style/invalidation/stylesheets.rs index 66a36de1a24..1889d325630 100644 --- a/components/style/invalidation/stylesheets.rs +++ b/components/style/invalidation/stylesheets.rs @@ -11,7 +11,7 @@ use Atom; use CaseSensitivityExt; use LocalName as SelectorLocalName; use dom::{TDocument, TElement, TNode}; -use fnv::FnvHashSet; +use fxhash::FxHashSet; use invalidation::element::element_wrapper::{ElementSnapshot, ElementWrapper}; use invalidation::element::restyle_hints::RestyleHint; use media_queries::Device; @@ -106,9 +106,9 @@ impl Invalidation { #[derive(MallocSizeOf)] pub struct StylesheetInvalidationSet { /// The subtrees we know we have to restyle so far. - invalid_scopes: FnvHashSet, + invalid_scopes: FxHashSet, /// The elements we know we have to restyle so far. - invalid_elements: FnvHashSet, + invalid_elements: FxHashSet, /// Whether the whole document should be restyled. fully_invalid: bool, } @@ -117,8 +117,8 @@ impl StylesheetInvalidationSet { /// Create an empty `StylesheetInvalidationSet`. pub fn new() -> Self { Self { - invalid_scopes: FnvHashSet::default(), - invalid_elements: FnvHashSet::default(), + invalid_scopes: FxHashSet::default(), + invalid_elements: FxHashSet::default(), fully_invalid: false, } } diff --git a/components/style/lib.rs b/components/style/lib.rs index 20a54c62680..a43f3f87c45 100644 --- a/components/style/lib.rs +++ b/components/style/lib.rs @@ -42,7 +42,7 @@ extern crate cssparser; extern crate debug_unreachable; extern crate euclid; extern crate fallible; -extern crate fnv; +extern crate fxhash; #[cfg(feature = "gecko")] #[macro_use] pub mod gecko_string_cache; @@ -93,6 +93,8 @@ extern crate string_cache; #[macro_use] extern crate style_derive; extern crate style_traits; +#[cfg(feature = "gecko")] +extern crate thin_slice; extern crate time; extern crate uluru; extern crate unicode_bidi; diff --git a/components/style/properties/gecko.mako.rs b/components/style/properties/gecko.mako.rs index c91ef4d4476..312f43878ae 100644 --- a/components/style/properties/gecko.mako.rs +++ b/components/style/properties/gecko.mako.rs @@ -368,6 +368,34 @@ def set_gecko_property(ffi_name, expr): return "self.gecko.%s = %s;" % (ffi_name, expr) %> +<%def name="impl_cbindgen_keyword(ident, gecko_ffi_name)"> + #[allow(non_snake_case)] + #[inline] + pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { + // unsafe: cbindgen ensures the representations match. + ${set_gecko_property(gecko_ffi_name, "unsafe { transmute(v) }")} + } + + #[allow(non_snake_case)] + #[inline] + pub fn clone_${ident}(&self) -> longhands::${ident}::computed_value::T { + // unsafe: cbindgen ensures the representations match. + unsafe { transmute(${get_gecko_property(gecko_ffi_name)}) } + } + + #[allow(non_snake_case)] + #[inline] + pub fn copy_${ident}_from(&mut self, other: &Self) { + self.gecko.${gecko_ffi_name} = other.gecko.${gecko_ffi_name}; + } + + #[allow(non_snake_case)] + #[inline] + pub fn reset_${ident}(&mut self, other: &Self) { + self.copy_${ident}_from(other) + } + + <%def name="impl_keyword_setter(ident, gecko_ffi_name, keyword, cast_type='u8', on_set=None)"> #[allow(non_snake_case)] pub fn set_${ident}(&mut self, v: longhands::${ident}::computed_value::T) { @@ -3015,7 +3043,7 @@ fn static_assert() { } -<% skip_box_longhands= """display overflow-y vertical-align +<% skip_box_longhands= """display -moz-appearance overflow-y vertical-align animation-name animation-delay animation-duration animation-direction animation-fill-mode animation-play-state animation-iteration-count animation-timing-function @@ -3031,57 +3059,41 @@ fn static_assert() { shape-outside contain touch-action translate scale""" %> <%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}"> - - // We manually-implement the |display| property until we get general - // infrastructure for preffing certain values. - <% display_keyword = Keyword("display", "inline block inline-block table inline-table table-row-group " + - "table-header-group table-footer-group table-row table-column-group " + - "table-column table-cell table-caption list-item flex none " + - "inline-flex grid inline-grid ruby ruby-base ruby-base-container " + - "ruby-text ruby-text-container contents flow-root -webkit-box " + - "-webkit-inline-box -moz-box -moz-inline-box -moz-grid -moz-inline-grid " + - "-moz-grid-group -moz-grid-line -moz-stack -moz-inline-stack -moz-deck " + - "-moz-popup -moz-groupbox", - gecko_enum_prefix="StyleDisplay", - gecko_strip_moz_prefix=False) %> - - fn match_display_keyword( - v: longhands::display::computed_value::T - ) -> structs::root::mozilla::StyleDisplay { - use properties::longhands::display::computed_value::T as Keyword; - // FIXME(bholley): Align binary representations and ditch |match| for cast + static_asserts - match v { - % for value in display_keyword.values_for('gecko'): - Keyword::${to_camel_case(value)} => - structs::${display_keyword.gecko_constant(value)}, - % endfor - } - } - + #[inline] pub fn set_display(&mut self, v: longhands::display::computed_value::T) { - let result = Self::match_display_keyword(v); - self.gecko.mDisplay = result; - self.gecko.mOriginalDisplay = result; + // unsafe: cbindgen ensures the representation is the same. + self.gecko.mDisplay = unsafe { transmute(v) }; + self.gecko.mOriginalDisplay = unsafe { transmute(v) }; } + #[inline] pub fn copy_display_from(&mut self, other: &Self) { self.gecko.mDisplay = other.gecko.mDisplay; self.gecko.mOriginalDisplay = other.gecko.mDisplay; } + #[inline] pub fn reset_display(&mut self, other: &Self) { self.copy_display_from(other) } + #[inline] pub fn set_adjusted_display( &mut self, v: longhands::display::computed_value::T, _is_item_or_root: bool ) { - self.gecko.mDisplay = Self::match_display_keyword(v); + // unsafe: cbindgen ensures the representation is the same. + self.gecko.mDisplay = unsafe { transmute(v) }; } - <%call expr="impl_keyword_clone('display', 'mDisplay', display_keyword)"> + #[inline] + pub fn clone_display(&self) -> longhands::display::computed_value::T { + // unsafe: cbindgen ensures the representation is the same. + unsafe { transmute(self.gecko.mDisplay) } + } + + ${impl_cbindgen_keyword('_moz_appearance', 'mAppearance')} <% float_keyword = Keyword("float", "Left Right None", gecko_enum_prefix="StyleFloat") %> ${impl_keyword('float', 'mFloat', float_keyword)} diff --git a/components/style/properties/helpers/animated_properties.mako.rs b/components/style/properties/helpers/animated_properties.mako.rs index d00f1947d86..f6277a21271 100644 --- a/components/style/properties/helpers/animated_properties.mako.rs +++ b/components/style/properties/helpers/animated_properties.mako.rs @@ -25,7 +25,7 @@ use servo_arc::Arc; use smallvec::SmallVec; use std::{cmp, ptr}; use std::mem::{self, ManuallyDrop}; -use hash::FnvHashMap; +use hash::FxHashMap; use super::ComputedValues; use values::CSSFloat; use values::animated::{Animate, Procedure, ToAnimatedValue, ToAnimatedZero}; @@ -232,7 +232,7 @@ impl AnimatedProperty { /// A collection of AnimationValue that were composed on an element. /// This HashMap stores the values that are the last AnimationValue to be /// composed for each TransitionProperty. -pub type AnimationValueMap = FnvHashMap; +pub type AnimationValueMap = FxHashMap; #[cfg(feature = "gecko")] unsafe impl HasFFI for AnimationValueMap { diff --git a/components/style/properties/longhands/box.mako.rs b/components/style/properties/longhands/box.mako.rs index 627b9a8ce3a..765731c13f2 100644 --- a/components/style/properties/longhands/box.mako.rs +++ b/components/style/properties/longhands/box.mako.rs @@ -508,41 +508,15 @@ ${helpers.predefined_type("contain", spec="https://drafts.csswg.org/css-contain/#contain-property")} // Non-standard -${helpers.single_keyword("-moz-appearance", - """none button button-arrow-down button-arrow-next button-arrow-previous button-arrow-up - button-bevel button-focus caret checkbox checkbox-container checkbox-label checkmenuitem - dialog dualbutton groupbox inner-spin-button listbox listitem menuarrow menubar menucheckbox - menuimage menuitem menuitemtext menulist menulist-button menulist-text menulist-textfield - menupopup menuradio menuseparator meterbar meterchunk number-input progressbar - progressbar-vertical progresschunk progresschunk-vertical radio radio-container radio-label - radiomenuitem range range-thumb resizer resizerpanel scale-horizontal scalethumbend - scalethumb-horizontal scalethumbstart scalethumbtick scalethumb-vertical scale-vertical - scrollbar scrollbar-horizontal scrollbar-small scrollbar-vertical scrollbarbutton-down - scrollbarbutton-left scrollbarbutton-right scrollbarbutton-up scrollbarthumb-horizontal - scrollbarthumb-vertical scrollbartrack-horizontal scrollbartrack-vertical scrollcorner - searchfield separator - spinner spinner-downbutton spinner-textfield spinner-upbutton splitter statusbar - statusbarpanel tab tabpanel tabpanels tab-scroll-arrow-back tab-scroll-arrow-forward - textfield textfield-multiline toolbar toolbarbutton toolbarbutton-dropdown toolbargripper - toolbox tooltip treeheader treeheadercell treeheadersortarrow treeitem treeline treetwisty - treetwistyopen treeview window - -moz-gtk-info-bar -moz-mac-active-source-list-selection -moz-mac-disclosure-button-closed - -moz-mac-disclosure-button-open -moz-mac-fullscreen-button -moz-mac-help-button - -moz-mac-source-list -moz-mac-source-list-selection -moz-mac-vibrancy-dark - -moz-mac-vibrancy-light -moz-mac-vibrant-titlebar-light -moz-mac-vibrant-titlebar-dark - -moz-win-borderless-glass -moz-win-browsertabbar-toolbox - -moz-win-communications-toolbox -moz-win-exclude-glass -moz-win-glass -moz-win-media-toolbox - -moz-window-button-box -moz-window-button-box-maximized -moz-window-button-close - -moz-window-button-maximize -moz-window-button-minimize -moz-window-button-restore - -moz-window-frame-bottom -moz-window-frame-left -moz-window-frame-right -moz-window-titlebar - -moz-window-titlebar-maximized - """, - gecko_ffi_name="mAppearance", - gecko_constant_prefix="ThemeWidgetType_NS_THEME", - products="gecko", - alias="-webkit-appearance:layout.css.webkit-appearance.enabled", - spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance)", - animation_value_type="discrete")} +${helpers.predefined_type( + "-moz-appearance", + "Appearance", + "computed::Appearance::None", + products="gecko", + alias="-webkit-appearance:layout.css.webkit-appearance.enabled", + spec="Nonstandard (https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance)", + animation_value_type="discrete", +)} ${helpers.predefined_type("-moz-binding", "url::UrlOrNone", "computed::url::UrlOrNone::none()", products="gecko", diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index ad184cd0fcf..fed6d52b7f7 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -410,25 +410,44 @@ pub mod animated_properties { #[derive(Clone, Copy, Debug)] pub struct NonCustomPropertyId(usize); +% if product == "gecko": +#[allow(dead_code)] +unsafe fn static_assert_nscsspropertyid() { + % for i, property in enumerate(data.longhands + data.shorthands + data.all_aliases()): + ::std::mem::transmute::<[u8; ${i}], [u8; ${property.nscsspropertyid()} as usize]>([0; ${i}]); // ${property.name} + % endfor +} +% endif + impl NonCustomPropertyId { #[cfg(feature = "gecko")] #[inline] fn to_nscsspropertyid(self) -> nsCSSPropertyID { - static MAP: [nsCSSPropertyID; ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())}] = [ - % for property in data.longhands + data.shorthands + data.all_aliases(): - ${property.nscsspropertyid()}, - % endfor - ]; + // unsafe: guaranteed by static_assert_nscsspropertyid above. + unsafe { ::std::mem::transmute(self.0 as i32) } + } - MAP[self.0] + /// Convert an `nsCSSPropertyID` into a `NonCustomPropertyId`. + #[cfg(feature = "gecko")] + #[inline] + pub fn from_nscsspropertyid(prop: nsCSSPropertyID) -> Result { + let prop = prop as i32; + if prop < 0 { + return Err(()); + } + if prop >= ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())} { + return Err(()); + } + // unsafe: guaranteed by static_assert_nscsspropertyid above. + Ok(unsafe { ::std::mem::transmute(prop as usize) }) } /// Get the property name. #[inline] - fn name(self) -> &'static str { + pub fn name(self) -> &'static str { static MAP: [&'static str; ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())}] = [ % for property in data.longhands + data.shorthands + data.all_aliases(): - "${property.name}", + "${property.name}", % endfor ]; MAP[self.0] @@ -464,7 +483,7 @@ impl NonCustomPropertyId { PREFS.get(pref).as_boolean().unwrap_or(false) % else: - unsafe { structs::nsCSSProps_gPropertyEnabled[self.to_nscsspropertyid() as usize] } + unsafe { structs::nsCSSProps_gPropertyEnabled[self.0] } % endif }; @@ -585,6 +604,31 @@ impl NonCustomPropertyId { ]; COLLECT_FUNCTIONS[self.0](f); } + + /// Turns this `NonCustomPropertyId` into a `PropertyId`. + #[inline] + pub fn to_property_id(self) -> PropertyId { + use std::mem::transmute; + if self.0 < ${len(data.longhands)} { + return unsafe { + PropertyId::Longhand(transmute(self.0 as u16)) + } + } + if self.0 < ${len(data.longhands) + len(data.shorthands)} { + return unsafe { + PropertyId::Shorthand(transmute((self.0 - ${len(data.longhands)}) as u16)) + } + } + assert!(self.0 < ${len(data.longhands) + len(data.shorthands) + len(data.all_aliases())}); + let alias_id: AliasId = unsafe { + transmute((self.0 - ${len(data.longhands) + len(data.shorthands)}) as u16) + }; + + match alias_id.aliased_property() { + AliasedPropertyId::Longhand(longhand) => PropertyId::LonghandAlias(longhand, alias_id), + AliasedPropertyId::Shorthand(shorthand) => PropertyId::ShorthandAlias(shorthand, alias_id), + } + } } impl From for NonCustomPropertyId { @@ -1237,10 +1281,11 @@ where /// An identifier for a given shorthand property. #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, PartialEq)] +#[repr(u16)] pub enum ShorthandId { - % for property in data.shorthands: + % for i, property in enumerate(data.shorthands): /// ${property.name} - ${property.camel_case}, + ${property.camel_case} = ${i}, % endfor } @@ -1767,42 +1812,11 @@ impl PropertyId { } /// Returns a property id from Gecko's nsCSSPropertyID. - /// - /// TODO(emilio): We should be able to make this a single integer cast to - /// `NonCustomPropertyId`. #[cfg(feature = "gecko")] #[allow(non_upper_case_globals)] + #[inline] pub fn from_nscsspropertyid(id: nsCSSPropertyID) -> Result { - use gecko_bindings::structs::*; - match id { - % for property in data.longhands: - ${property.nscsspropertyid()} => { - Ok(PropertyId::Longhand(LonghandId::${property.camel_case})) - } - % for alias in property.alias: - ${alias.nscsspropertyid()} => { - Ok(PropertyId::LonghandAlias( - LonghandId::${property.camel_case}, - AliasId::${alias.camel_case} - )) - } - % endfor - % endfor - % for property in data.shorthands: - ${property.nscsspropertyid()} => { - Ok(PropertyId::Shorthand(ShorthandId::${property.camel_case})) - } - % for alias in property.alias: - ${alias.nscsspropertyid()} => { - Ok(PropertyId::ShorthandAlias( - ShorthandId::${property.camel_case}, - AliasId::${alias.camel_case} - )) - } - % endfor - % endfor - _ => Err(()) - } + Ok(NonCustomPropertyId::from_nscsspropertyid(id)?.to_property_id()) } /// Returns true if the property is a shorthand or shorthand alias. @@ -1858,6 +1872,18 @@ impl PropertyId { id.enabled_for_all_content() } + /// Converts this PropertyId in nsCSSPropertyID, resolving aliases to the + /// resolved property, and returning eCSSPropertyExtra_variable for custom + /// properties. + #[cfg(feature = "gecko")] + #[inline] + pub fn to_nscsspropertyid_resolving_aliases(&self) -> nsCSSPropertyID { + match self.non_custom_non_alias_id() { + Some(id) => id.to_nscsspropertyid(), + None => nsCSSPropertyID::eCSSPropertyExtra_variable, + } + } + fn allowed_in(&self, context: &ParserContext) -> bool { let id = match self.non_custom_id() { // Custom properties are allowed everywhere @@ -2387,7 +2413,7 @@ pub use gecko_properties::style_structs; /// The module where all the style structs are defined. #[cfg(feature = "servo")] pub mod style_structs { - use fnv::FnvHasher; + use fxhash::FxHasher; use super::longhands; use std::hash::{Hash, Hasher}; use logical_geometry::WritingMode; @@ -2534,7 +2560,7 @@ pub mod style_structs { pub fn compute_font_hash(&mut self) { // Corresponds to the fields in // `gfx::font_template::FontTemplateDescriptor`. - let mut hasher: FnvHasher = Default::default(); + let mut hasher: FxHasher = Default::default(); self.font_weight.hash(&mut hasher); self.font_stretch.hash(&mut hasher); self.font_style.hash(&mut hasher); @@ -4230,6 +4256,7 @@ pub fn adjust_border_width(style: &mut StyleBuilder) { /// An identifier for a given alias property. #[derive(Clone, Copy, Eq, PartialEq)] #[cfg_attr(feature = "servo", derive(MallocSizeOf))] +#[repr(u16)] pub enum AliasId { % for i, property in enumerate(data.all_aliases()): /// ${property.name} @@ -4237,17 +4264,38 @@ pub enum AliasId { % endfor } +#[derive(Clone, Copy, Eq, PartialEq)] +enum AliasedPropertyId { + #[allow(dead_code)] // Servo doesn't have aliased shorthands. + Shorthand(ShorthandId), + Longhand(LonghandId), +} + impl fmt::Debug for AliasId { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - let name = match *self { - % for property in data.all_aliases(): - AliasId::${property.camel_case} => "${property.camel_case}", - % endfor - }; + let name = NonCustomPropertyId::from(*self).name(); formatter.write_str(name) } } +impl AliasId { + /// Returns the property we're aliasing, as a longhand or a shorthand. + #[inline] + fn aliased_property(self) -> AliasedPropertyId { + static MAP: [AliasedPropertyId; ${len(data.all_aliases())}] = [ + % for alias in data.all_aliases(): + % if alias.original.type() == "longhand": + AliasedPropertyId::Longhand(LonghandId::${alias.original.camel_case}), + % else: + <% assert alias.original.type() == "shorthand" %> + AliasedPropertyId::Shorthand(ShorthandId::${alias.original.camel_case}), + % endif + % endfor + ]; + MAP[self as usize] + } +} + // NOTE(emilio): Callers are responsible to deal with prefs. #[macro_export] macro_rules! css_properties_accessors { diff --git a/components/style/rule_cache.rs b/components/style/rule_cache.rs index 1bd65775e3a..5e386d711cb 100644 --- a/components/style/rule_cache.rs +++ b/components/style/rule_cache.rs @@ -5,7 +5,7 @@ //! A cache from rule node to computed values, in order to cache reset //! properties. -use fnv::FnvHashMap; +use fxhash::FxHashMap; use logical_geometry::WritingMode; use properties::{ComputedValues, StyleBuilder}; use rule_tree::StrongRuleNode; @@ -71,14 +71,14 @@ impl RuleCacheConditions { /// A TLS cache from rules matched to computed values. pub struct RuleCache { // FIXME(emilio): Consider using LRUCache or something like that? - map: FnvHashMap); 1]>>, + map: FxHashMap); 1]>>, } impl RuleCache { /// Creates an empty `RuleCache`. pub fn new() -> Self { Self { - map: FnvHashMap::default(), + map: FxHashMap::default(), } } diff --git a/components/style/rule_tree/mod.rs b/components/style/rule_tree/mod.rs index 2ce403a666d..5414b254aae 100644 --- a/components/style/rule_tree/mod.rs +++ b/components/style/rule_tree/mod.rs @@ -1146,13 +1146,13 @@ impl StrongRuleNode { unsafe fn assert_free_list_has_no_duplicates_or_null(&self) { assert!(cfg!(debug_assertions), "This is an expensive check!"); - use hash::FnvHashSet; + use hash::FxHashSet; let me = &*self.ptr(); assert!(me.is_root()); let mut current = self.ptr(); - let mut seen = FnvHashSet::default(); + let mut seen = FxHashSet::default(); while current != FREE_LIST_SENTINEL { let next = (*current).next_free.load(Ordering::Relaxed); assert!(!next.is_null()); diff --git a/components/style/selector_map.rs b/components/style/selector_map.rs index 3b0dcca9525..3ab78ce2d87 100644 --- a/components/style/selector_map.rs +++ b/components/style/selector_map.rs @@ -96,6 +96,8 @@ pub trait SelectorMapEntry: Sized + Clone { /// TODO: Tune the initial capacity of the HashMap #[derive(Debug, MallocSizeOf)] pub struct SelectorMap { + /// Rules that have `:root` selectors. + pub root: SmallVec<[T; 1]>, /// A hash from an ID to rules which contain that ID selector. pub id_hash: MaybeCaseInsensitiveHashMap>, /// A hash from a class name to rules which contain that class selector. @@ -104,7 +106,7 @@ pub struct SelectorMap { pub local_name_hash: PrecomputedHashMap>, /// A hash from namespace to rules which contain that namespace selector. pub namespace_hash: PrecomputedHashMap>, - /// Rules that don't have ID, class, or element selectors. + /// All other rules. pub other: SmallVec<[T; 1]>, /// The number of entries in this map. pub count: usize, @@ -124,6 +126,7 @@ impl SelectorMap { /// Trivially constructs an empty `SelectorMap`. pub fn new() -> Self { SelectorMap { + root: SmallVec::new(), id_hash: MaybeCaseInsensitiveHashMap::new(), class_hash: MaybeCaseInsensitiveHashMap::new(), local_name_hash: HashMap::default(), @@ -135,6 +138,7 @@ impl SelectorMap { /// Clears the hashmap retaining storage. pub fn clear(&mut self) { + self.root.clear(); self.id_hash.clear(); self.class_hash.clear(); self.local_name_hash.clear(); @@ -181,6 +185,19 @@ impl SelectorMap { // At the end, we're going to sort the rules that we added, so remember // where we began. let init_len = matching_rules_list.len(); + + if rule_hash_target.is_root() { + SelectorMap::get_matching_rules( + element, + &self.root, + matching_rules_list, + context, + flags_setter, + cascade_level, + shadow_cascade_order, + ); + } + if let Some(id) = rule_hash_target.id() { if let Some(rules) = self.id_hash.get(id, quirks_mode) { SelectorMap::get_matching_rules( @@ -287,6 +304,7 @@ impl SelectorMap { self.count += 1; let vector = match find_bucket(entry.selector()) { + Bucket::Root => &mut self.root, Bucket::ID(id) => self.id_hash .try_entry(id.clone(), quirks_mode)? .or_insert_with(SmallVec::new), @@ -340,6 +358,14 @@ impl SelectorMap { E: TElement, F: FnMut(&'a T) -> bool, { + if element.is_root() { + for entry in self.root.iter() { + if !f(&entry) { + return false; + } + } + } + if let Some(id) = element.id() { if let Some(v) = self.id_hash.get(id, quirks_mode) { for entry in v.iter() { @@ -444,6 +470,7 @@ impl SelectorMap { } enum Bucket<'a> { + Root, ID(&'a Atom), Class(&'a Atom), LocalName { @@ -456,6 +483,7 @@ enum Bucket<'a> { fn specific_bucket_for<'a>(component: &'a Component) -> Bucket<'a> { match *component { + Component::Root => Bucket::Root, Component::ID(ref id) => Bucket::ID(id), Component::Class(ref class) => Bucket::Class(class), Component::LocalName(ref selector) => Bucket::LocalName { @@ -498,14 +526,19 @@ fn find_bucket<'a>(mut iter: SelectorIter<'a, SelectorImpl>) -> Bucket<'a> { // We basically want to find the most specific bucket, // where: // - // id > class > local name > namespace > universal. + // root > id > class > local name > namespace > universal. // for ss in &mut iter { let new_bucket = specific_bucket_for(ss); match new_bucket { - Bucket::ID(..) => return new_bucket, - Bucket::Class(..) => { + Bucket::Root => return new_bucket, + Bucket::ID(..) => { current_bucket = new_bucket; + } + Bucket::Class(..) => { + if !matches!(current_bucket, Bucket::ID(..)) { + current_bucket = new_bucket; + } }, Bucket::LocalName { .. } => { if matches!(current_bucket, Bucket::Universal | Bucket::Namespace(..)) { diff --git a/components/style/servo/selector_parser.rs b/components/style/servo/selector_parser.rs index 894148a1786..2083539f51b 100644 --- a/components/style/servo/selector_parser.rs +++ b/components/style/servo/selector_parser.rs @@ -11,7 +11,7 @@ use attr::{AttrIdentifier, AttrValue}; use cssparser::{serialize_identifier, CowRcStr, Parser as CssParser, SourceLocation, ToCss}; use dom::{OpaqueNode, TElement, TNode}; use element_state::{DocumentState, ElementState}; -use fnv::FnvHashMap; +use fxhash::FxHashMap; use invalidation::element::document_state::InvalidationMatchingData; use invalidation::element::element_wrapper::ElementSnapshot; use properties::{ComputedValues, PropertyFlags}; @@ -617,12 +617,12 @@ impl SelectorImpl { /// A map from elements to snapshots for the Servo style back-end. #[derive(Debug)] -pub struct SnapshotMap(FnvHashMap); +pub struct SnapshotMap(FxHashMap); impl SnapshotMap { /// Create a new empty `SnapshotMap`. pub fn new() -> Self { - SnapshotMap(FnvHashMap::default()) + SnapshotMap(FxHashMap::default()) } /// Get a snapshot given an element. @@ -632,7 +632,7 @@ impl SnapshotMap { } impl Deref for SnapshotMap { - type Target = FnvHashMap; + type Target = FxHashMap; fn deref(&self) -> &Self::Target { &self.0 diff --git a/components/style/stylesheets/stylesheet.rs b/components/style/stylesheets/stylesheet.rs index ee24a918c73..02730f7090d 100644 --- a/components/style/stylesheets/stylesheet.rs +++ b/components/style/stylesheets/stylesheet.rs @@ -7,7 +7,7 @@ use context::QuirksMode; use cssparser::{Parser, ParserInput, RuleListParser}; use error_reporting::{ContextualParseError, ParseErrorReporter}; use fallible::FallibleVec; -use fnv::FnvHashMap; +use fxhash::FxHashMap; use invalidation::media_queries::{MediaListKey, ToMediaListKey}; #[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf}; @@ -42,7 +42,7 @@ pub struct UserAgentStylesheets { #[allow(missing_docs)] pub struct Namespaces { pub default: Option, - pub prefixes: FnvHashMap, + pub prefixes: FxHashMap, } /// The contents of a given stylesheet. This effectively maps to a diff --git a/components/style/stylist.rs b/components/style/stylist.rs index f4614f945af..52feb19c411 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -898,7 +898,6 @@ impl Stylist { where E: TElement, { - let pseudo = pseudo.canonical(); debug_assert!(pseudo.is_lazy()); // Apply the selector flags. We should be in sequential mode @@ -1892,7 +1891,7 @@ impl ElementAndPseudoRules { let map = match pseudo_element { None => &mut self.element_map, Some(pseudo) => self.pseudos_map - .get_or_insert_with(&pseudo.canonical(), || Box::new(SelectorMap::new())), + .get_or_insert_with(pseudo, || Box::new(SelectorMap::new())), }; map.insert(rule, quirks_mode) @@ -1906,7 +1905,7 @@ impl ElementAndPseudoRules { #[inline] fn rules(&self, pseudo: Option<&PseudoElement>) -> Option<&SelectorMap> { match pseudo { - Some(pseudo) => self.pseudos_map.get(&pseudo.canonical()).map(|p| &**p), + Some(pseudo) => self.pseudos_map.get(pseudo).map(|p| &**p), None => Some(&self.element_map), } } @@ -2190,7 +2189,7 @@ impl CascadeData { precomputed_pseudo_element_decls .as_mut() .expect("Expected precomputed declarations for the UA level") - .get_or_insert_with(&pseudo.canonical(), Vec::new) + .get_or_insert_with(pseudo, Vec::new) .push(ApplicableDeclarationBlock::new( StyleSource::from_rule(locked.clone()), self.rules_source_order, diff --git a/components/style/values/computed/box.rs b/components/style/values/computed/box.rs index f5c101cbc38..b0db8337608 100644 --- a/components/style/values/computed/box.rs +++ b/components/style/values/computed/box.rs @@ -10,7 +10,7 @@ use values::generics::box_::AnimationIterationCount as GenericAnimationIteration use values::generics::box_::Perspective as GenericPerspective; use values::generics::box_::VerticalAlign as GenericVerticalAlign; -pub use values::specified::box_::{AnimationName, Contain, Display, OverflowClipBox}; +pub use values::specified::box_::{AnimationName, Appearance, Contain, Display, OverflowClipBox}; pub use values::specified::box_::{Clear as SpecifiedClear, Float as SpecifiedFloat}; pub use values::specified::box_::{OverscrollBehavior, ScrollSnapType, TouchAction, TransitionProperty, WillChange}; diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index 14a74a4ed53..fa2a9cdcf0d 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -43,7 +43,7 @@ pub use self::font::{FontFamily, FontLanguageOverride, FontStyle, FontVariantEas pub use self::font::{FontFeatureSettings, FontVariantLigatures, FontVariantNumeric}; pub use self::font::{MozScriptLevel, MozScriptMinSize, MozScriptSizeMultiplier, XLang, XTextZoom}; pub use self::box_::{AnimationIterationCount, AnimationName, Contain, Display, TransitionProperty}; -pub use self::box_::{Clear, Float}; +pub use self::box_::{Appearance, Clear, Float}; pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective}; pub use self::box_::{ScrollSnapType, TouchAction, VerticalAlign, WillChange}; pub use self::color::{Color, ColorPropertyValue, RGBAColor}; diff --git a/components/style/values/specified/box.rs b/components/style/values/specified/box.rs index cec7c8ccc33..11084ce7d44 100644 --- a/components/style/values/specified/box.rs +++ b/components/style/values/specified/box.rs @@ -19,12 +19,16 @@ use values::generics::box_::VerticalAlign as GenericVerticalAlign; use values::specified::{AllowQuirks, Number}; use values::specified::length::{LengthOrPercentage, NonNegativeLength}; +fn in_ua_or_chrome_sheet(context: &ParserContext) -> bool { + use stylesheets::Origin; + context.stylesheet_origin == Origin::UserAgent || + context.chrome_rules_enabled() +} + #[cfg(feature = "gecko")] fn moz_display_values_enabled(context: &ParserContext) -> bool { use gecko_bindings::structs; - use stylesheets::Origin; - context.stylesheet_origin == Origin::UserAgent || - context.chrome_rules_enabled() || + in_ua_or_chrome_sheet(context) || unsafe { structs::StaticPrefs_sVarCache_layout_css_xul_display_values_content_enabled } @@ -33,37 +37,48 @@ fn moz_display_values_enabled(context: &ParserContext) -> bool { #[cfg(feature = "gecko")] fn moz_box_display_values_enabled(context: &ParserContext) -> bool { use gecko_bindings::structs; - use stylesheets::Origin; - context.stylesheet_origin == Origin::UserAgent || - context.chrome_rules_enabled() || + in_ua_or_chrome_sheet(context) || unsafe { structs::StaticPrefs_sVarCache_layout_css_xul_box_display_values_content_enabled } } +/// Defines an element’s display type, which consists of +/// the two basic qualities of how an element generates boxes +/// +/// +/// +/// NOTE(emilio): Order is important in Gecko! +/// +/// If you change it, make sure to take a look at the +/// FrameConstructionDataByDisplay stuff (both the XUL and non-XUL version), and +/// ensure it's still correct! +/// +/// Also, when you change this from Gecko you may need to regenerate the +/// C++-side bindings (see components/style/cbindgen.toml). #[allow(missing_docs)] #[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, SpecifiedValueInfo, ToComputedValue, ToCss)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -/// Defines an element’s display type, which consists of -/// the two basic qualities of how an element generates boxes -/// +#[repr(u8)] pub enum Display { - Inline, + None = 0, Block, + #[cfg(feature = "gecko")] + FlowRoot, + Inline, InlineBlock, + ListItem, Table, InlineTable, TableRowGroup, + TableColumn, + TableColumnGroup, TableHeaderGroup, TableFooterGroup, TableRow, - TableColumnGroup, - TableColumn, TableCell, TableCaption, - ListItem, - None, #[parse(aliases = "-webkit-flex")] Flex, #[parse(aliases = "-webkit-inline-flex")] @@ -85,8 +100,6 @@ pub enum Display { #[cfg(feature = "gecko")] Contents, #[cfg(feature = "gecko")] - FlowRoot, - #[cfg(feature = "gecko")] WebkitBox, #[cfg(feature = "gecko")] WebkitInlineBox, @@ -119,10 +132,10 @@ pub enum Display { MozDeck, #[cfg(feature = "gecko")] #[parse(condition = "moz_display_values_enabled")] - MozPopup, + MozGroupbox, #[cfg(feature = "gecko")] #[parse(condition = "moz_display_values_enabled")] - MozGroupbox, + MozPopup, } impl Display { @@ -870,3 +883,252 @@ pub enum Clear { InlineStart, InlineEnd } + +/// The value for the `appearance` property. +/// +/// https://developer.mozilla.org/en-US/docs/Web/CSS/-moz-appearance +/// +/// NOTE(emilio): When changing this you may want to regenerate the C++ bindings +/// (see components/style/cbindgen.toml) +#[allow(missing_docs)] +#[derive(Clone, Copy, Debug, Eq, Hash, MallocSizeOf, Parse, PartialEq, + SpecifiedValueInfo, ToCss, ToComputedValue)] +#[repr(u8)] +pub enum Appearance { + /// No appearance at all. + None, + /// A typical dialog button. + Button, + /// Various arrows that go in buttons + ButtonArrowDown, + ButtonArrowNext, + ButtonArrowPrevious, + ButtonArrowUp, + /// A rectangular button that contains complex content + /// like images (e.g. HTML