Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

108 changes: 46 additions & 62 deletions components/fonts/font_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ use std::sync::atomic::{AtomicBool, Ordering};
use app_units::Au;
use content_security_policy::Violation;
use fonts_traits::{
CSSFontFaceDescriptors, FontDescriptor, FontIdentifier, FontTemplate, FontTemplateRef,
FontTemplateRefMethods, StylesheetWebFontLoadFinishedCallback,
CSSFontFaceDescriptors, FontDescriptor, FontFaceRuleWithOrigin, FontIdentifier, FontTemplate,
FontTemplateRef, FontTemplateRefMethods, StylesheetWebFontLoadFinishedCallback,
WebFontSetDifference,
};
use log::{debug, trace};
use malloc_size_of::MallocSizeOf;
Expand Down Expand Up @@ -41,8 +42,8 @@ use style::font_face::{
};
use style::properties::generated::font_face::Descriptors as FontFaceRuleDescriptors;
use style::properties::style_structs::Font as FontStyleStruct;
use style::shared_lock::{Locked, StylesheetGuards};
use style::stylesheets::{FontFaceRule, Origin};
use style::shared_lock::StylesheetGuards;
use style::stylesheets::FontFaceRule;
use style::stylist::Stylist;
use style::values::computed::font::{FamilyName, FontFamilyNameSyntax, SingleFontFamily};
use url::Url;
Expand Down Expand Up @@ -644,7 +645,7 @@ pub trait FontContextWebFontMethods {
guards: &StylesheetGuards<'_>,
callback: StylesheetWebFontLoadFinishedCallback,
document_context: &WebFontDocumentContext,
);
) -> WebFontSetDifference;
fn load_single_font_face_rule(
&self,
font_face_rule: &FontFaceRule,
Expand Down Expand Up @@ -697,38 +698,38 @@ impl FontContextWebFontMethods for Arc<FontContext> {
guards: &StylesheetGuards<'_>,
callback: StylesheetWebFontLoadFinishedCallback,
document_context: &WebFontDocumentContext,
) {
let mut removed_any = false;

self.known_font_face_rules
) -> WebFontSetDifference {
let difference = self
.known_font_face_rules
.lock()
.diff_old_and_new_font_face_rules(
stylist,
guards,
|new_rule| {
self.load_single_font_face_rule(
new_rule,
webview_id,
callback.clone(),
document_context,
);
},
|stale_rule| {
self.remove_single_font_face_rule(
&stale_rule.descriptors,
&mut self.web_fonts.write(),
);
removed_any = true;
},
.diff_old_and_new_font_face_rules(stylist, guards);

for added_rule in &difference.added_font_faces {
let added_rule = added_rule.read_with(guards);
self.load_single_font_face_rule(
added_rule,
webview_id,
callback.clone(),
document_context,
);
}
for removed_rule in &difference.removed_font_faces {
let removed_rule = removed_rule.read_with(guards);
self.remove_single_font_face_rule(
&removed_rule.descriptors,
&mut self.web_fonts.write(),
);
}

if removed_any {
if !difference.removed_font_faces.is_empty() {
// We modified the list of available fonts, so invalidate resolved font groups.
self.resolved_font_groups.write().clear();

// Ensure that we clean up any WebRender resources on the next display list update.
self.have_removed_web_fonts.store(true, Ordering::Relaxed);
}

difference
}

fn load_web_font_for_script(
Expand Down Expand Up @@ -1237,47 +1238,24 @@ struct KnownFontFaceRule {
generation: bool,
}

#[derive(MallocSizeOf)]
struct FontFaceRuleWithOrigin {
#[conditional_malloc_size_of]
rule: ServoArc<Locked<FontFaceRule>>,
origin: Origin,
}

impl FontFaceRuleWithOrigin {
fn read_with<'a>(&'a self, guards: &'a StylesheetGuards) -> &'a FontFaceRule {
match self.origin {
Origin::Author => self.rule.read_with(guards.author),
Origin::UserAgent | Origin::User => self.rule.read_with(guards.ua_or_user),
}
}
}

impl KnownFontFaceRules {
/// Computes the difference between the `@font-face `rules that are currently in effect
/// and the ones that the `Stylist` knows about. The caller is notified about new or removed rules
/// with callbacks.
fn diff_old_and_new_font_face_rules<NewRuleCallback, StaleRuleCallback>(
fn diff_old_and_new_font_face_rules(
&mut self,
stylist: &Stylist,
guards: &StylesheetGuards<'_>,
mut new_rule_callback: NewRuleCallback,
mut stale_rule_callback: StaleRuleCallback,
) where
NewRuleCallback: FnMut(&FontFaceRule),
StaleRuleCallback: FnMut(&FontFaceRule),
{
) -> WebFontSetDifference {
let mut difference = WebFontSetDifference::default();
self.generation = !self.generation;

let font_face_rules_in_cascade_order = stylist
.iter_extra_data_origins()
.flat_map(|(extra_data, origin)| {
extra_data.font_faces.iter().rev().zip(iter::repeat(origin))
})
.map(|((rule, _layer), origin)| FontFaceRuleWithOrigin {
rule: rule.clone(),
origin,
});
.map(|((rule, _layer), origin)| FontFaceRuleWithOrigin::new(rule.clone(), origin));

// First, find any *new* font families that were not defined previously
let mut number_of_unchanged_rules = 0;
Expand Down Expand Up @@ -1308,9 +1286,9 @@ impl KnownFontFaceRules {
let mut index_of_existing_entry_for_this_rule = None;
for (index, known_font_face) in known_font_faces_for_family.iter().enumerate() {
// See if this is a entry for this @font-face that existed prior to the current update
if ServoArc::ptr_eq(
&known_font_face.rule_with_origin.rule,
&rule_with_origin.rule,
if FontFaceRuleWithOrigin::ptr_eq(
&known_font_face.rule_with_origin,
&rule_with_origin,
) {
index_of_existing_entry_for_this_rule = Some(index);
}
Expand Down Expand Up @@ -1345,7 +1323,9 @@ impl KnownFontFaceRules {
if conflicting_declaration_with_higher_priority_exists {
let stale_rule =
known_font_faces_for_family.remove(index_of_existing_entry_for_this_rule);
stale_rule_callback(stale_rule.rule_with_origin.read_with(guards));
difference
.removed_font_faces
.push(stale_rule.rule_with_origin);
} else {
number_of_unchanged_rules += 1;
known_font_faces_for_family[index_of_existing_entry_for_this_rule].generation =
Expand All @@ -1357,7 +1337,7 @@ impl KnownFontFaceRules {
continue;
} else {
// This is a new rule that does not conflict with anything that previously existed, so insert it.
new_rule_callback(borrowed_rule);
difference.added_font_faces.push(rule_with_origin.clone());
known_font_faces_for_family.push(KnownFontFaceRule {
rule_with_origin,
generation: self.generation,
Expand All @@ -1369,19 +1349,23 @@ impl KnownFontFaceRules {
// This is the common case, where the new set of known @font-face rules is a superset of
// the old one after applying the cascade. In this case there is nothing more to do,
// because all old @font-face rules are still present.
return;
return difference;
}

// Remove all `@font-face` rules that were not updated - those no longer exist on the stylist.
self.contents.retain(|_, known_font_faces_for_family| {
known_font_faces_for_family
.extract_if(.., |rule| rule.generation != self.generation)
.for_each(|removed_rule| {
stale_rule_callback(removed_rule.rule_with_origin.read_with(guards))
difference
.removed_font_faces
.push(removed_rule.rule_with_origin);
});

!known_font_faces_for_family.is_empty()
});

difference
}
}

Expand Down
66 changes: 45 additions & 21 deletions components/layout/layout_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use embedder_traits::{
};
use euclid::{Point2D, Rect, Scale, Size2D};
use fonts::{FontContext, FontContextWebFontMethods};
use fonts_traits::StylesheetWebFontLoadFinishedCallback;
use fonts_traits::{StylesheetWebFontLoadFinishedCallback, WebFontSetDifference};
use icu_locid::subtags::Language;
use layout_api::{
AxesOverflow, BoxAreaType, CSSPixelRectVec, DangerousStyleNode, IFrameSizes, Layout,
Expand Down Expand Up @@ -1003,12 +1003,8 @@ impl LayoutThread {
});
let mut reflow_statistics = Default::default();

let (mut reflow_phases_run, iframe_sizes) = self.restyle_and_build_trees(
&mut reflow_request,
document,
root_element,
&image_resolver,
);
let (mut reflow_phases_run, iframe_sizes, changed_web_fonts) = self
.restyle_and_build_trees(&mut reflow_request, document, root_element, &image_resolver);
if self.build_stacking_context_tree_for_reflow(&reflow_request) {
reflow_phases_run.insert(ReflowPhasesRun::BuiltStackingContextTree);
}
Expand Down Expand Up @@ -1042,6 +1038,7 @@ impl LayoutThread {
pending_svg_elements_for_serialization,
iframe_sizes: Some(iframe_sizes),
reflow_statistics,
changed_web_fonts,
})
}

Expand All @@ -1052,7 +1049,7 @@ impl LayoutThread {
document: ServoDangerousStyleDocument<'dom>,
guards: &StylesheetGuards,
ua_stylesheets: &UserAgentStylesheets,
) -> StylesheetInvalidationSet {
) -> StylistStylesheetUpdate {
let need_user_agent_stylesheet_addition = !self.have_added_user_agent_stylesheets;
if need_user_agent_stylesheet_addition {
for stylesheet in &ua_stylesheets.user_agent_stylesheets {
Expand Down Expand Up @@ -1092,17 +1089,23 @@ impl LayoutThread {

// Load new @font-face rules and remove old ones if necessary.
// TODO: Can we make the invalidation set tell us whether any @font-face rules changed?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this TODO still relevant? I am not intimately familiar with this code, but I think it does now.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its still relevant. Ideally stylo could tell us whether a stylesheet that was added to the page and then we might not have to call rebuild_font_face_set at all.

if need_user_agent_stylesheet_addition || reflow_request.stylesheets_changed() {
self.font_context.rebuild_font_face_set(
self.webview_id,
&self.stylist,
guards,
self.web_font_finished_loading_callback.clone(),
&reflow_request.document_context,
);
}
let changed_web_fonts =
if need_user_agent_stylesheet_addition || reflow_request.stylesheets_changed() {
self.font_context.rebuild_font_face_set(
self.webview_id,
&self.stylist,
guards,
self.web_font_finished_loading_callback.clone(),
&reflow_request.document_context,
)
} else {
WebFontSetDifference::default()
};

invalidation_set
StylistStylesheetUpdate {
invalidation_set,
changed_web_fonts,
}
}

#[servo_tracing::instrument(skip_all)]
Expand All @@ -1112,7 +1115,7 @@ impl LayoutThread {
document: ServoDangerousStyleDocument<'_>,
root_element: ServoLayoutElement<'_>,
image_resolver: &Arc<ImageResolver>,
) -> (ReflowPhasesRun, IFrameSizes) {
) -> (ReflowPhasesRun, IFrameSizes, WebFontSetDifference) {
let mut snapshot_map = SnapshotMap::new();
let _snapshot_setter = match reflow_request.restyle.as_mut() {
Some(restyle) => SnapshotSetter::new(restyle, &mut snapshot_map),
Expand Down Expand Up @@ -1144,7 +1147,14 @@ impl LayoutThread {
}
}

self.prepare_stylist_for_reflow(reflow_request, document, &guards, &user_agent_stylesheets)
let stylist_update = self.prepare_stylist_for_reflow(
reflow_request,
document,
&guards,
&user_agent_stylesheets,
);
stylist_update
.invalidation_set
.process_style(dangerous_root_element, Some(&snapshot_map));

if self.previously_highlighted_dom_node.get() != reflow_request.highlighted_dom_node {
Expand Down Expand Up @@ -1256,7 +1266,11 @@ impl LayoutThread {

if !damage.contains(LayoutDamage::DescendantCollectedAsLayoutRoot) {
layout_context.style_context.stylist.rule_tree().maybe_gc();
return (ReflowPhasesRun::empty(), IFrameSizes::default());
return (
ReflowPhasesRun::empty(),
IFrameSizes::default(),
stylist_update.changed_web_fonts,
);
}

debug_assert!(!layout_roots.is_empty());
Expand All @@ -1267,6 +1281,7 @@ impl LayoutThread {
return (
ReflowPhasesRun::RanLayout,
std::mem::take(&mut *layout_context.iframe_sizes.lock()),
stylist_update.changed_web_fonts,
);
}

Expand Down Expand Up @@ -1317,6 +1332,7 @@ impl LayoutThread {
(
ReflowPhasesRun::RanLayout,
std::mem::take(&mut *iframe_sizes),
stylist_update.changed_web_fonts,
)
}

Expand Down Expand Up @@ -1891,3 +1907,11 @@ impl ReflowPhases {
}
}
}

/// Summarizes changes after flushing stylesheets on the `Stylist`.
struct StylistStylesheetUpdate {
/// Information about what kind of selectors changed.
invalidation_set: StylesheetInvalidationSet,
/// A list of changes to the set of web fonts.
changed_web_fonts: WebFontSetDifference,
}
Loading
Loading