Auto merge of #16333 - Manishearth:stylo-metrics, r=heycam

stylo: support font metrics

r=heycam https://bugzilla.mozilla.org/show_bug.cgi?id=1341724

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/16333)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-04-10 01:35:18 -05:00 committed by GitHub
commit dafa6c94eb
14 changed files with 135 additions and 58 deletions

View file

@ -343,6 +343,7 @@ mod bindings {
"FragmentOrURL",
"FrameRequestCallback",
"GeckoParserExtraData",
"GeckoFontMetrics",
"gfxAlternateValue",
"gfxFontFeature",
"gfxFontVariation",
@ -626,6 +627,7 @@ mod bindings {
"FontFamilyList",
"FontFamilyType",
"FontSizePrefs",
"GeckoFontMetrics",
"Keyframe",
"ServoBundledURI",
"ServoElementSnapshot",

View file

@ -9,7 +9,9 @@
use Atom;
use app_units::Au;
use context::SharedStyleContext;
use euclid::Size2D;
use logical_geometry::WritingMode;
use media_queries::Device;
use properties::style_structs::Font;
use std::fmt;
/// Represents the font metrics that style needs from a font to compute the
@ -18,8 +20,8 @@ use std::fmt;
pub struct FontMetrics {
/// The x-height of the font.
pub x_height: Au,
/// The zero advance.
pub zero_advance_measure: Size2D<Au>,
/// The zero advance. This is usually writing mode dependent
pub zero_advance_measure: Au,
}
/// The result for querying font metrics for a given font family.
@ -27,11 +29,30 @@ pub struct FontMetrics {
pub enum FontMetricsQueryResult {
/// The font is available, but we may or may not have found any font metrics
/// for it.
Available(Option<FontMetrics>),
Available(FontMetrics),
/// The font is not available.
NotAvailable,
}
/// A trait used to represent something capable of providing us font metrics.
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
/// virtual calls in the case we are repeatedly unable to find font metrics?
/// That is not too common in practice though.
fn query(&self, _font: &Font, _font_size: Au, _wm: WritingMode,
_in_media_query: bool, _device: &Device) -> FontMetricsQueryResult {
FontMetricsQueryResult::NotAvailable
}
/// 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;
}
// 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
@ -67,22 +88,3 @@ pub fn get_metrics_provider_for_product() -> ::gecko::wrapper::GeckoFontMetricsP
pub fn get_metrics_provider_for_product() -> ServoMetricsProvider {
ServoMetricsProvider
}
/// A trait used to represent something capable of providing us font metrics.
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
/// virtual calls in the case we are repeatedly unable to find font metrics?
/// That is not too common in practice though.
fn query(&self, _font_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;
}

View file

@ -513,6 +513,7 @@ impl Expression {
// insists on having an actual ComputedValues inside itself.
style: default_values.clone(),
font_metrics_provider: &provider,
in_media_query: true,
};
let required_value = match self.value {

View file

@ -22,7 +22,7 @@ use dom::{self, AnimationRules, DescendantsBit, LayoutIterator, NodeInfo, TEleme
use dom::{OpaqueNode, PresentationalHintsSynthetizer};
use element_state::ElementState;
use error_reporting::StdoutErrorReporter;
use font_metrics::FontMetricsProvider;
use font_metrics::{FontMetrics, FontMetricsProvider, FontMetricsQueryResult};
use gecko::global_style_data::GLOBAL_STYLE_DATA;
use gecko::selector_parser::{SelectorImpl, NonTSPseudoClass, PseudoElement};
use gecko::snapshot_helpers;
@ -51,10 +51,13 @@ use gecko_bindings::structs::NODE_HAS_DIRTY_DESCENDANTS_FOR_SERVO;
use gecko_bindings::structs::NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE;
use gecko_bindings::structs::NODE_IS_NATIVE_ANONYMOUS;
use gecko_bindings::sugar::ownership::HasArcFFI;
use logical_geometry::WritingMode;
use media_queries::Device;
use parking_lot::RwLock;
use properties::{ComputedValues, parse_style_attribute};
use properties::{Importance, PropertyDeclaration, PropertyDeclarationBlock};
use properties::animated_properties::AnimationValueMap;
use properties::style_structs::Font;
use rule_tree::CascadeLevel as ServoCascadeLevel;
use selector_parser::{ElementExt, Snapshot};
use selectors::Element;
@ -468,6 +471,24 @@ impl FontMetricsProvider for GeckoFontMetricsProvider {
cache.push((font_name.clone(), sizes));
sizes.size_for_generic(font_family)
}
fn query(&self, font: &Font, font_size: Au, wm: WritingMode,
in_media_query: bool, device: &Device) -> FontMetricsQueryResult {
use gecko_bindings::bindings::Gecko_GetFontMetrics;
let gecko_metrics = unsafe {
Gecko_GetFontMetrics(&*device.pres_context,
wm.is_vertical() && !wm.is_sideways(),
font.gecko(),
font_size.0,
// we don't use the user font set in a media query
!in_media_query)
};
let metrics = FontMetrics {
x_height: Au(gecko_metrics.mXSize),
zero_advance_measure: Au(gecko_metrics.mChSize),
};
FontMetricsQueryResult::Available(metrics)
}
}
impl structs::FontSizePrefs {

View file

@ -28,6 +28,7 @@ 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::GeckoFontMetrics;
use gecko_bindings::structs::Keyframe;
use gecko_bindings::structs::ServoBundledURI;
use gecko_bindings::structs::ServoElementSnapshot;
@ -1076,6 +1077,12 @@ extern "C" {
extern "C" {
pub fn Gecko_GetBaseSize(lang: *mut nsIAtom) -> FontSizePrefs;
}
extern "C" {
pub fn Gecko_GetFontMetrics(pres_context: RawGeckoPresContextBorrowed,
is_vertical: bool, font: *const nsStyleFont,
font_size: nscoord, use_user_font_set: bool)
-> GeckoFontMetrics;
}
extern "C" {
pub fn Gecko_GetMediaFeatures() -> *const nsMediaFeature;
}

View file

@ -27213,6 +27213,33 @@ pub mod root {
fn clone(&self) -> Self { *self }
}
#[repr(C)]
#[derive(Debug, Copy)]
pub struct GeckoFontMetrics {
pub mChSize: root::nscoord,
pub mXSize: root::nscoord,
}
#[test]
fn bindgen_test_layout_GeckoFontMetrics() {
assert_eq!(::std::mem::size_of::<GeckoFontMetrics>() , 8usize , concat
! ( "Size of: " , stringify ! ( GeckoFontMetrics ) ));
assert_eq! (::std::mem::align_of::<GeckoFontMetrics>() , 4usize ,
concat ! (
"Alignment of " , stringify ! ( GeckoFontMetrics ) ));
assert_eq! (unsafe {
& ( * ( 0 as * const GeckoFontMetrics ) ) . mChSize as *
const _ as usize } , 0usize , concat ! (
"Alignment of field: " , stringify ! ( GeckoFontMetrics )
, "::" , stringify ! ( mChSize ) ));
assert_eq! (unsafe {
& ( * ( 0 as * const GeckoFontMetrics ) ) . mXSize as *
const _ as usize } , 4usize , concat ! (
"Alignment of field: " , stringify ! ( GeckoFontMetrics )
, "::" , stringify ! ( mXSize ) ));
}
impl Clone for GeckoFontMetrics {
fn clone(&self) -> Self { *self }
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct nsROCSSPrimitiveValue([u8; 0]);
#[repr(C)]

View file

@ -26554,6 +26554,33 @@ pub mod root {
fn clone(&self) -> Self { *self }
}
#[repr(C)]
#[derive(Debug, Copy)]
pub struct GeckoFontMetrics {
pub mChSize: root::nscoord,
pub mXSize: root::nscoord,
}
#[test]
fn bindgen_test_layout_GeckoFontMetrics() {
assert_eq!(::std::mem::size_of::<GeckoFontMetrics>() , 8usize , concat
! ( "Size of: " , stringify ! ( GeckoFontMetrics ) ));
assert_eq! (::std::mem::align_of::<GeckoFontMetrics>() , 4usize ,
concat ! (
"Alignment of " , stringify ! ( GeckoFontMetrics ) ));
assert_eq! (unsafe {
& ( * ( 0 as * const GeckoFontMetrics ) ) . mChSize as *
const _ as usize } , 0usize , concat ! (
"Alignment of field: " , stringify ! ( GeckoFontMetrics )
, "::" , stringify ! ( mChSize ) ));
assert_eq! (unsafe {
& ( * ( 0 as * const GeckoFontMetrics ) ) . mXSize as *
const _ as usize } , 4usize , concat ! (
"Alignment of field: " , stringify ! ( GeckoFontMetrics )
, "::" , stringify ! ( mXSize ) ));
}
impl Clone for GeckoFontMetrics {
fn clone(&self) -> Self { *self }
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct nsROCSSPrimitiveValue([u8; 0]);
#[repr(C)]

View file

@ -2072,6 +2072,7 @@ pub fn apply_declarations<'a, F, I>(device: &Device,
layout_parent_style: layout_parent_style,
style: starting_style,
font_metrics_provider: font_metrics_provider,
in_media_query: false,
};
// Set computed values, overwriting earlier declarations for the same

View file

@ -190,6 +190,7 @@ impl Range<specified::Length> {
// A real provider will be needed here once we do; since
// ch units can exist in media queries.
font_metrics_provider: &ServoMetricsProvider,
in_media_query: true,
};
match *self {

View file

@ -58,6 +58,9 @@ pub struct Context<'a> {
/// A font metrics provider, used to access font metrics to implement
/// font-relative units.
pub font_metrics_provider: &'a FontMetricsProvider,
/// Whether or not we are computing the media list in a media query
pub in_media_query: bool,
}
impl<'a> Context<'a> {

View file

@ -9,7 +9,7 @@
use app_units::Au;
use cssparser::{Parser, Token};
use euclid::size::Size2D;
use font_metrics::FontMetrics;
use font_metrics::FontMetricsQueryResult;
use parser::{Parse, ParserContext};
use std::{cmp, fmt, mem};
use std::ascii::AsciiExt;
@ -69,22 +69,17 @@ impl ToCss for FontRelativeLength {
}
impl FontRelativeLength {
/// Gets the first available font metrics from the current context's
/// font-family list.
pub fn find_first_available_font_metrics(context: &Context) -> Option<FontMetrics> {
use font_metrics::FontMetricsQueryResult::*;
for family in context.style().get_font().font_family_iter() {
if let Available(metrics) = context.font_metrics_provider.query(family.atom()) {
return metrics;
}
}
None
}
/// Computes the font-relative length. We use the use_inherited flag to
/// special-case the computation of font-size.
pub fn to_computed_value(&self, context: &Context, use_inherited: bool) -> Au {
fn query_font_metrics(context: &Context, reference_font_size: Au) -> FontMetricsQueryResult {
context.font_metrics_provider.query(context.style().get_font(),
reference_font_size,
context.style().writing_mode,
context.in_media_query,
context.device)
}
let reference_font_size = if use_inherited {
context.inherited_style().get_font().clone_font_size()
} else {
@ -95,33 +90,20 @@ impl FontRelativeLength {
match *self {
FontRelativeLength::Em(length) => reference_font_size.scale_by(length),
FontRelativeLength::Ex(length) => {
match Self::find_first_available_font_metrics(context) {
Some(metrics) => metrics.x_height,
match query_font_metrics(context, reference_font_size) {
FontMetricsQueryResult::Available(metrics) => metrics.x_height.scale_by(length),
// https://drafts.csswg.org/css-values/#ex
//
// In the cases where it is impossible or impractical to
// determine the x-height, a value of 0.5em must be
// assumed.
//
None => reference_font_size.scale_by(0.5 * length),
FontMetricsQueryResult::NotAvailable => reference_font_size.scale_by(0.5 * length),
}
},
FontRelativeLength::Ch(length) => {
let wm = context.style().writing_mode;
// TODO(emilio, #14144): Compute this properly once we support
// all the relevant writing-mode related properties, this should
// be equivalent to "is the text in the block direction?".
let vertical = wm.is_vertical();
match Self::find_first_available_font_metrics(context) {
Some(metrics) => {
if vertical {
metrics.zero_advance_measure.height
} else {
metrics.zero_advance_measure.width
}
}
match query_font_metrics(context, reference_font_size) {
FontMetricsQueryResult::Available(metrics) => metrics.zero_advance_measure.scale_by(length),
// https://drafts.csswg.org/css-values/#ch
//
// In the cases where it is impossible or impractical to
@ -132,8 +114,8 @@ impl FontRelativeLength {
// writing-mode is vertical-rl or vertical-lr and
// text-orientation is upright).
//
None => {
if vertical {
FontMetricsQueryResult::NotAvailable => {
if context.style().writing_mode.is_vertical() {
reference_font_size.scale_by(length)
} else {
reference_font_size.scale_by(0.5 * length)

View file

@ -683,6 +683,7 @@ impl MaybeNew for ViewportConstraints {
layout_parent_style: device.default_computed_values(),
style: device.default_computed_values().clone(),
font_metrics_provider: &provider,
in_media_query: false,
};
// DEVICE-ADAPT § 9.3 Resolving 'extend-to-zoom'

View file

@ -1709,6 +1709,7 @@ pub extern "C" fn Servo_GetComputedKeyframeValues(keyframes: RawGeckoKeyframeLis
layout_parent_style: parent_style.unwrap_or(default_values),
style: (**style).clone(),
font_metrics_provider: &metrics,
in_media_query: false,
};
for (index, keyframe) in keyframes.iter().enumerate() {

View file

@ -53,6 +53,7 @@ fn test_linear_gradient() {
layout_parent_style: initial_style,
style: initial_style.clone(),
font_metrics_provider: &ServoMetricsProvider,
in_media_query: false,
};
assert_eq!(specified::AngleOrCorner::None.to_computed_value(&specified_context),
computed::AngleOrCorner::Angle(Angle::from_radians(PI)));