Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stylo: Make font base size computation threadsafe #16316

Merged
merged 3 commits into from Apr 9, 2017
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -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);
}
}
@@ -53,6 +53,7 @@ impl<'a> RecalcStyleAndConstructFlows<'a> {
impl<'a, E> DomTraversal<E> for RecalcStyleAndConstructFlows<'a>
where E: TElement,
E::ConcreteNode: LayoutNode,
E::FontMetricsProvider: Send,
{
type ThreadLocalContext = ScopedThreadLocalLayoutContext<E>;

@@ -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())
}
@@ -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());
}
@@ -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<Animation>
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<ComputedValues>,
/// If `damage` is provided, inserts the appropriate restyle damage.
pub fn update_style_for_animation(context: &SharedStyleContext,
animation: &Animation,
style: &mut Arc<ComputedValues>) {
style: &mut Arc<ComputedValues>,
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();

@@ -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",
@@ -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<E: TElement> {
pub statistics: TraversalStatistics,
/// Information related to the current element, non-None during processing.
pub current_element_info: Option<CurrentElementInfo>,
/// 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<E: TElement> ThreadLocalStyleContext<E> {
@@ -303,6 +307,7 @@ impl<E: TElement> ThreadLocalStyleContext<E> {
tasks: Vec::new(),
statistics: TraversalStatistics::default(),
current_element_info: None,
font_metrics_provider: E::FontMetricsProvider::create_from(shared),
}
}

@@ -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<ConcreteElement = Self>;

/// 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;

@@ -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;
}

@@ -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 {
@@ -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<Vec<(Atom, ::gecko_bindings::structs::FontSizePrefs)>>,
}

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)) }
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.