diff --git a/components/layout/animation.rs b/components/layout/animation.rs index 232f9cd2efe..be6d5baf77c 100644 --- a/components/layout/animation.rs +++ b/components/layout/animation.rs @@ -13,6 +13,7 @@ use script_traits::{AnimationState, ConstellationControlMsg, LayoutMsg as Conste use std::collections::HashMap; use std::sync::mpsc::Receiver; use style::animation::{Animation, update_style_for_animation}; +use style::font_metrics::ServoMetricsProvider; use style::selector_parser::RestyleDamage; use style::timer::Timer; @@ -143,7 +144,8 @@ pub fn recalc_style_for_animations(context: &LayoutContext, let old_style = fragment.style.clone(); update_style_for_animation(&context.style_context, animation, - &mut fragment.style); + &mut fragment.style, + &ServoMetricsProvider); damage |= RestyleDamage::compute(&old_style, &fragment.style); } } diff --git a/components/layout/traversal.rs b/components/layout/traversal.rs index 844a61527b6..88852e4193a 100644 --- a/components/layout/traversal.rs +++ b/components/layout/traversal.rs @@ -53,6 +53,7 @@ impl<'a> RecalcStyleAndConstructFlows<'a> { impl<'a, E> DomTraversal for RecalcStyleAndConstructFlows<'a> where E: TElement, E::ConcreteNode: LayoutNode, + E::FontMetricsProvider: Send, { type ThreadLocalContext = ScopedThreadLocalLayoutContext; diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index 1956cd6018d..4b118bdf7f1 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -67,6 +67,7 @@ use style::data::ElementData; use style::dom::{DescendantsBit, DirtyDescendants, LayoutIterator, NodeInfo, OpaqueNode}; use style::dom::{PresentationalHintsSynthetizer, TElement, TNode, UnsafeNode}; use style::element_state::*; +use style::font_metrics::ServoMetricsProvider; use style::properties::{ComputedValues, PropertyDeclarationBlock}; use style::selector_parser::{NonTSPseudoClass, PseudoElement, SelectorImpl}; use style::shared_lock::{SharedRwLock as StyleSharedRwLock, Locked as StyleLocked}; @@ -373,6 +374,8 @@ impl<'le> PresentationalHintsSynthetizer for ServoLayoutElement<'le> { impl<'le> TElement for ServoLayoutElement<'le> { type ConcreteNode = ServoLayoutNode<'le>; + type FontMetricsProvider = ServoMetricsProvider; + fn as_node(&self) -> ServoLayoutNode<'le> { ServoLayoutNode::from_layout_js(self.element.upcast()) } diff --git a/components/script_layout_interface/wrapper_traits.rs b/components/script_layout_interface/wrapper_traits.rs index a1c59d83114..88a1dbebb33 100644 --- a/components/script_layout_interface/wrapper_traits.rs +++ b/components/script_layout_interface/wrapper_traits.rs @@ -21,6 +21,7 @@ use style::context::SharedStyleContext; use style::data::ElementData; use style::dom::{LayoutIterator, NodeInfo, PresentationalHintsSynthetizer, TNode}; use style::dom::OpaqueNode; +use style::font_metrics::ServoMetricsProvider; use style::properties::{CascadeFlags, ServoComputedValues}; use style::selector_parser::{PseudoElement, PseudoElementCascadeType, SelectorImpl}; @@ -411,7 +412,8 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug + &context.guards, &style_pseudo, Some(data.styles().primary.values()), - CascadeFlags::empty()); + CascadeFlags::empty(), + &ServoMetricsProvider); data.styles_mut().cached_pseudos .insert(style_pseudo.clone(), new_style); } @@ -426,7 +428,8 @@ pub trait ThreadSafeLayoutElement: Clone + Copy + Sized + Debug + &context.guards, unsafe { &self.unsafe_get() }, &style_pseudo, - data.styles().primary.values()); + data.styles().primary.values(), + &ServoMetricsProvider); data.styles_mut().cached_pseudos .insert(style_pseudo.clone(), new_style.unwrap()); } diff --git a/components/style/animation.rs b/components/style/animation.rs index 6c540d3a708..aeff683f407 100644 --- a/components/style/animation.rs +++ b/components/style/animation.rs @@ -10,6 +10,7 @@ use bezier::Bezier; use context::SharedStyleContext; use dom::{OpaqueNode, UnsafeNode}; use euclid::point::Point2D; +use font_metrics::FontMetricsProvider; use keyframes::{KeyframesStep, KeyframesStepValue}; use properties::{self, CascadeFlags, ComputedValues, Importance}; use properties::animated_properties::{AnimatedProperty, TransitionProperty}; @@ -410,7 +411,8 @@ pub fn start_transitions_if_applicable(new_animations_sender: &Sender fn compute_style_for_animation_step(context: &SharedStyleContext, step: &KeyframesStep, previous_style: &ComputedValues, - style_from_cascade: &ComputedValues) + style_from_cascade: &ComputedValues, + font_metrics_provider: &FontMetricsProvider) -> ComputedValues { match step.value { KeyframesStepValue::ComputedValues => style_from_cascade.clone(), @@ -433,7 +435,7 @@ fn compute_style_for_animation_step(context: &SharedStyleContext, previous_style, /* cascade_info = */ None, &*context.error_reporter, - /* Metrics provider */ None, + font_metrics_provider, CascadeFlags::empty()); computed } @@ -534,7 +536,8 @@ pub fn update_style_for_animation_frame(mut new_style: &mut Arc, /// If `damage` is provided, inserts the appropriate restyle damage. pub fn update_style_for_animation(context: &SharedStyleContext, animation: &Animation, - style: &mut Arc) { + style: &mut Arc, + font_metrics_provider: &FontMetricsProvider) { debug!("update_style_for_animation: entering"); debug_assert!(!animation.is_expired()); @@ -658,7 +661,8 @@ pub fn update_style_for_animation(context: &SharedStyleContext, let from_style = compute_style_for_animation_step(context, last_keyframe, &**style, - &state.cascade_style); + &state.cascade_style, + font_metrics_provider); // NB: The spec says that the timing function can be overwritten // from the keyframe style. @@ -672,7 +676,8 @@ pub fn update_style_for_animation(context: &SharedStyleContext, let target_style = compute_style_for_animation_step(context, target_keyframe, &from_style, - &state.cascade_style); + &state.cascade_style, + font_metrics_provider); let mut new_style = (*style).clone(); diff --git a/components/style/build_gecko.rs b/components/style/build_gecko.rs index deffa87ce96..85997cdb3c2 100644 --- a/components/style/build_gecko.rs +++ b/components/style/build_gecko.rs @@ -308,6 +308,7 @@ mod bindings { "mozilla::SERVO_PREF_.*", "kNameSpaceID_.*", "kGenericFont_.*", + "kPresContext_.*", ]; let whitelist = [ "RawGecko.*", @@ -338,6 +339,7 @@ mod bindings { "FontFamilyListRefCnt", "FontFamilyName", "FontFamilyType", + "FontSizePrefs", "FragmentOrURL", "FrameRequestCallback", "GeckoParserExtraData", @@ -623,6 +625,7 @@ mod bindings { "ComputedTimingFunction_BeforeFlag", "FontFamilyList", "FontFamilyType", + "FontSizePrefs", "Keyframe", "ServoBundledURI", "ServoElementSnapshot", diff --git a/components/style/context.rs b/components/style/context.rs index 8d9871604a0..583b5bda187 100644 --- a/components/style/context.rs +++ b/components/style/context.rs @@ -12,6 +12,7 @@ use data::ElementData; use dom::{OpaqueNode, TNode, TElement, SendElement}; use error_reporting::ParseErrorReporter; use euclid::Size2D; +use font_metrics::FontMetricsProvider; #[cfg(feature = "gecko")] use gecko_bindings::structs; use matching::StyleSharingCandidateCache; use parking_lot::RwLock; @@ -291,6 +292,9 @@ pub struct ThreadLocalStyleContext { pub statistics: TraversalStatistics, /// Information related to the current element, non-None during processing. pub current_element_info: Option, + /// The struct used to compute and cache font metrics from style + /// for evaluation of the font-relative em/ch units and font-size + pub font_metrics_provider: E::FontMetricsProvider, } impl ThreadLocalStyleContext { @@ -303,6 +307,7 @@ impl ThreadLocalStyleContext { tasks: Vec::new(), statistics: TraversalStatistics::default(), current_element_info: None, + font_metrics_provider: E::FontMetricsProvider::create_from(shared), } } diff --git a/components/style/dom.rs b/components/style/dom.rs index bd201683afe..f64c594bd4e 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -12,6 +12,7 @@ use atomic_refcell::{AtomicRef, AtomicRefCell, AtomicRefMut}; #[cfg(feature = "gecko")] use context::UpdateAnimationsTasks; use data::ElementData; use element_state::ElementState; +use font_metrics::FontMetricsProvider; use properties::{ComputedValues, PropertyDeclarationBlock}; use selector_parser::{ElementExt, PreExistingComputedValues, PseudoElement}; use selectors::matching::ElementSelectorFlags; @@ -278,6 +279,12 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre /// The concrete node type. type ConcreteNode: TNode; + /// Type of the font metrics provider + /// + /// XXXManishearth It would be better to make this a type parameter on + /// ThreadLocalStyleContext and StyleContext + type FontMetricsProvider: FontMetricsProvider; + /// Get this element as a node. fn as_node(&self) -> Self::ConcreteNode; diff --git a/components/style/font_metrics.rs b/components/style/font_metrics.rs index 417c7999845..5210ded345e 100644 --- a/components/style/font_metrics.rs +++ b/components/style/font_metrics.rs @@ -8,6 +8,7 @@ use Atom; use app_units::Au; +use context::SharedStyleContext; use euclid::Size2D; use std::fmt; @@ -31,8 +32,44 @@ pub enum FontMetricsQueryResult { NotAvailable, } +// TODO: Servo's font metrics provider will probably not live in this crate, so this will +// have to be replaced with something else (perhaps a trait method on TElement) +// when we get there +#[derive(Debug)] +#[cfg(feature = "servo")] +/// Dummy metrics provider for Servo. Knows nothing about fonts and does not provide +/// any metrics. +pub struct ServoMetricsProvider; + +#[cfg(feature = "servo")] +impl FontMetricsProvider for ServoMetricsProvider { + fn create_from(_: &SharedStyleContext) -> Self { + ServoMetricsProvider + } + + fn get_size(&self, _font_name: &Atom, _font_family: u8) -> Au { + unreachable!("Dummy provider should never be used to compute font size") + } +} + +// Servo's font metrics provider will probably not live in this crate, so this will +// have to be replaced with something else (perhaps a trait method on TElement) +// when we get there + +#[cfg(feature = "gecko")] +/// Construct a font metrics provider for the current product +pub fn get_metrics_provider_for_product() -> ::gecko::wrapper::GeckoFontMetricsProvider { + ::gecko::wrapper::GeckoFontMetricsProvider::new() +} + +#[cfg(feature = "servo")] +/// Construct a font metrics provider for the current product +pub fn get_metrics_provider_for_product() -> ServoMetricsProvider { + ServoMetricsProvider +} + /// A trait used to represent something capable of providing us font metrics. -pub trait FontMetricsProvider: Send + Sync + fmt::Debug { +pub trait FontMetricsProvider: fmt::Debug { /// Obtain the metrics for given font family. /// /// TODO: We could make this take the full list, I guess, and save a few @@ -41,4 +78,11 @@ pub trait FontMetricsProvider: Send + Sync + fmt::Debug { fn query(&self, _font_name: &Atom) -> FontMetricsQueryResult { FontMetricsQueryResult::NotAvailable } + + /// Get default size of a given language and generic family + fn get_size(&self, font_name: &Atom, font_family: u8) -> Au; + + /// Construct from a shared style context + fn create_from(context: &SharedStyleContext) -> Self where Self: Sized; } + diff --git a/components/style/gecko/media_queries.rs b/components/style/gecko/media_queries.rs index 4b557aa2f8c..6ba63e75d8f 100644 --- a/components/style/gecko/media_queries.rs +++ b/components/style/gecko/media_queries.rs @@ -7,6 +7,7 @@ use app_units::Au; use cssparser::{CssStringWriter, Parser, Token}; use euclid::Size2D; +use font_metrics::get_metrics_provider_for_product; use gecko_bindings::bindings; use gecko_bindings::structs::{nsCSSValue, nsCSSUnit, nsStringBuffer}; use gecko_bindings::structs::{nsMediaExpression_Range, nsMediaFeature}; @@ -499,6 +500,8 @@ impl Expression { let default_values = device.default_computed_values(); + let provider = get_metrics_provider_for_product(); + // http://dev.w3.org/csswg/mediaqueries3/#units // em units are relative to the initial font-size. let context = computed::Context { @@ -509,7 +512,7 @@ impl Expression { // This cloning business is kind of dumb.... It's because Context // insists on having an actual ComputedValues inside itself. style: default_values.clone(), - font_metrics_provider: None, + font_metrics_provider: &provider, }; let required_value = match self.value { diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index 6f59864b322..e0b1b4af0f0 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -14,13 +14,15 @@ //! style system it's kind of pointless in the Stylo case, and only Servo forces //! the separation between the style system implementation and everything else. +use app_units::Au; use atomic_refcell::AtomicRefCell; -use context::UpdateAnimationsTasks; +use context::{SharedStyleContext, UpdateAnimationsTasks}; use data::ElementData; use dom::{self, AnimationRules, DescendantsBit, LayoutIterator, NodeInfo, TElement, TNode, UnsafeNode}; use dom::{OpaqueNode, PresentationalHintsSynthetizer}; use element_state::ElementState; use error_reporting::StdoutErrorReporter; +use font_metrics::FontMetricsProvider; use gecko::global_style_data::GLOBAL_STYLE_DATA; use gecko::selector_parser::{SelectorImpl, NonTSPseudoClass, PseudoElement}; use gecko::snapshot_helpers; @@ -60,6 +62,7 @@ use selectors::matching::{ElementSelectorFlags, StyleRelations}; use selectors::parser::{AttrSelector, NamespaceConstraint}; use shared_lock::Locked; use sink::Push; +use std::cell::RefCell; use std::fmt; use std::ptr; use std::sync::Arc; @@ -426,8 +429,65 @@ fn get_animation_rule(element: &GeckoElement, } } +#[derive(Debug)] +/// Gecko font metrics provider +pub struct GeckoFontMetricsProvider { + /// Cache of base font sizes for each language + /// + /// Usually will have 1 element. + /// + // This may be slow on pages using more languages, might be worth optimizing + // by caching lang->group mapping separately and/or using a hashmap on larger + // loads. + pub font_size_cache: RefCell>, +} + +impl GeckoFontMetricsProvider { + /// Construct + pub fn new() -> Self { + GeckoFontMetricsProvider { + font_size_cache: RefCell::new(Vec::new()), + } + } +} + +impl FontMetricsProvider for GeckoFontMetricsProvider { + fn create_from(_: &SharedStyleContext) -> GeckoFontMetricsProvider { + GeckoFontMetricsProvider::new() + } + + fn get_size(&self, font_name: &Atom, font_family: u8) -> Au { + use gecko_bindings::bindings::Gecko_GetBaseSize; + let mut cache = self.font_size_cache.borrow_mut(); + if let Some(sizes) = cache.iter().find(|el| el.0 == *font_name) { + return sizes.1.size_for_generic(font_family); + } + let sizes = unsafe { + Gecko_GetBaseSize(font_name.as_ptr()) + }; + cache.push((font_name.clone(), sizes)); + sizes.size_for_generic(font_family) + } +} + +impl structs::FontSizePrefs { + fn size_for_generic(&self, font_family: u8) -> Au { + Au(match font_family { + structs::kPresContext_DefaultVariableFont_ID => self.mDefaultVariableSize, + structs::kPresContext_DefaultFixedFont_ID => self.mDefaultFixedSize, + structs::kGenericFont_serif => self.mDefaultSerifSize, + structs::kGenericFont_sans_serif => self.mDefaultSansSerifSize, + structs::kGenericFont_monospace => self.mDefaultMonospaceSize, + structs::kGenericFont_cursive => self.mDefaultCursiveSize, + structs::kGenericFont_fantasy => self.mDefaultFantasySize, + x => unreachable!("Unknown generic ID {}", x), + }) + } +} + impl<'le> TElement for GeckoElement<'le> { type ConcreteNode = GeckoNode<'le>; + type FontMetricsProvider = GeckoFontMetricsProvider; fn as_node(&self) -> Self::ConcreteNode { unsafe { GeckoNode(&*(self.0 as *const _ as *const RawGeckoNode)) } diff --git a/components/style/gecko_bindings/bindings.rs b/components/style/gecko_bindings/bindings.rs index 2420617ab34..ba6f97defcf 100644 --- a/components/style/gecko_bindings/bindings.rs +++ b/components/style/gecko_bindings/bindings.rs @@ -27,6 +27,7 @@ use gecko_bindings::structs::TraversalRootBehavior; use gecko_bindings::structs::ComputedTimingFunction_BeforeFlag; use gecko_bindings::structs::FontFamilyList; use gecko_bindings::structs::FontFamilyType; +use gecko_bindings::structs::FontSizePrefs; use gecko_bindings::structs::Keyframe; use gecko_bindings::structs::ServoBundledURI; use gecko_bindings::structs::ServoElementSnapshot; @@ -1062,10 +1063,7 @@ extern "C" { aSource: *const nsStyleFont); } extern "C" { - pub fn Gecko_nsStyleFont_GetBaseSize(font: *const nsStyleFont, - pres_context: - RawGeckoPresContextBorrowed) - -> nscoord; + pub fn Gecko_GetBaseSize(lang: *mut nsIAtom) -> FontSizePrefs; } extern "C" { pub fn Gecko_GetMediaFeatures() -> *const nsMediaFeature; diff --git a/components/style/gecko_bindings/structs_debug.rs b/components/style/gecko_bindings/structs_debug.rs index 65a0dc3adf8..1c65c51d414 100644 --- a/components/style/gecko_bindings/structs_debug.rs +++ b/components/style/gecko_bindings/structs_debug.rs @@ -19547,6 +19547,8 @@ pub mod root { #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct gfxMissingFontRecorder([u8; 0]); + pub const kPresContext_DefaultVariableFont_ID: u8 = 0; + pub const kPresContext_DefaultFixedFont_ID: u8 = 1; #[repr(C)] #[derive(Debug)] pub struct nsRootPresContext { @@ -27150,6 +27152,67 @@ pub mod root { fn clone(&self) -> Self { *self } } #[repr(C)] + #[derive(Debug, Copy)] + pub struct FontSizePrefs { + pub mDefaultVariableSize: root::nscoord, + pub mDefaultFixedSize: root::nscoord, + pub mDefaultSerifSize: root::nscoord, + pub mDefaultSansSerifSize: root::nscoord, + pub mDefaultMonospaceSize: root::nscoord, + pub mDefaultCursiveSize: root::nscoord, + pub mDefaultFantasySize: root::nscoord, + } + #[test] + fn bindgen_test_layout_FontSizePrefs() { + assert_eq!(::std::mem::size_of::() , 28usize , concat ! + ( "Size of: " , stringify ! ( FontSizePrefs ) )); + assert_eq! (::std::mem::align_of::() , 4usize , concat + ! ( "Alignment of " , stringify ! ( FontSizePrefs ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const FontSizePrefs ) ) . + mDefaultVariableSize as * const _ as usize } , 0usize , + concat ! ( + "Alignment of field: " , stringify ! ( FontSizePrefs ) , + "::" , stringify ! ( mDefaultVariableSize ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const FontSizePrefs ) ) . mDefaultFixedSize + as * const _ as usize } , 4usize , concat ! ( + "Alignment of field: " , stringify ! ( FontSizePrefs ) , + "::" , stringify ! ( mDefaultFixedSize ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const FontSizePrefs ) ) . mDefaultSerifSize + as * const _ as usize } , 8usize , concat ! ( + "Alignment of field: " , stringify ! ( FontSizePrefs ) , + "::" , stringify ! ( mDefaultSerifSize ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const FontSizePrefs ) ) . + mDefaultSansSerifSize as * const _ as usize } , 12usize , + concat ! ( + "Alignment of field: " , stringify ! ( FontSizePrefs ) , + "::" , stringify ! ( mDefaultSansSerifSize ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const FontSizePrefs ) ) . + mDefaultMonospaceSize as * const _ as usize } , 16usize , + concat ! ( + "Alignment of field: " , stringify ! ( FontSizePrefs ) , + "::" , stringify ! ( mDefaultMonospaceSize ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const FontSizePrefs ) ) . + mDefaultCursiveSize as * const _ as usize } , 20usize , + concat ! ( + "Alignment of field: " , stringify ! ( FontSizePrefs ) , + "::" , stringify ! ( mDefaultCursiveSize ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const FontSizePrefs ) ) . + mDefaultFantasySize as * const _ as usize } , 24usize , + concat ! ( + "Alignment of field: " , stringify ! ( FontSizePrefs ) , + "::" , stringify ! ( mDefaultFantasySize ) )); + } + impl Clone for FontSizePrefs { + fn clone(&self) -> Self { *self } + } + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct nsROCSSPrimitiveValue([u8; 0]); #[repr(C)] diff --git a/components/style/gecko_bindings/structs_release.rs b/components/style/gecko_bindings/structs_release.rs index 9d00e454f34..e75474a2b43 100644 --- a/components/style/gecko_bindings/structs_release.rs +++ b/components/style/gecko_bindings/structs_release.rs @@ -18981,6 +18981,8 @@ pub mod root { #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct gfxMissingFontRecorder([u8; 0]); + pub const kPresContext_DefaultVariableFont_ID: u8 = 0; + pub const kPresContext_DefaultFixedFont_ID: u8 = 1; #[repr(C)] #[derive(Debug)] pub struct nsRootPresContext { @@ -26491,6 +26493,67 @@ pub mod root { fn clone(&self) -> Self { *self } } #[repr(C)] + #[derive(Debug, Copy)] + pub struct FontSizePrefs { + pub mDefaultVariableSize: root::nscoord, + pub mDefaultFixedSize: root::nscoord, + pub mDefaultSerifSize: root::nscoord, + pub mDefaultSansSerifSize: root::nscoord, + pub mDefaultMonospaceSize: root::nscoord, + pub mDefaultCursiveSize: root::nscoord, + pub mDefaultFantasySize: root::nscoord, + } + #[test] + fn bindgen_test_layout_FontSizePrefs() { + assert_eq!(::std::mem::size_of::() , 28usize , concat ! + ( "Size of: " , stringify ! ( FontSizePrefs ) )); + assert_eq! (::std::mem::align_of::() , 4usize , concat + ! ( "Alignment of " , stringify ! ( FontSizePrefs ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const FontSizePrefs ) ) . + mDefaultVariableSize as * const _ as usize } , 0usize , + concat ! ( + "Alignment of field: " , stringify ! ( FontSizePrefs ) , + "::" , stringify ! ( mDefaultVariableSize ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const FontSizePrefs ) ) . mDefaultFixedSize + as * const _ as usize } , 4usize , concat ! ( + "Alignment of field: " , stringify ! ( FontSizePrefs ) , + "::" , stringify ! ( mDefaultFixedSize ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const FontSizePrefs ) ) . mDefaultSerifSize + as * const _ as usize } , 8usize , concat ! ( + "Alignment of field: " , stringify ! ( FontSizePrefs ) , + "::" , stringify ! ( mDefaultSerifSize ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const FontSizePrefs ) ) . + mDefaultSansSerifSize as * const _ as usize } , 12usize , + concat ! ( + "Alignment of field: " , stringify ! ( FontSizePrefs ) , + "::" , stringify ! ( mDefaultSansSerifSize ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const FontSizePrefs ) ) . + mDefaultMonospaceSize as * const _ as usize } , 16usize , + concat ! ( + "Alignment of field: " , stringify ! ( FontSizePrefs ) , + "::" , stringify ! ( mDefaultMonospaceSize ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const FontSizePrefs ) ) . + mDefaultCursiveSize as * const _ as usize } , 20usize , + concat ! ( + "Alignment of field: " , stringify ! ( FontSizePrefs ) , + "::" , stringify ! ( mDefaultCursiveSize ) )); + assert_eq! (unsafe { + & ( * ( 0 as * const FontSizePrefs ) ) . + mDefaultFantasySize as * const _ as usize } , 24usize , + concat ! ( + "Alignment of field: " , stringify ! ( FontSizePrefs ) , + "::" , stringify ! ( mDefaultFantasySize ) )); + } + impl Clone for FontSizePrefs { + fn clone(&self) -> Self { *self } + } + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct nsROCSSPrimitiveValue([u8; 0]); #[repr(C)] diff --git a/components/style/gecko_string_cache/mod.rs b/components/style/gecko_string_cache/mod.rs index b10fd58174a..ca2c37ec213 100644 --- a/components/style/gecko_string_cache/mod.rs +++ b/components/style/gecko_string_cache/mod.rs @@ -186,10 +186,11 @@ impl fmt::Display for WeakAtom { impl Atom { /// Execute a callback with the atom represented by `ptr`. - pub unsafe fn with(ptr: *mut nsIAtom, callback: &mut F) where F: FnMut(&Atom) { + pub unsafe fn with(ptr: *mut nsIAtom, callback: &mut F) -> R where F: FnMut(&Atom) -> R { let atom = Atom(WeakAtom::new(ptr)); - callback(&atom); + let ret = callback(&atom); mem::forget(atom); + ret } /// Creates an atom from an static atom pointer without checking in release diff --git a/components/style/matching.rs b/components/style/matching.rs index f0f288ef00f..7fab97d7d7a 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -15,6 +15,7 @@ use cascade_info::CascadeInfo; use context::{SequentialTask, SharedStyleContext, StyleContext}; use data::{ComputedStyle, ElementData, ElementStyles, RestyleData}; use dom::{AnimationRules, SendElement, TElement, TNode}; +use font_metrics::FontMetricsProvider; use properties::{CascadeFlags, ComputedValues, SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, cascade}; use properties::longhands::display::computed_value as display; use restyle_hints::{RESTYLE_STYLE_ATTRIBUTE, RESTYLE_CSS_ANIMATIONS, RestyleHint}; @@ -323,6 +324,7 @@ trait PrivateMatchMethods: TElement { fn cascade_with_rules(&self, shared_context: &SharedStyleContext, + font_metrics_provider: &FontMetricsProvider, rule_node: &StrongRuleNode, primary_style: &ComputedStyle, cascade_flags: CascadeFlags, @@ -388,6 +390,7 @@ trait PrivateMatchMethods: TElement { layout_parent_style, Some(&mut cascade_info), &*shared_context.error_reporter, + font_metrics_provider, cascade_flags)); cascade_info.finish(&self.as_node()); @@ -406,7 +409,8 @@ trait PrivateMatchMethods: TElement { // Grab the rule node. let rule_node = &pseudo_style.as_ref().map_or(primary_style, |p| &*p.1).rules; - self.cascade_with_rules(context.shared, rule_node, primary_style, cascade_flags, pseudo_style.is_some()) + self.cascade_with_rules(context.shared, &context.thread_local.font_metrics_provider, + rule_node, primary_style, cascade_flags, pseudo_style.is_some()) } /// Computes values and damage for the primary or pseudo style of an element, @@ -470,6 +474,7 @@ trait PrivateMatchMethods: TElement { cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP) } self.cascade_with_rules(context.shared, + &context.thread_local.font_metrics_provider, &without_transition_rules, primary_style, cascade_flags, @@ -531,7 +536,8 @@ trait PrivateMatchMethods: TElement { let shared_context = context.shared; if let Some(ref mut old) = *old_values { self.update_animations_for_cascade(shared_context, old, - possibly_expired_animations); + possibly_expired_animations, + &context.thread_local.font_metrics_provider); } let new_animations_sender = &context.thread_local.new_animations_sender; @@ -606,7 +612,8 @@ trait PrivateMatchMethods: TElement { fn update_animations_for_cascade(&self, context: &SharedStyleContext, style: &mut Arc, - possibly_expired_animations: &mut Vec) { + possibly_expired_animations: &mut Vec, + font_metrics: &FontMetricsProvider) { // Finish any expired transitions. let this_opaque = self.as_node().opaque(); animation::complete_expired_transitions(this_opaque, style, context); @@ -633,7 +640,8 @@ trait PrivateMatchMethods: TElement { if !running_animation.is_expired() { animation::update_style_for_animation(context, running_animation, - style); + style, + font_metrics); if let Animation::Transition(_, _, _, ref frame, _) = *running_animation { possibly_expired_animations.push(frame.property_animation.clone()) } @@ -1145,6 +1153,7 @@ pub trait MatchMethods : TElement { /// Returns computed values without animation and transition rules. fn get_base_style(&self, shared_context: &SharedStyleContext, + font_metrics_provider: &FontMetricsProvider, primary_style: &ComputedStyle, pseudo_style: &Option<(&PseudoElement, &ComputedStyle)>) -> Arc { @@ -1163,6 +1172,7 @@ pub trait MatchMethods : TElement { cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP) } self.cascade_with_rules(shared_context, + font_metrics_provider, &without_animation_rules, primary_style, cascade_flags, diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index b7ef293239c..64c83d418c5 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -536,7 +536,8 @@ ${helpers.single_keyword("font-variant-caps", type ComputedValue = Au; #[inline] fn to_computed_value(&self, cx: &Context) -> computed_value::T { - use gecko_bindings::bindings::Gecko_nsStyleFont_GetBaseSize; + use gecko_bindings::bindings::Gecko_GetBaseSize; + use gecko_bindings::structs; use values::specified::length::au_to_int_px; // Data from nsRuleNode.cpp in Gecko // Mapping from base size and HTML size to pixels @@ -561,10 +562,11 @@ ${helpers.single_keyword("font-variant-caps", // XXXManishearth handle quirks mode - let base_size = unsafe { - Gecko_nsStyleFont_GetBaseSize(cx.style().get_font().gecko(), - &*cx.device.pres_context) - }; + let ref gecko_font = cx.style().get_font().gecko(); + let base_size = unsafe { Atom::with(gecko_font.mLanguage.raw(), &mut |atom| { + cx.font_metrics_provider.get_size(atom, gecko_font.mGenericID).0 + }) }; + let base_size_px = au_to_int_px(base_size as f32); let html_size = *self as usize; if base_size_px >= 9 && base_size_px <= 16 { diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 88121aa32f3..d1c8e0849af 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -1944,6 +1944,7 @@ pub fn cascade(device: &Device, layout_parent_style: Option<<&ComputedValues>, cascade_info: Option<<&mut CascadeInfo>, error_reporter: &ParseErrorReporter, + font_metrics_provider: &FontMetricsProvider, flags: CascadeFlags) -> ComputedValues { debug_assert_eq!(parent_style.is_some(), layout_parent_style.is_some()); @@ -1988,7 +1989,7 @@ pub fn cascade(device: &Device, layout_parent_style, cascade_info, error_reporter, - None, + font_metrics_provider, flags) } @@ -2002,7 +2003,7 @@ pub fn apply_declarations<'a, F, I>(device: &Device, layout_parent_style: &ComputedValues, mut cascade_info: Option<<&mut CascadeInfo>, error_reporter: &ParseErrorReporter, - font_metrics_provider: Option<<&FontMetricsProvider>, + font_metrics_provider: &FontMetricsProvider, flags: CascadeFlags) -> ComputedValues where F: Fn() -> I, @@ -2109,6 +2110,7 @@ pub fn apply_declarations<'a, F, I>(device: &Device, | LonghandId::TextOrientation | LonghandId::AnimationName | LonghandId::TransitionProperty + | LonghandId::XLang % endif ); if diff --git a/components/style/servo/media_queries.rs b/components/style/servo/media_queries.rs index 75bd789766f..95a537bfdfb 100644 --- a/components/style/servo/media_queries.rs +++ b/components/style/servo/media_queries.rs @@ -7,6 +7,7 @@ use app_units::Au; use cssparser::Parser; use euclid::{Size2D, TypedSize2D}; +use font_metrics::ServoMetricsProvider; use media_queries::MediaType; use properties::ComputedValues; use std::fmt; @@ -185,7 +186,10 @@ impl Range { // This cloning business is kind of dumb.... It's because Context // insists on having an actual ComputedValues inside itself. style: default_values.clone(), - font_metrics_provider: None + // Servo doesn't support font metrics + // A real provider will be needed here once we do; since + // ch units can exist in media queries. + font_metrics_provider: &ServoMetricsProvider, }; match *self { diff --git a/components/style/stylist.rs b/components/style/stylist.rs index 2982457364a..4731ec84cb9 100644 --- a/components/style/stylist.rs +++ b/components/style/stylist.rs @@ -10,6 +10,7 @@ use {Atom, LocalName}; use data::ComputedStyle; use dom::{AnimationRules, PresentationalHintsSynthetizer, TElement}; use error_reporting::StdoutErrorReporter; +use font_metrics::FontMetricsProvider; use keyframes::KeyframesAnimation; use media_queries::Device; use pdqsort::sort_by; @@ -345,7 +346,8 @@ impl Stylist { guards: &StylesheetGuards, pseudo: &PseudoElement, parent: Option<&Arc>, - cascade_flags: CascadeFlags) + cascade_flags: CascadeFlags, + font_metrics: &FontMetricsProvider) -> ComputedStyle { debug_assert!(pseudo.is_precomputed()); @@ -381,6 +383,7 @@ impl Stylist { parent.map(|p| &**p), None, &StdoutErrorReporter, + font_metrics, cascade_flags); ComputedStyle::new(rule_node, Arc::new(computed)) } @@ -392,6 +395,8 @@ impl Stylist { pseudo: &PseudoElement, parent_style: &Arc) -> Arc { + use font_metrics::ServoMetricsProvider; + // For most (but not all) pseudo-elements, we inherit all values from the parent. let inherit_all = match *pseudo { PseudoElement::ServoText | @@ -416,7 +421,8 @@ impl Stylist { if inherit_all { cascade_flags.insert(INHERIT_ALL); } - self.precomputed_values_for_pseudo(guards, &pseudo, Some(parent_style), cascade_flags) + self.precomputed_values_for_pseudo(guards, &pseudo, Some(parent_style), cascade_flags, + &ServoMetricsProvider) .values.unwrap() } @@ -431,7 +437,8 @@ impl Stylist { guards: &StylesheetGuards, element: &E, pseudo: &PseudoElement, - parent: &Arc) + parent: &Arc, + font_metrics: &FontMetricsProvider) -> Option where E: TElement + fmt::Debug + @@ -494,6 +501,7 @@ impl Stylist { Some(&**parent), None, &StdoutErrorReporter, + font_metrics, CascadeFlags::empty()); Some(ComputedStyle::new(rule_node, Arc::new(computed))) diff --git a/components/style/values/computed/mod.rs b/components/style/values/computed/mod.rs index b2b64e1c212..47d809c5ebb 100644 --- a/components/style/values/computed/mod.rs +++ b/components/style/values/computed/mod.rs @@ -57,9 +57,7 @@ pub struct Context<'a> { /// A font metrics provider, used to access font metrics to implement /// font-relative units. - /// - /// TODO(emilio): This should be required, see #14079. - pub font_metrics_provider: Option<&'a FontMetricsProvider>, + pub font_metrics_provider: &'a FontMetricsProvider, } impl<'a> Context<'a> { diff --git a/components/style/values/specified/length.rs b/components/style/values/specified/length.rs index 5e96e0e40f6..d6dcad04a3b 100644 --- a/components/style/values/specified/length.rs +++ b/components/style/values/specified/length.rs @@ -73,11 +73,9 @@ impl FontRelativeLength { /// font-family list. pub fn find_first_available_font_metrics(context: &Context) -> Option { use font_metrics::FontMetricsQueryResult::*; - if let Some(ref metrics_provider) = context.font_metrics_provider { - for family in context.style().get_font().font_family_iter() { - if let Available(metrics) = metrics_provider.query(family.atom()) { - return metrics; - } + for family in context.style().get_font().font_family_iter() { + if let Available(metrics) = context.font_metrics_provider.query(family.atom()) { + return metrics; } } diff --git a/components/style/viewport.rs b/components/style/viewport.rs index d2d2af77480..22426a2fbc6 100644 --- a/components/style/viewport.rs +++ b/components/style/viewport.rs @@ -13,6 +13,7 @@ use app_units::Au; use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser, parse_important}; use cssparser::ToCss as ParserToCss; use euclid::size::TypedSize2D; +use font_metrics::get_metrics_provider_for_product; use media_queries::Device; use parser::{Parse, ParserContext, log_css_error}; use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard}; @@ -672,6 +673,8 @@ impl MaybeNew for ViewportConstraints { // resolved against initial values let initial_viewport = device.au_viewport_size(); + let provider = get_metrics_provider_for_product(); + // TODO(emilio): Stop cloning `ComputedValues` around! let context = Context { is_root_element: false, @@ -679,7 +682,7 @@ impl MaybeNew for ViewportConstraints { inherited_style: device.default_computed_values(), layout_parent_style: device.default_computed_values(), style: device.default_computed_values().clone(), - font_metrics_provider: None, // TODO: Should have! + font_metrics_provider: &provider, }; // DEVICE-ADAPT ยง 9.3 Resolving 'extend-to-zoom' diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 99d48fe58e5..2e65da44ab0 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -20,6 +20,7 @@ use style::data::{ElementData, ElementStyles, RestyleData}; use style::dom::{AnimationOnlyDirtyDescendants, DirtyDescendants}; use style::dom::{ShowSubtreeData, TElement, TNode}; use style::error_reporting::StdoutErrorReporter; +use style::font_metrics::get_metrics_provider_for_product; use style::gecko::data::{PerDocumentStyleData, PerDocumentStyleDataImpl}; use style::gecko::global_style_data::GLOBAL_STYLE_DATA; use style::gecko::restyle_damage::GeckoRestyleDamage; @@ -418,7 +419,8 @@ pub extern "C" fn Servo_StyleSet_GetBaseComputedValuesForElement(raw_data: RawSe let pseudos = &styles.pseudos; let pseudo_style = pseudo.as_ref().map(|p| (p, pseudos.get(p).unwrap())); - element.get_base_style(shared_context, &styles.primary, &pseudo_style) + let provider = get_metrics_provider_for_product(); + element.get_base_style(shared_context, &provider, &styles.primary, &pseudo_style) .into_strong() } @@ -812,8 +814,9 @@ pub extern "C" fn Servo_ComputedValues_GetForAnonymousBox(parent_style_or_null: if skip_display_fixup { cascade_flags.insert(SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP); } + let metrics = get_metrics_provider_for_product(); data.stylist.precomputed_values_for_pseudo(&guards, &pseudo, maybe_parent, - cascade_flags) + cascade_flags, &metrics) .values.unwrap() .into_strong() } @@ -859,10 +862,12 @@ fn get_pseudo_style(guard: &SharedRwLockReadGuard, element: GeckoElement, pseudo let d = doc_data.borrow_mut(); let base = styles.primary.values(); let guards = StylesheetGuards::same(guard); + let metrics = get_metrics_provider_for_product(); d.stylist.lazily_compute_pseudo_element_style(&guards, &element, &pseudo, - base) + base, + &metrics) .map(|s| s.values().clone()) }, } @@ -1672,6 +1677,7 @@ pub extern "C" fn Servo_GetComputedKeyframeValues(keyframes: RawGeckoKeyframeLis let parent_style = parent_style.as_ref().map(|r| &**ComputedValues::as_arc(&r)); let default_values = data.default_computed_values(); + let metrics = get_metrics_provider_for_product(); let context = Context { is_root_element: false, @@ -1679,7 +1685,7 @@ pub extern "C" fn Servo_GetComputedKeyframeValues(keyframes: RawGeckoKeyframeLis inherited_style: parent_style.unwrap_or(default_values), layout_parent_style: parent_style.unwrap_or(default_values), style: (**style).clone(), - font_metrics_provider: None, + font_metrics_provider: &metrics, }; for (index, keyframe) in keyframes.iter().enumerate() { diff --git a/tests/unit/style/parsing/image.rs b/tests/unit/style/parsing/image.rs index 7b42def9758..3e3bce84f89 100644 --- a/tests/unit/style/parsing/image.rs +++ b/tests/unit/style/parsing/image.rs @@ -6,6 +6,7 @@ use cssparser::Parser; use euclid::size::TypedSize2D; use media_queries::CSSErrorReporterTest; use std::f32::consts::PI; +use style::font_metrics::ServoMetricsProvider; use style::media_queries::{Device, MediaType}; use style::parser::ParserContext; use style::properties::ComputedValues; @@ -51,7 +52,7 @@ fn test_linear_gradient() { inherited_style: initial_style, layout_parent_style: initial_style, style: initial_style.clone(), - font_metrics_provider: None, + font_metrics_provider: &ServoMetricsProvider, }; assert_eq!(specified::AngleOrCorner::None.to_computed_value(&specified_context), computed::AngleOrCorner::Angle(Angle::from_radians(PI)));