aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMukilan Thiyagarajan <mukilan@igalia.com>2025-02-19 11:20:01 +0530
committerGitHub <noreply@github.com>2025-02-19 05:50:01 +0000
commit56840e0a3509f017f745a002332c6125431260a6 (patch)
treea9401b39fa1717127d5e99e1c6a57ad2f102b737
parent29e0fad21ec561b1778e8d973c4e800702f1b38b (diff)
downloadservo-56840e0a3509f017f745a002332c6125431260a6.tar.gz
servo-56840e0a3509f017f745a002332c6125431260a6.zip
script: add skeleton implementation of `FontFace` API (#35262)
This patch implements the `FontFace` interface, but with some caveats 1. The interface is only exposed on `Window`. Support for Workers will be handled in the future. 2. The concept of `css-connected` `FontFace` is not implemented, so `@font-face` rules in stylesheets will not be represented in the DOM. 3. The constructor only supports using `url()` strings as source and `ArrayBuffer` and `ArrayBufferView` are not supported yet. A skeleton implementation of the `load` method of `FontFaceSet` is also implemented in this patch. The intention is to support some web pages that don't load without this method. Signed-off-by: Mukilan Thiyagarajan <mukilan@igalia.com>
-rw-r--r--components/config/prefs.rs2
-rw-r--r--components/fonts/font_context.rs451
-rw-r--r--components/fonts/font_store.rs62
-rw-r--r--components/fonts/font_template.rs4
-rw-r--r--components/fonts/system_font_service.rs2
-rw-r--r--components/layout_thread/lib.rs4
-rw-r--r--components/layout_thread_2020/lib.rs4
-rw-r--r--components/malloc_size_of/lib.rs1
-rw-r--r--components/profile/time.rs1
-rw-r--r--components/script/dom/eventsource.rs2
-rw-r--r--components/script/dom/fontface.rs577
-rw-r--r--components/script/dom/fontfaceset.rs69
-rw-r--r--components/script/dom/mod.rs1
-rw-r--r--components/script/dom/window.rs4
-rw-r--r--components/script/network_listener.rs6
-rw-r--r--components/script/script_runtime.rs3
-rw-r--r--components/script/script_thread.rs3
-rw-r--r--components/script/task_manager.rs1
-rw-r--r--components/script/task_source.rs4
-rw-r--r--components/script_bindings/codegen/Bindings.conf8
-rw-r--r--components/script_bindings/webidls/FontFace.webidl54
-rw-r--r--components/script_bindings/webidls/FontFaceSet.webidl8
-rw-r--r--components/shared/background_hang_monitor/lib.rs1
-rw-r--r--components/shared/fonts/lib.rs2
-rw-r--r--components/shared/net/lib.rs4
-rw-r--r--components/shared/profile/time.rs60
-rw-r--r--tests/wpt/meta/__dir__.ini2
-rw-r--r--tests/wpt/meta/css/css-cascade/layer-cssom-order-reverse.html.ini3
-rw-r--r--tests/wpt/meta/css/css-cascade/layer-font-face-override.html.ini6
-rw-r--r--tests/wpt/meta/css/css-fonts/fallback-remote-to-data-url.html.ini3
-rw-r--r--tests/wpt/meta/css/css-fonts/font-display/font-display-change.html.ini3
-rw-r--r--tests/wpt/meta/css/css-fonts/format-specifiers-variations.html.ini132
-rw-r--r--tests/wpt/meta/css/css-fonts/parsing/font-size-adjust-computed.html.ini17
-rw-r--r--tests/wpt/meta/css/css-fonts/variations/font-parse-numeric-stretch-style-weight.html.ini156
-rw-r--r--tests/wpt/meta/css/css-masking/clip-path/clip-path-svg-text-font-loading.html.ini2
-rw-r--r--tests/wpt/meta/css/css-values/ch-empty-pseudo-recalc-on-font-load.html.ini1
-rw-r--r--tests/wpt/meta/css/css-values/ch-pseudo-recalc-on-font-load.html.ini1
-rw-r--r--tests/wpt/meta/css/css-values/ch-recalc-on-font-load.html.ini10
-rw-r--r--tests/wpt/meta/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.html.ini4
-rw-r--r--tests/wpt/meta/html/canvas/offscreen/text/2d.text.measure.width.empty.html.ini3
-rw-r--r--tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.condensed.html.ini2
-rw-r--r--tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.expanded.html.ini2
-rw-r--r--tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.extra-condensed.html.ini2
-rw-r--r--tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.extra-expanded.html.ini2
-rw-r--r--tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.normal.html.ini2
-rw-r--r--tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.semi-condensed.html.ini2
-rw-r--r--tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.semi-expanded.html.ini2
-rw-r--r--tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.ultra-condensed.html.ini2
-rw-r--r--tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.ultra-expanded.html.ini2
-rw-r--r--tests/wpt/meta/webidl/current-realm.html.ini3
-rw-r--r--tests/wpt/mozilla/meta/MANIFEST.json2
-rw-r--r--tests/wpt/mozilla/tests/mozilla/interfaces.worker.js1
52 files changed, 1157 insertions, 548 deletions
diff --git a/components/config/prefs.rs b/components/config/prefs.rs
index a6dffb72b43..8a3c8595f1d 100644
--- a/components/config/prefs.rs
+++ b/components/config/prefs.rs
@@ -81,6 +81,7 @@ pub struct Preferences {
pub dom_customelements_enabled: bool,
pub dom_document_dblclick_timeout: i64,
pub dom_document_dblclick_dist: i64,
+ pub dom_fontface_enabled: bool,
pub dom_forcetouch_enabled: bool,
pub dom_fullscreen_test: bool,
pub dom_gamepad_enabled: bool,
@@ -245,6 +246,7 @@ impl Preferences {
dom_customelements_enabled: true,
dom_document_dblclick_dist: 1,
dom_document_dblclick_timeout: 300,
+ dom_fontface_enabled: false,
dom_forcetouch_enabled: false,
dom_fullscreen_test: false,
dom_gamepad_enabled: true,
diff --git a/components/fonts/font_context.rs b/components/fonts/font_context.rs
index 212fb587f84..df8c7785658 100644
--- a/components/fonts/font_context.rs
+++ b/components/fonts/font_context.rs
@@ -11,7 +11,7 @@ use std::sync::Arc;
use app_units::Au;
use base::id::WebViewId;
use fnv::FnvHasher;
-use fonts_traits::WebFontLoadFinishedCallback;
+use fonts_traits::StylesheetWebFontLoadFinishedCallback;
use log::{debug, trace};
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
use malloc_size_of_derive::MallocSizeOf;
@@ -21,7 +21,9 @@ use parking_lot::{Mutex, RwLock};
use servo_arc::Arc as ServoArc;
use servo_url::ServoUrl;
use style::computed_values::font_variant_caps::T as FontVariantCaps;
-use style::font_face::{FontFaceSourceFormat, FontFaceSourceFormatKeyword, Source, UrlSource};
+use style::font_face::{
+ FontFaceSourceFormat, FontFaceSourceFormatKeyword, Source, SourceList, UrlSource,
+};
use style::media_queries::Device;
use style::properties::style_structs::Font as FontStyleStruct;
use style::shared_lock::SharedRwLockReadGuard;
@@ -133,18 +135,6 @@ impl FontContext {
}
}
- /// Handle the situation where a web font finishes loading, specifying if the load suceeded or failed.
- fn handle_web_font_load_finished(
- &self,
- finished_callback: &WebFontLoadFinishedCallback,
- succeeded: bool,
- ) {
- if succeeded {
- self.invalidate_font_groups_after_web_font_load();
- }
- finished_callback(succeeded);
- }
-
/// Returns a `FontGroup` representing fonts which can be used for layout, given the `style`.
/// Font groups are cached, so subsequent calls with the same `style` will return a reference
/// to an existing `FontGroup`.
@@ -355,17 +345,138 @@ impl FontContext {
fn invalidate_font_groups_after_web_font_load(&self) {
self.resolved_font_groups.write().clear();
}
+
+ pub fn is_supported_web_font_source(source: &&Source) -> bool {
+ let url_source = match source {
+ Source::Url(ref url_source) => url_source,
+ Source::Local(_) => return true,
+ };
+ let format_hint = match url_source.format_hint {
+ Some(ref format_hint) => format_hint,
+ None => return true,
+ };
+
+ if matches!(
+ format_hint,
+ FontFaceSourceFormat::Keyword(
+ FontFaceSourceFormatKeyword::Truetype |
+ FontFaceSourceFormatKeyword::Opentype |
+ FontFaceSourceFormatKeyword::Woff |
+ FontFaceSourceFormatKeyword::Woff2
+ )
+ ) {
+ return true;
+ }
+
+ if let FontFaceSourceFormat::String(string) = format_hint {
+ return string == "truetype" ||
+ string == "opentype" ||
+ string == "woff" ||
+ string == "woff2";
+ }
+
+ false
+ }
}
-#[derive(Clone)]
pub(crate) struct WebFontDownloadState {
- webview_id: WebViewId,
- pub(crate) css_font_face_descriptors: Arc<CSSFontFaceDescriptors>,
+ webview_id: Option<WebViewId>,
+ css_font_face_descriptors: CSSFontFaceDescriptors,
remaining_sources: Vec<Source>,
- finished_callback: WebFontLoadFinishedCallback,
core_resource_thread: CoreResourceThread,
- local_fonts: Arc<HashMap<Atom, Option<FontTemplateRef>>>,
- pub(crate) stylesheet: DocumentStyleSheet,
+ local_fonts: HashMap<Atom, Option<FontTemplateRef>>,
+ font_context: Arc<FontContext>,
+ initiator: WebFontLoadInitiator,
+}
+
+impl WebFontDownloadState {
+ fn new(
+ webview_id: Option<WebViewId>,
+ font_context: Arc<FontContext>,
+ css_font_face_descriptors: CSSFontFaceDescriptors,
+ initiator: WebFontLoadInitiator,
+ sources: Vec<Source>,
+ local_fonts: HashMap<Atom, Option<FontTemplateRef>>,
+ ) -> WebFontDownloadState {
+ match initiator {
+ WebFontLoadInitiator::Stylesheet(ref stylesheet, _) => {
+ font_context
+ .web_fonts
+ .write()
+ .handle_web_font_load_started_for_stylesheet(stylesheet);
+ },
+ WebFontLoadInitiator::Script(_) => {
+ font_context
+ .web_fonts
+ .write()
+ .handle_web_font_load_started_for_script();
+ },
+ };
+ let core_resource_thread = font_context.resource_threads.lock().clone();
+ WebFontDownloadState {
+ webview_id,
+ css_font_face_descriptors,
+ remaining_sources: sources,
+ core_resource_thread,
+ local_fonts,
+ font_context,
+ initiator,
+ }
+ }
+
+ fn handle_web_font_load_success(self, new_template: FontTemplate) {
+ let family_name = self.css_font_face_descriptors.family_name.clone();
+ match self.initiator {
+ WebFontLoadInitiator::Stylesheet(ref stylesheet, ref callback) => {
+ let not_cancelled = self
+ .font_context
+ .web_fonts
+ .write()
+ .handle_web_font_loaded_for_stylesheet(stylesheet, family_name, new_template);
+ self.font_context
+ .invalidate_font_groups_after_web_font_load();
+ callback(not_cancelled);
+ },
+ WebFontLoadInitiator::Script(callback) => {
+ self.font_context
+ .web_fonts
+ .write()
+ .handle_web_font_load_finished_for_script();
+ callback(family_name, Some(new_template));
+ },
+ }
+ }
+
+ fn handle_web_font_load_failure(self) {
+ let family_name = self.css_font_face_descriptors.family_name.clone();
+ match self.initiator {
+ WebFontLoadInitiator::Stylesheet(ref stylesheet, ref callback) => {
+ self.font_context
+ .web_fonts
+ .write()
+ .handle_web_font_load_failed_for_stylesheet(stylesheet);
+ callback(false);
+ },
+ WebFontLoadInitiator::Script(callback) => {
+ self.font_context
+ .web_fonts
+ .write()
+ .handle_web_font_load_finished_for_script();
+ callback(family_name, None);
+ },
+ }
+ }
+
+ fn font_load_cancelled(&self) -> bool {
+ match self.initiator {
+ WebFontLoadInitiator::Stylesheet(ref stylesheet, _) => self
+ .font_context
+ .web_fonts
+ .read()
+ .font_load_cancelled_for_stylesheet(stylesheet),
+ WebFontLoadInitiator::Script(_) => false,
+ }
+ }
}
pub trait FontContextWebFontMethods {
@@ -375,8 +486,20 @@ pub trait FontContextWebFontMethods {
stylesheet: &DocumentStyleSheet,
guard: &SharedRwLockReadGuard,
device: &Device,
- finished_callback: WebFontLoadFinishedCallback,
+ finished_callback: StylesheetWebFontLoadFinishedCallback,
) -> usize;
+ fn load_web_font_for_script(
+ &self,
+ webview_id: Option<WebViewId>,
+ source_list: SourceList,
+ descriptors: CSSFontFaceDescriptors,
+ finished_callback: ScriptWebFontLoadFinishedCallback,
+ );
+ fn add_template_to_font_context(
+ &self,
+ family_name: LowercaseFontFamilyName,
+ font_template: FontTemplate,
+ );
fn remove_all_web_fonts_from_stylesheet(&self, stylesheet: &DocumentStyleSheet);
fn collect_unused_webrender_resources(&self, all: bool)
-> (Vec<FontKey>, Vec<FontInstanceKey>);
@@ -389,7 +512,7 @@ impl FontContextWebFontMethods for Arc<FontContext> {
stylesheet: &DocumentStyleSheet,
guard: &SharedRwLockReadGuard,
device: &Device,
- finished_callback: WebFontLoadFinishedCallback,
+ finished_callback: StylesheetWebFontLoadFinishedCallback,
) -> usize {
let mut number_loading = 0;
for rule in stylesheet.effective_rules(device, guard) {
@@ -402,57 +525,17 @@ impl FontContextWebFontMethods for Arc<FontContext> {
continue;
};
- let sources: Vec<Source> = font_face
- .sources()
- .0
- .iter()
- .rev()
- .filter(is_supported_web_font_source)
- .cloned()
- .collect();
- if sources.is_empty() {
- continue;
- }
-
- // Fetch all local fonts first, beacause if we try to fetch them later on during the process of
- // loading the list of web font `src`s we may be running in the context of the router thread, which
- // means we won't be able to seend IPC messages to the FontCacheThread.
- //
- // TODO: This is completely wrong. The specification says that `local()` font-family should match
- // against full PostScript names, but this is matching against font family names. This works...
- // sometimes.
- let mut local_fonts = HashMap::new();
- for source in sources.iter() {
- if let Source::Local(family_name) = source {
- local_fonts
- .entry(family_name.name.clone())
- .or_insert_with(|| {
- let family = SingleFontFamily::FamilyName(FamilyName {
- name: family_name.name.clone(),
- syntax: FontFamilyNameSyntax::Quoted,
- });
- self.system_font_service_proxy
- .find_matching_font_templates(None, &family)
- .first()
- .cloned()
- });
- }
- }
+ let css_font_face_descriptors = rule.into();
+ let completion_handler =
+ WebFontLoadInitiator::Stylesheet(stylesheet.clone(), finished_callback.clone());
number_loading += 1;
- self.web_fonts
- .write()
- .handle_web_font_load_started_for_stylesheet(stylesheet);
-
- self.process_next_web_font_source(WebFontDownloadState {
- webview_id,
- css_font_face_descriptors: Arc::new(rule.into()),
- remaining_sources: sources,
- finished_callback: finished_callback.clone(),
- core_resource_thread: self.resource_threads.lock().clone(),
- local_fonts: Arc::new(local_fonts),
- stylesheet: stylesheet.clone(),
- });
+ self.start_loading_one_web_font(
+ Some(webview_id),
+ font_face.sources(),
+ css_font_face_descriptors,
+ completion_handler,
+ );
}
number_loading
@@ -553,15 +636,84 @@ impl FontContextWebFontMethods for Arc<FontContext> {
removed_instance_keys.into_iter().collect(),
)
}
+
+ fn load_web_font_for_script(
+ &self,
+ webview_id: Option<WebViewId>,
+ sources: SourceList,
+ descriptors: CSSFontFaceDescriptors,
+ finished_callback: ScriptWebFontLoadFinishedCallback,
+ ) {
+ let completion_handler = WebFontLoadInitiator::Script(finished_callback);
+ self.start_loading_one_web_font(webview_id, &sources, descriptors, completion_handler);
+ }
+
+ fn add_template_to_font_context(
+ &self,
+ family_name: LowercaseFontFamilyName,
+ new_template: FontTemplate,
+ ) {
+ self.web_fonts
+ .write()
+ .add_new_template(family_name, new_template);
+ self.invalidate_font_groups_after_web_font_load();
+ }
}
impl FontContext {
+ fn start_loading_one_web_font(
+ self: &Arc<FontContext>,
+ webview_id: Option<WebViewId>,
+ source_list: &SourceList,
+ css_font_face_descriptors: CSSFontFaceDescriptors,
+ completion_handler: WebFontLoadInitiator,
+ ) {
+ let sources: Vec<Source> = source_list
+ .0
+ .iter()
+ .rev()
+ .filter(Self::is_supported_web_font_source)
+ .cloned()
+ .collect();
+
+ // Fetch all local fonts first, beacause if we try to fetch them later on during the process of
+ // loading the list of web font `src`s we may be running in the context of the router thread, which
+ // means we won't be able to seend IPC messages to the FontCacheThread.
+ //
+ // TODO: This is completely wrong. The specification says that `local()` font-family should match
+ // against full PostScript names, but this is matching against font family names. This works...
+ // sometimes.
+ let mut local_fonts = HashMap::new();
+ for source in sources.iter() {
+ if let Source::Local(family_name) = source {
+ local_fonts
+ .entry(family_name.name.clone())
+ .or_insert_with(|| {
+ let family = SingleFontFamily::FamilyName(FamilyName {
+ name: family_name.name.clone(),
+ syntax: FontFamilyNameSyntax::Quoted,
+ });
+ self.system_font_service_proxy
+ .find_matching_font_templates(None, &family)
+ .first()
+ .cloned()
+ });
+ }
+ }
+
+ self.process_next_web_font_source(WebFontDownloadState::new(
+ webview_id,
+ self.clone(),
+ css_font_face_descriptors,
+ completion_handler,
+ sources,
+ local_fonts,
+ ));
+ }
+
fn process_next_web_font_source(self: &Arc<FontContext>, mut state: WebFontDownloadState) {
let Some(source) = state.remaining_sources.pop() else {
- self.web_fonts
- .write()
- .handle_web_font_failed_to_load(&state);
- self.handle_web_font_load_finished(&state.finished_callback, false);
+ state.handle_web_font_load_failure();
return;
};
@@ -581,17 +733,13 @@ impl FontContext {
let template = FontTemplate::new_for_local_web_font(
local_template.clone(),
&state.css_font_face_descriptors,
- state.stylesheet.clone(),
+ state.initiator.stylesheet().cloned(),
)
.ok()?;
Some(template)
})
{
- let not_cancelled = self
- .web_fonts
- .write()
- .handle_web_font_loaded(&state, new_template);
- self.handle_web_font_load_finished(&state.finished_callback, not_cancelled);
+ state.handle_web_font_load_success(new_template);
} else {
this.process_next_web_font_source(state);
}
@@ -600,12 +748,29 @@ impl FontContext {
}
}
+pub type ScriptWebFontLoadFinishedCallback =
+ Box<dyn FnOnce(LowercaseFontFamilyName, Option<FontTemplate>) + Send>;
+
+pub(crate) enum WebFontLoadInitiator {
+ Stylesheet(DocumentStyleSheet, StylesheetWebFontLoadFinishedCallback),
+ Script(ScriptWebFontLoadFinishedCallback),
+}
+
+impl WebFontLoadInitiator {
+ pub(crate) fn stylesheet(&self) -> Option<&DocumentStyleSheet> {
+ match self {
+ Self::Stylesheet(stylesheet, _) => Some(stylesheet),
+ Self::Script(_) => None,
+ }
+ }
+}
+
struct RemoteWebFontDownloader {
- font_context: Arc<FontContext>,
+ state: Option<WebFontDownloadState>,
url: ServoArc<Url>,
web_font_family_name: LowercaseFontFamilyName,
- response_valid: Mutex<bool>,
- response_data: Mutex<Vec<u8>>,
+ response_valid: bool,
+ response_data: Vec<u8>,
}
enum DownloaderResponseResult {
@@ -628,23 +793,21 @@ impl RemoteWebFontDownloader {
};
// FIXME: This shouldn't use NoReferrer, but the current documents url
- let request = RequestBuilder::new(
- Some(state.webview_id),
- url.clone().into(),
- Referrer::NoReferrer,
- )
- .destination(Destination::Font);
+ let request =
+ RequestBuilder::new(state.webview_id, url.clone().into(), Referrer::NoReferrer)
+ .destination(Destination::Font);
+
+ let core_resource_thread_clone = state.core_resource_thread.clone();
debug!("Loading @font-face {} from {}", web_font_family_name, url);
- let downloader = Self {
- font_context,
+ let mut downloader = Self {
url,
web_font_family_name,
- response_valid: Mutex::new(false),
- response_data: Mutex::default(),
+ response_valid: false,
+ response_data: Vec::new(),
+ state: Some(state),
};
- let core_resource_thread_clone = state.core_resource_thread.clone();
fetch_async(
&core_resource_thread_clone,
request,
@@ -653,40 +816,42 @@ impl RemoteWebFontDownloader {
match downloader.handle_web_font_fetch_message(response_message) {
DownloaderResponseResult::InProcess => {},
DownloaderResponseResult::Finished => {
- if !downloader.process_downloaded_font_and_signal_completion(&state) {
- downloader
- .font_context
- .process_next_web_font_source(state.clone())
+ if !downloader.process_downloaded_font_and_signal_completion() {
+ font_context.process_next_web_font_source(downloader.take_state())
}
},
- DownloaderResponseResult::Failure => downloader
- .font_context
- .process_next_web_font_source(state.clone()),
+ DownloaderResponseResult::Failure => {
+ font_context.process_next_web_font_source(downloader.take_state())
+ },
}
}),
)
}
+ fn take_state(&mut self) -> WebFontDownloadState {
+ self.state
+ .take()
+ .expect("must be non-None until download either succeeds or fails")
+ }
+
/// After a download finishes, try to process the downloaded data, returning true if
/// the font is added successfully to the [`FontContext`] or false if it isn't.
- fn process_downloaded_font_and_signal_completion(&self, state: &WebFontDownloadState) -> bool {
- if self
- .font_context
- .web_fonts
- .read()
- .font_load_cancelled_for_stylesheet(&state.stylesheet)
- {
- self.font_context
- .handle_web_font_load_finished(&state.finished_callback, false);
+ fn process_downloaded_font_and_signal_completion(&mut self) -> bool {
+ let state = self
+ .state
+ .as_ref()
+ .expect("must be non-None until processing is completed");
+ if state.font_load_cancelled() {
+ self.take_state().handle_web_font_load_failure();
// Returning true here prevents trying to load the next font on the source list.
return true;
}
- let font_data = std::mem::take(&mut *self.response_data.lock());
+ let font_data = std::mem::take(&mut self.response_data);
trace!(
- "@font-face {} data={:?}",
+ "Downloaded @font-face {} ({} bytes)",
self.web_font_family_name,
- font_data
+ font_data.len()
);
let font_data = match fontsan::process(&font_data) {
@@ -705,6 +870,8 @@ impl RemoteWebFontDownloader {
let Ok(handle) = PlatformFont::new_from_data(identifier, &font_data, None) else {
return false;
};
+
+ let state = self.take_state();
let mut descriptor = handle.descriptor();
descriptor
.override_values_with_css_font_template_descriptors(&state.css_font_face_descriptors);
@@ -712,20 +879,16 @@ impl RemoteWebFontDownloader {
let new_template = FontTemplate::new(
FontIdentifier::Web(url),
descriptor,
- Some(state.stylesheet.clone()),
+ state.initiator.stylesheet().cloned(),
);
- self.font_context
+
+ state
+ .font_context
.font_data
.write()
.insert(new_template.identifier.clone(), font_data);
- let not_cancelled = self
- .font_context
- .web_fonts
- .write()
- .handle_web_font_loaded(state, new_template);
- self.font_context
- .handle_web_font_load_finished(&state.finished_callback, not_cancelled);
+ state.handle_web_font_load_success(new_template);
// If the load was canceled above, then we still want to return true from this function in
// order to halt any attempt to load sources that come later on the source list.
@@ -733,7 +896,7 @@ impl RemoteWebFontDownloader {
}
fn handle_web_font_fetch_message(
- &self,
+ &mut self,
response_message: FetchResponseMsg,
) -> DownloaderResponseResult {
match response_message {
@@ -746,7 +909,7 @@ impl RemoteWebFontDownloader {
self.web_font_family_name,
meta_result.is_ok()
);
- *self.response_valid.lock() = meta_result.is_ok();
+ self.response_valid = meta_result.is_ok();
DownloaderResponseResult::InProcess
},
FetchResponseMsg::ProcessResponseChunk(_, new_bytes) => {
@@ -755,8 +918,8 @@ impl RemoteWebFontDownloader {
self.web_font_family_name,
new_bytes
);
- if *self.response_valid.lock() {
- self.response_data.lock().extend(new_bytes)
+ if self.response_valid {
+ self.response_data.extend(new_bytes)
}
DownloaderResponseResult::InProcess
},
@@ -766,7 +929,7 @@ impl RemoteWebFontDownloader {
self.web_font_family_name,
response
);
- if response.is_err() || !*self.response_valid.lock() {
+ if response.is_err() || !self.response_valid {
return DownloaderResponseResult::Failure;
}
DownloaderResponseResult::Finished
@@ -804,35 +967,3 @@ impl Hash for FontGroupCacheKey {
self.style.hash.hash(hasher)
}
}
-
-fn is_supported_web_font_source(source: &&Source) -> bool {
- let url_source = match source {
- Source::Url(ref url_source) => url_source,
- Source::Local(_) => return true,
- };
- let format_hint = match url_source.format_hint {
- Some(ref format_hint) => format_hint,
- None => return true,
- };
-
- if matches!(
- format_hint,
- FontFaceSourceFormat::Keyword(
- FontFaceSourceFormatKeyword::Truetype |
- FontFaceSourceFormatKeyword::Opentype |
- FontFaceSourceFormatKeyword::Woff |
- FontFaceSourceFormatKeyword::Woff2
- )
- ) {
- return true;
- }
-
- if let FontFaceSourceFormat::String(string) = format_hint {
- return string == "truetype" ||
- string == "opentype" ||
- string == "woff" ||
- string == "woff2";
- }
-
- false
-}
diff --git a/components/fonts/font_store.rs b/components/fonts/font_store.rs
index 5791319bb14..826be947672 100644
--- a/components/fonts/font_store.rs
+++ b/components/fonts/font_store.rs
@@ -12,14 +12,14 @@ use style::stylesheets::DocumentStyleSheet;
use style::values::computed::{FontStyle, FontWeight};
use crate::font::FontDescriptor;
-use crate::font_context::WebFontDownloadState;
use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods, IsOblique};
use crate::system_font_service::{FontIdentifier, LowercaseFontFamilyName};
#[derive(Default)]
pub struct FontStore {
pub(crate) families: HashMap<LowercaseFontFamilyName, FontTemplates>,
- web_fonts_loading: Vec<(DocumentStyleSheet, usize)>,
+ web_fonts_loading_for_stylesheets: Vec<(DocumentStyleSheet, usize)>,
+ web_fonts_loading_for_script: usize,
}
pub(crate) type CrossThreadFontStore = Arc<RwLock<FontStore>>;
@@ -33,13 +33,13 @@ impl FontStore {
stylesheet: &DocumentStyleSheet,
) -> bool {
!self
- .web_fonts_loading
+ .web_fonts_loading_for_stylesheets
.iter()
.any(|(loading_stylesheet, _)| loading_stylesheet == stylesheet)
}
pub(crate) fn handle_stylesheet_removed(&mut self, stylesheet: &DocumentStyleSheet) {
- self.web_fonts_loading
+ self.web_fonts_loading_for_stylesheets
.retain(|(loading_stylesheet, _)| loading_stylesheet != stylesheet);
}
@@ -48,54 +48,82 @@ impl FontStore {
stylesheet: &DocumentStyleSheet,
) {
if let Some((_, count)) = self
- .web_fonts_loading
+ .web_fonts_loading_for_stylesheets
.iter_mut()
.find(|(loading_stylesheet, _)| loading_stylesheet == stylesheet)
{
*count += 1;
} else {
- self.web_fonts_loading.push((stylesheet.clone(), 1))
+ self.web_fonts_loading_for_stylesheets
+ .push((stylesheet.clone(), 1))
}
}
fn remove_one_web_font_loading_for_stylesheet(&mut self, stylesheet: &DocumentStyleSheet) {
if let Some((_, count)) = self
- .web_fonts_loading
+ .web_fonts_loading_for_stylesheets
.iter_mut()
.find(|(loading_stylesheet, _)| loading_stylesheet == stylesheet)
{
*count -= 1;
}
- self.web_fonts_loading.retain(|(_, count)| *count != 0);
+ self.web_fonts_loading_for_stylesheets
+ .retain(|(_, count)| *count != 0);
}
- pub(crate) fn handle_web_font_failed_to_load(&mut self, state: &WebFontDownloadState) {
- self.remove_one_web_font_loading_for_stylesheet(&state.stylesheet);
+ pub(crate) fn handle_web_font_load_failed_for_stylesheet(
+ &mut self,
+ stylesheet: &DocumentStyleSheet,
+ ) {
+ self.remove_one_web_font_loading_for_stylesheet(stylesheet);
}
/// Handle a web font load finishing, adding the new font to the [`FontStore`]. If the web font
/// load was canceled (for instance, if the stylesheet was removed), then do nothing and return
/// false.
- pub(crate) fn handle_web_font_loaded(
+ pub(crate) fn handle_web_font_loaded_for_stylesheet(
&mut self,
- state: &WebFontDownloadState,
+ stylesheet: &DocumentStyleSheet,
+ family_name: LowercaseFontFamilyName,
new_template: FontTemplate,
) -> bool {
// Abort processing this web font if the originating stylesheet was removed.
- if self.font_load_cancelled_for_stylesheet(&state.stylesheet) {
+ if self.font_load_cancelled_for_stylesheet(stylesheet) {
return false;
}
- let family_name = state.css_font_face_descriptors.family_name.clone();
+
+ self.add_new_template(family_name, new_template);
+
+ self.remove_one_web_font_loading_for_stylesheet(stylesheet);
+
+ true
+ }
+
+ pub(crate) fn add_new_template(
+ &mut self,
+ family_name: LowercaseFontFamilyName,
+ new_template: FontTemplate,
+ ) {
self.families
.entry(family_name)
.or_default()
.add_template(new_template);
- self.remove_one_web_font_loading_for_stylesheet(&state.stylesheet);
- true
+ }
+
+ pub(crate) fn handle_web_font_load_started_for_script(&mut self) {
+ self.web_fonts_loading_for_script += 1;
+ }
+
+ pub(crate) fn handle_web_font_load_finished_for_script(&mut self) {
+ self.web_fonts_loading_for_script -= 1;
}
pub(crate) fn number_of_fonts_still_loading(&self) -> usize {
- self.web_fonts_loading.iter().map(|(_, count)| count).sum()
+ self.web_fonts_loading_for_script +
+ self.web_fonts_loading_for_stylesheets
+ .iter()
+ .map(|(_, count)| count)
+ .sum::<usize>()
}
}
diff --git a/components/fonts/font_template.rs b/components/fonts/font_template.rs
index d7733f66274..4c2e32bd3ea 100644
--- a/components/fonts/font_template.rs
+++ b/components/fonts/font_template.rs
@@ -177,13 +177,13 @@ impl FontTemplate {
pub fn new_for_local_web_font(
local_template: FontTemplateRef,
css_font_template_descriptors: &CSSFontFaceDescriptors,
- stylesheet: DocumentStyleSheet,
+ stylesheet: Option<DocumentStyleSheet>,
) -> Result<FontTemplate, &'static str> {
let mut alias_template = local_template.borrow().clone();
alias_template
.descriptor
.override_values_with_css_font_template_descriptors(css_font_template_descriptors);
- alias_template.stylesheet = Some(stylesheet);
+ alias_template.stylesheet = stylesheet;
Ok(alias_template)
}
diff --git a/components/fonts/system_font_service.rs b/components/fonts/system_font_service.rs
index b29eca6b86c..e50b3144f54 100644
--- a/components/fonts/system_font_service.rs
+++ b/components/fonts/system_font_service.rs
@@ -565,7 +565,7 @@ impl SystemFontServiceProxy {
}
}
-#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, PartialEq, Serialize)]
+#[derive(Clone, Debug, Default, Deserialize, Eq, Hash, MallocSizeOf, PartialEq, Serialize)]
pub struct LowercaseFontFamilyName {
inner: String,
}
diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs
index 987d8ad1839..8c275230b23 100644
--- a/components/layout_thread/lib.rs
+++ b/components/layout_thread/lib.rs
@@ -24,7 +24,7 @@ use fnv::FnvHashMap;
use fonts::{
get_and_reset_text_shaping_performance_counter, FontContext, FontContextWebFontMethods,
};
-use fonts_traits::WebFontLoadFinishedCallback;
+use fonts_traits::StylesheetWebFontLoadFinishedCallback;
use fxhash::{FxHashMap, FxHashSet};
use ipc_channel::ipc::IpcSender;
use layout::construct::ConstructionResult;
@@ -627,7 +627,7 @@ impl LayoutThread {
stylesheet,
guard,
self.stylist.device(),
- Arc::new(web_font_finished_loading_callback) as WebFontLoadFinishedCallback,
+ Arc::new(web_font_finished_loading_callback) as StylesheetWebFontLoadFinishedCallback,
);
}
diff --git a/components/layout_thread_2020/lib.rs b/components/layout_thread_2020/lib.rs
index 03f47d5904c..a591226b946 100644
--- a/components/layout_thread_2020/lib.rs
+++ b/components/layout_thread_2020/lib.rs
@@ -23,7 +23,7 @@ use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect, Size2D as
use euclid::{Point2D, Scale, Size2D, Vector2D};
use fnv::FnvHashMap;
use fonts::{FontContext, FontContextWebFontMethods};
-use fonts_traits::WebFontLoadFinishedCallback;
+use fonts_traits::StylesheetWebFontLoadFinishedCallback;
use fxhash::FxHashMap;
use ipc_channel::ipc::IpcSender;
use layout::context::LayoutContext;
@@ -602,7 +602,7 @@ impl LayoutThread {
stylesheet,
guard,
self.stylist.device(),
- Arc::new(web_font_finished_loading_callback) as WebFontLoadFinishedCallback,
+ Arc::new(web_font_finished_loading_callback) as StylesheetWebFontLoadFinishedCallback,
);
}
diff --git a/components/malloc_size_of/lib.rs b/components/malloc_size_of/lib.rs
index ec633a49f9b..50d4ce30718 100644
--- a/components/malloc_size_of/lib.rs
+++ b/components/malloc_size_of/lib.rs
@@ -711,6 +711,7 @@ malloc_size_of_is_0!(std::sync::atomic::AtomicUsize);
malloc_size_of_is_0!(std::time::Duration);
malloc_size_of_is_0!(std::time::Instant);
malloc_size_of_is_0!(std::time::SystemTime);
+malloc_size_of_is_0!(style::font_face::SourceList);
macro_rules! malloc_size_of_is_webrender_malloc_size_of(
($($ty:ty),+) => (
diff --git a/components/profile/time.rs b/components/profile/time.rs
index 73a48efa764..c6afff4e7b5 100644
--- a/components/profile/time.rs
+++ b/components/profile/time.rs
@@ -104,6 +104,7 @@ impl Formattable for ProfilerCategory {
ProfilerCategory::ScriptDocumentEvent => "Script Document Event",
ProfilerCategory::ScriptEvaluate => "Script JS Evaluate",
ProfilerCategory::ScriptFileRead => "Script File Read",
+ ProfilerCategory::ScriptFontLoading => "Script Font Loading",
ProfilerCategory::ScriptHistoryEvent => "Script History Event",
ProfilerCategory::ScriptImageCacheMsg => "Script Image Cache Msg",
ProfilerCategory::ScriptInputEvent => "Script Input Event",
diff --git a/components/script/dom/eventsource.rs b/components/script/dom/eventsource.rs
index ba4c1dca9dc..bbec13d0d83 100644
--- a/components/script/dom/eventsource.rs
+++ b/components/script/dom/eventsource.rs
@@ -594,7 +594,7 @@ impl EventSourceMethods<crate::DomTypeHolder> for EventSource {
last_event_id: String::new(),
resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
};
- let listener = NetworkListener {
+ let mut listener = NetworkListener {
context: Arc::new(Mutex::new(context)),
task_source: global.task_manager().networking_task_source().into(),
};
diff --git a/components/script/dom/fontface.rs b/components/script/dom/fontface.rs
new file mode 100644
index 00000000000..9fb753600fa
--- /dev/null
+++ b/components/script/dom/fontface.rs
@@ -0,0 +1,577 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+use std::cell::{Cell, RefCell};
+use std::rc::Rc;
+
+use cssparser::{Parser, ParserInput};
+use dom_struct::dom_struct;
+use fonts::{FontContext, FontContextWebFontMethods, FontTemplate, LowercaseFontFamilyName};
+use js::rust::HandleObject;
+use style::error_reporting::ParseErrorReporter;
+use style::font_face::SourceList;
+use style::parser::ParserContext;
+use style::stylesheets::{CssRuleType, FontFaceRule, Origin, UrlExtraData};
+use style_traits::{ParsingMode, ToCss};
+
+use super::bindings::cell::DomRefCell;
+use super::bindings::codegen::UnionTypes::StringOrArrayBufferViewOrArrayBuffer;
+use super::bindings::error::{Error, ErrorResult, Fallible};
+use super::bindings::refcounted::Trusted;
+use super::bindings::reflector::DomGlobal;
+use super::bindings::root::MutNullableDom;
+use super::types::FontFaceSet;
+use crate::dom::bindings::codegen::Bindings::FontFaceBinding::{
+ FontFaceDescriptors, FontFaceLoadStatus, FontFaceMethods,
+};
+use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
+use crate::dom::bindings::codegen::UnionTypes;
+use crate::dom::bindings::reflector::{reflect_dom_object_with_proto, Reflector};
+use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::str::DOMString;
+use crate::dom::globalscope::GlobalScope;
+use crate::dom::promise::Promise;
+use crate::dom::window::Window;
+use crate::script_runtime::CanGc;
+
+/// <https://drafts.csswg.org/css-font-loading/#fontface-interface>
+#[dom_struct]
+pub struct FontFace {
+ reflector: Reflector,
+ status: Cell<FontFaceLoadStatus>,
+ family_name: DomRefCell<DOMString>,
+ descriptors: DomRefCell<FontFaceDescriptors>,
+
+ /// A reference to the [`FontFaceSet`] that this `FontFace` is a member of, if it has been
+ /// added to one. `None` otherwise. The spec suggests that a `FontFace` can be a member of
+ /// multiple `FontFaceSet`s, but this doesn't seem to be the case in practice, as the
+ /// `FontFaceSet` constructor is not exposed on the global scope.
+ font_face_set: MutNullableDom<FontFaceSet>,
+
+ /// This holds the [`FontTemplate`] resulting from loading this `FontFace`, to be used when the
+ /// `FontFace` is added to the global `FontFaceSet` and thus the `[FontContext]`.
+ //
+ // TODO: This could potentially share the `FontTemplateRef` created by `FontContext`, rather
+ // than having its own copy of the template.
+ #[no_trace = "Does not contain managed objects"]
+ template: RefCell<Option<(LowercaseFontFamilyName, FontTemplate)>>,
+
+ #[no_trace = "Does not contain managed objects"]
+ /// <https://drafts.csswg.org/css-font-loading/#m-fontface-urls-slot>
+ urls: DomRefCell<Option<SourceList>>,
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-fontstatuspromise-slot>
+ #[ignore_malloc_size_of = "Rc"]
+ font_status_promise: Rc<Promise>,
+}
+
+/// Given the various font face descriptors, construct the equivalent `@font-face` css rule as a
+/// string and parse it using `style` crate. Returns `Err(Error::Syntax)` if parsing fails.
+///
+/// Due to lack of support in the `style` crate, parsing the whole `@font-face` rule is much easier
+/// to implement than parsing each declaration on its own.
+fn parse_font_face_descriptors(
+ global: &GlobalScope,
+ family_name: &DOMString,
+ sources: Option<&str>,
+ input_descriptors: &FontFaceDescriptors,
+) -> Fallible<FontFaceRule> {
+ let window = global.as_window(); // TODO: Support calling FontFace APIs from Worker
+ let quirks_mode = window.Document().quirks_mode();
+ let url_data = UrlExtraData(window.get_url().get_arc());
+ let error_reporter = FontFaceErrorReporter {
+ not_encountered_error: Cell::new(true),
+ };
+ let parser_context = ParserContext::new(
+ Origin::Author,
+ &url_data,
+ Some(CssRuleType::FontFace),
+ ParsingMode::DEFAULT,
+ quirks_mode,
+ /* namespaces = */ Default::default(),
+ Some(&error_reporter as &dyn ParseErrorReporter),
+ None,
+ );
+
+ let FontFaceDescriptors {
+ ref ascentOverride,
+ ref descentOverride,
+ ref display,
+ ref featureSettings,
+ ref lineGapOverride,
+ ref stretch,
+ ref style,
+ ref unicodeRange,
+ ref variationSettings,
+ ref weight,
+ } = input_descriptors;
+
+ let _ = variationSettings; // TODO: Stylo doesn't parse font-variation-settings yet.
+ let maybe_sources = sources.map_or_else(String::new, |sources| format!("src: {sources};"));
+ let font_face_rule = format!(
+ r"
+ ascent-override: {ascentOverride};
+ descent-override: {descentOverride};
+ font-display: {display};
+ font-family: {family_name};
+ font-feature-settings: {featureSettings};
+ font-stretch: {stretch};
+ font-style: {style};
+ font-weight: {weight};
+ line-gap-override: {lineGapOverride};
+ unicode-range: {unicodeRange};
+ {maybe_sources}
+ "
+ );
+
+ // TODO: Should this be the source location in the script that invoked the font face API?
+ let location = cssparser::SourceLocation { line: 0, column: 0 };
+ let mut input = ParserInput::new(&font_face_rule);
+ let mut parser = Parser::new(&mut input);
+ let mut parsed_font_face_rule =
+ style::font_face::parse_font_face_block(&parser_context, &mut parser, location);
+
+ if let Some(ref mut sources) = parsed_font_face_rule.sources {
+ let supported_sources: Vec<_> = sources
+ .0
+ .iter()
+ .rev()
+ .filter(FontContext::is_supported_web_font_source)
+ .cloned()
+ .collect();
+ if supported_sources.is_empty() {
+ error_reporter.not_encountered_error.set(false);
+ } else {
+ sources.0 = supported_sources;
+ }
+ }
+
+ if error_reporter.not_encountered_error.get() {
+ Ok(parsed_font_face_rule)
+ } else {
+ Err(Error::Syntax)
+ }
+}
+
+fn serialize_parsed_descriptors(font_face_rule: &FontFaceRule) -> FontFaceDescriptors {
+ FontFaceDescriptors {
+ ascentOverride: font_face_rule.ascent_override.to_css_string().into(),
+ descentOverride: font_face_rule.descent_override.to_css_string().into(),
+ display: font_face_rule.display.to_css_string().into(),
+ featureSettings: font_face_rule.feature_settings.to_css_string().into(),
+ lineGapOverride: font_face_rule.line_gap_override.to_css_string().into(),
+ stretch: font_face_rule.stretch.to_css_string().into(),
+ style: font_face_rule.style.to_css_string().into(),
+ unicodeRange: font_face_rule.unicode_range.to_css_string().into(),
+ variationSettings: font_face_rule.variation_settings.to_css_string().into(),
+ weight: font_face_rule.weight.to_css_string().into(),
+ }
+}
+
+struct FontFaceErrorReporter {
+ not_encountered_error: Cell<bool>,
+}
+
+impl ParseErrorReporter for FontFaceErrorReporter {
+ fn report_error(
+ &self,
+ _url: &UrlExtraData,
+ _location: cssparser::SourceLocation,
+ _error: style::error_reporting::ContextualParseError,
+ ) {
+ self.not_encountered_error.set(false);
+ }
+}
+
+impl FontFace {
+ /// Construct a [`FontFace`] to be used in the case of failure in parsing the
+ /// font face descriptors.
+ fn new_failed_font_face(global: &GlobalScope, can_gc: CanGc) -> Self {
+ let font_status_promise = Promise::new(global, can_gc);
+ // If any of them fail to parse correctly, reject font face’s [[FontStatusPromise]] with a
+ // DOMException named "SyntaxError"
+ font_status_promise.reject_error(Error::Syntax);
+
+ // set font face’s corresponding attributes to the empty string, and set font face’s status
+ // attribute to "error"
+ Self {
+ reflector: Reflector::new(),
+ font_face_set: MutNullableDom::default(),
+ font_status_promise,
+ family_name: DomRefCell::default(),
+ urls: DomRefCell::default(),
+ descriptors: DomRefCell::new(FontFaceDescriptors {
+ ascentOverride: DOMString::new(),
+ descentOverride: DOMString::new(),
+ display: DOMString::new(),
+ featureSettings: DOMString::new(),
+ lineGapOverride: DOMString::new(),
+ stretch: DOMString::new(),
+ style: DOMString::new(),
+ unicodeRange: DOMString::new(),
+ variationSettings: DOMString::new(),
+ weight: DOMString::new(),
+ }),
+ status: Cell::new(FontFaceLoadStatus::Error),
+ template: RefCell::default(),
+ }
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#font-face-constructor>
+ fn new_inherited(
+ global: &GlobalScope,
+ family_name: DOMString,
+ source: StringOrArrayBufferViewOrArrayBuffer,
+ descriptors: &FontFaceDescriptors,
+ can_gc: CanGc,
+ ) -> Self {
+ // TODO: Add support for ArrayBuffer and ArrayBufferView sources.
+ let StringOrArrayBufferViewOrArrayBuffer::String(ref source_string) = source else {
+ return Self::new_failed_font_face(global, can_gc);
+ };
+
+ // Step 1. Parse the family argument, and the members of the descriptors argument,
+ // according to the grammars of the corresponding descriptors of the CSS @font-face rule If
+ // the source argument is a CSSOMString, parse it according to the grammar of the CSS src
+ // descriptor of the @font-face rule.
+ let parse_result =
+ parse_font_face_descriptors(global, &family_name, Some(source_string), descriptors);
+
+ let Ok(ref parsed_font_face_rule) = parse_result else {
+ // If any of them fail to parse correctly, reject font face’s
+ // [[FontStatusPromise]] with a DOMException named "SyntaxError", set font face’s
+ // corresponding attributes to the empty string, and set font face’s status attribute
+ // to "error".
+ return Self::new_failed_font_face(global, can_gc);
+ };
+
+ // Set its internal [[FontStatusPromise]] slot to a fresh pending Promise object.
+ let font_status_promise = Promise::new(global, can_gc);
+
+ let sources = parsed_font_face_rule
+ .sources
+ .clone()
+ .expect("Sources should be non-None after validation");
+
+ // Let font face be a fresh FontFace object.
+ Self {
+ reflector: Reflector::new(),
+
+ // Set font face’s status attribute to "unloaded".
+ status: Cell::new(FontFaceLoadStatus::Unloaded),
+
+ // Set font face’s corresponding attributes to the serialization of the parsed values.
+ descriptors: DomRefCell::new(serialize_parsed_descriptors(parsed_font_face_rule)),
+
+ font_face_set: MutNullableDom::default(),
+ family_name: DomRefCell::new(family_name.clone()),
+ urls: DomRefCell::new(Some(sources)),
+ template: RefCell::default(),
+ font_status_promise,
+ }
+ }
+
+ pub(crate) fn new(
+ global: &GlobalScope,
+ proto: Option<HandleObject>,
+ font_family: DOMString,
+ source: StringOrArrayBufferViewOrArrayBuffer,
+ descriptors: &FontFaceDescriptors,
+ can_gc: CanGc,
+ ) -> DomRoot<Self> {
+ reflect_dom_object_with_proto(
+ Box::new(Self::new_inherited(
+ global,
+ font_family,
+ source,
+ descriptors,
+ can_gc,
+ )),
+ global,
+ proto,
+ can_gc,
+ )
+ }
+
+ pub(super) fn set_associated_font_face_set(&self, font_face_set: &FontFaceSet) {
+ self.font_face_set.set(Some(font_face_set));
+ }
+
+ pub(super) fn loaded(&self) -> bool {
+ self.status.get() == FontFaceLoadStatus::Loaded
+ }
+
+ pub(super) fn template(&self) -> Option<(LowercaseFontFamilyName, FontTemplate)> {
+ self.template.borrow().clone()
+ }
+
+ /// Implements the body of the setter for the descriptor attributes of the [`FontFace`] interface.
+ ///
+ /// <https://drafts.csswg.org/css-font-loading/#fontface-interface>:
+ /// On setting, parse the string according to the grammar for the corresponding @font-face
+ /// descriptor. If it does not match the grammar, throw a SyntaxError; otherwise, set the attribute
+ /// to the serialization of the parsed value.
+ fn validate_and_set_descriptors(&self, new_descriptors: FontFaceDescriptors) -> ErrorResult {
+ let global = self.global();
+ let parsed_font_face_rule = parse_font_face_descriptors(
+ &global,
+ &self.family_name.borrow(),
+ None,
+ &new_descriptors,
+ )?;
+
+ *self.descriptors.borrow_mut() = serialize_parsed_descriptors(&parsed_font_face_rule);
+ Ok(())
+ }
+}
+
+impl FontFaceMethods<crate::DomTypeHolder> for FontFace {
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-family>
+ fn Family(&self) -> DOMString {
+ self.family_name.borrow().clone()
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-family>
+ fn SetFamily(&self, family_name: DOMString) -> ErrorResult {
+ let descriptors = self.descriptors.borrow();
+ let global = self.global();
+ let _ = parse_font_face_descriptors(&global, &family_name, None, &descriptors)?;
+ *self.family_name.borrow_mut() = family_name;
+ Ok(())
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-style>
+ fn Style(&self) -> DOMString {
+ self.descriptors.borrow().style.clone()
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-style>
+ fn SetStyle(&self, value: DOMString) -> ErrorResult {
+ let mut new_descriptors = self.descriptors.borrow().clone();
+ new_descriptors.style = value;
+ self.validate_and_set_descriptors(new_descriptors)
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-weight>
+ fn Weight(&self) -> DOMString {
+ self.descriptors.borrow().weight.clone()
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-weight>
+ fn SetWeight(&self, value: DOMString) -> ErrorResult {
+ let mut new_descriptors = self.descriptors.borrow().clone();
+ new_descriptors.weight = value;
+ self.validate_and_set_descriptors(new_descriptors)
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-stretch>
+ fn Stretch(&self) -> DOMString {
+ self.descriptors.borrow().stretch.clone()
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-stretch>
+ fn SetStretch(&self, value: DOMString) -> ErrorResult {
+ let mut new_descriptors = self.descriptors.borrow().clone();
+ new_descriptors.stretch = value;
+ self.validate_and_set_descriptors(new_descriptors)
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-unicoderange>
+ fn UnicodeRange(&self) -> DOMString {
+ self.descriptors.borrow().unicodeRange.clone()
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-unicoderange>
+ fn SetUnicodeRange(&self, value: DOMString) -> ErrorResult {
+ let mut new_descriptors = self.descriptors.borrow().clone();
+ new_descriptors.unicodeRange = value;
+ self.validate_and_set_descriptors(new_descriptors)
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-featuresettings>
+ fn FeatureSettings(&self) -> DOMString {
+ self.descriptors.borrow().featureSettings.clone()
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-featuresettings>
+ fn SetFeatureSettings(&self, value: DOMString) -> ErrorResult {
+ let mut new_descriptors = self.descriptors.borrow().clone();
+ new_descriptors.featureSettings = value;
+ self.validate_and_set_descriptors(new_descriptors)
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-variationsettings>
+ fn VariationSettings(&self) -> DOMString {
+ self.descriptors.borrow().variationSettings.clone()
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-variationsettings>
+ fn SetVariationSettings(&self, value: DOMString) -> ErrorResult {
+ let mut new_descriptors = self.descriptors.borrow().clone();
+ new_descriptors.variationSettings = value;
+ self.validate_and_set_descriptors(new_descriptors)
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-display>
+ fn Display(&self) -> DOMString {
+ self.descriptors.borrow().display.clone()
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-display>
+ fn SetDisplay(&self, value: DOMString) -> ErrorResult {
+ let mut new_descriptors = self.descriptors.borrow().clone();
+ new_descriptors.display = value;
+ self.validate_and_set_descriptors(new_descriptors)
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-ascentoverride>
+ fn AscentOverride(&self) -> DOMString {
+ self.descriptors.borrow().ascentOverride.clone()
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-ascentoverride>
+ fn SetAscentOverride(&self, value: DOMString) -> ErrorResult {
+ let mut new_descriptors = self.descriptors.borrow().clone();
+ new_descriptors.ascentOverride = value;
+ self.validate_and_set_descriptors(new_descriptors)
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-descentoverride>
+ fn DescentOverride(&self) -> DOMString {
+ self.descriptors.borrow().descentOverride.clone()
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-descentoverride>
+ fn SetDescentOverride(&self, value: DOMString) -> ErrorResult {
+ let mut new_descriptors = self.descriptors.borrow().clone();
+ new_descriptors.descentOverride = value;
+ self.validate_and_set_descriptors(new_descriptors)
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-linegapoverride>
+ fn LineGapOverride(&self) -> DOMString {
+ self.descriptors.borrow().lineGapOverride.clone()
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-linegapoverride>
+ fn SetLineGapOverride(&self, value: DOMString) -> ErrorResult {
+ let mut new_descriptors = self.descriptors.borrow().clone();
+ new_descriptors.lineGapOverride = value;
+ self.validate_and_set_descriptors(new_descriptors)
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-status>
+ fn Status(&self) -> FontFaceLoadStatus {
+ self.status.get()
+ }
+
+ /// The load() method of FontFace forces a url-based font face to request its font data and
+ /// load. For fonts constructed from a buffer source, or fonts that are already loading or
+ /// loaded, it does nothing.
+ /// <https://drafts.csswg.org/css-font-loading/#font-face-load>
+ fn Load(&self) -> Rc<Promise> {
+ let Some(sources) = self.urls.borrow_mut().take() else {
+ // Step 2. If font face’s [[Urls]] slot is null, or its status attribute is anything
+ // other than "unloaded", return font face’s [[FontStatusPromise]] and abort these
+ // steps.
+ return self.font_status_promise.clone();
+ };
+
+ // FontFace must not be loaded at this point as `self.urls` is not None, implying `Load`
+ // wasn't called already. In our implementation, `urls` is set after parsing, so it
+ // cannot be `Some` if the status is `Error`.
+ debug_assert_eq!(self.status.get(), FontFaceLoadStatus::Unloaded);
+
+ let global = self.global();
+ let trusted = Trusted::new(self);
+ let task_source = global
+ .task_manager()
+ .font_loading_task_source()
+ .to_sendable();
+
+ let finished_callback = Box::new(
+ move |family_name: LowercaseFontFamilyName, load_result: Option<_>| {
+ let trusted = trusted.clone();
+
+ // Step 5. When the load operation completes, successfully or not, queue a task to
+ // run the following steps synchronously:
+ task_source.queue(task!(resolve_font_face_load_task: move || {
+ let font_face = trusted.root();
+
+ match load_result {
+ None => {
+ // Step 5.1. If the attempt to load fails, reject font face’s
+ // [[FontStatusPromise]] with a DOMException whose name is "NetworkError"
+ // and set font face’s status attribute to "error".
+ font_face.status.set(FontFaceLoadStatus::Error);
+ font_face.font_status_promise.reject_error(Error::Network);
+ }
+ Some(template) => {
+ // Step 5.2. Otherwise, font face now represents the loaded font;
+ // fulfill font face’s [[FontStatusPromise]] with font face and set
+ // font face’s status attribute to "loaded".
+ font_face.status.set(FontFaceLoadStatus::Loaded);
+ let old_template = font_face.template.borrow_mut().replace((family_name, template));
+ debug_assert!(old_template.is_none(), "FontFace's template must be intialized only once");
+ font_face.font_status_promise.resolve_native(&font_face);
+ }
+ }
+
+ if let Some(font_face_set) = font_face.font_face_set.get() {
+ // For each FontFaceSet font face is in: ...
+ //
+ // This implements steps 5.1.1, 5.1.2, 5.2.1 and 5.2.2 - these
+ // take care of changing the status of the `FontFaceSet` in which this
+ // `FontFace` is a member, for both failed and successful load.
+ font_face_set.handle_font_face_status_changed(&font_face);
+ }
+ }));
+ },
+ );
+
+ // We parse the descriptors again because they are stored as `DOMString`s in this `FontFace`
+ // but the `load_web_font_for_script` API needs parsed values.
+ let parsed_font_face_rule = parse_font_face_descriptors(
+ &global,
+ &self.family_name.borrow(),
+ None,
+ &self.descriptors.borrow(),
+ )
+ .expect("Parsing shouldn't fail as descriptors are valid by construction");
+
+ // Step 4. Using the value of font face’s [[Urls]] slot, attempt to load a font as defined
+ // in [CSS-FONTS-3], as if it was the value of a @font-face rule’s src descriptor.
+ // TODO: FontFaceSet is not supported on Workers yet. The `as_window` call below should be
+ // replaced when we do support it.
+ global.as_window().font_context().load_web_font_for_script(
+ global.webview_id(),
+ sources,
+ (&parsed_font_face_rule).into(),
+ finished_callback,
+ );
+
+ // Step 3. Set font face’s status attribute to "loading", return font face’s
+ // [[FontStatusPromise]], and continue executing the rest of this algorithm asynchronously.
+ self.status.set(FontFaceLoadStatus::Loading);
+ self.font_status_promise.clone()
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontface-loaded>
+ fn Loaded(&self) -> Rc<Promise> {
+ self.font_status_promise.clone()
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#font-face-constructor>
+ fn Constructor(
+ window: &Window,
+ proto: Option<HandleObject>,
+ can_gc: CanGc,
+ family: DOMString,
+ source: UnionTypes::StringOrArrayBufferViewOrArrayBuffer,
+ descriptors: &FontFaceDescriptors,
+ ) -> DomRoot<FontFace> {
+ let global = window.as_global_scope();
+ FontFace::new(global, proto, family, source, descriptors, can_gc)
+ }
+}
diff --git a/components/script/dom/fontfaceset.rs b/components/script/dom/fontfaceset.rs
index dfaaf553b10..497da30680d 100644
--- a/components/script/dom/fontfaceset.rs
+++ b/components/script/dom/fontfaceset.rs
@@ -5,26 +5,35 @@
use std::rc::Rc;
use dom_struct::dom_struct;
+use fonts::FontContextWebFontMethods;
use js::rust::HandleObject;
+use super::bindings::reflector::DomGlobal;
+use super::types::Window;
use crate::dom::bindings::codegen::Bindings::FontFaceSetBinding::FontFaceSetMethods;
+use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
+use crate::dom::bindings::refcounted::TrustedPromise;
use crate::dom::bindings::reflector::reflect_dom_object_with_proto;
use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::str::DOMString;
use crate::dom::eventtarget::EventTarget;
+use crate::dom::fontface::FontFace;
use crate::dom::globalscope::GlobalScope;
use crate::dom::promise::Promise;
-use crate::realms::enter_realm;
use crate::script_runtime::CanGc;
+/// <https://drafts.csswg.org/css-font-loading/#FontFaceSet-interface>
#[dom_struct]
pub(crate) struct FontFaceSet {
target: EventTarget,
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontfaceset-readypromise-slot>
#[ignore_malloc_size_of = "Rc"]
promise: Rc<Promise>,
}
impl FontFaceSet {
- pub(crate) fn new_inherited(global: &GlobalScope, can_gc: CanGc) -> Self {
+ fn new_inherited(global: &GlobalScope, can_gc: CanGc) -> Self {
FontFaceSet {
target: EventTarget::new_inherited(),
promise: Promise::new(global, can_gc),
@@ -44,9 +53,24 @@ impl FontFaceSet {
)
}
+ pub(super) fn handle_font_face_status_changed(&self, font_face: &FontFace) {
+ if font_face.loaded() {
+ let Some(window) = DomRoot::downcast::<Window>(self.global()) else {
+ return;
+ };
+
+ let (family_name, template) = font_face
+ .template()
+ .expect("A loaded web font should have a template");
+ window
+ .font_context()
+ .add_template_to_font_context(family_name, template);
+ window.Document().dirty_all_nodes();
+ }
+ }
+
pub(crate) fn fulfill_ready_promise_if_needed(&self) {
if !self.promise.is_fulfilled() {
- let _ac = enter_realm(&*self.promise);
self.promise.resolve_native(self);
}
}
@@ -57,4 +81,43 @@ impl FontFaceSetMethods<crate::DomTypeHolder> for FontFaceSet {
fn Ready(&self) -> Rc<Promise> {
self.promise.clone()
}
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontfaceset-add>
+ fn Add(&self, font_face: &FontFace) -> DomRoot<FontFaceSet> {
+ font_face.set_associated_font_face_set(self);
+ self.handle_font_face_status_changed(font_face);
+ DomRoot::from_ref(self)
+ }
+
+ /// <https://drafts.csswg.org/css-font-loading/#dom-fontfaceset-load>
+ fn Load(&self, _font: DOMString, _text: DOMString, can_gc: CanGc) -> Rc<Promise> {
+ // Step 1. Let font face set be the FontFaceSet object this method was called on. Let
+ // promise be a newly-created promise object.
+ let promise = Promise::new(&self.global(), can_gc);
+
+ // TODO: Step 3. Find the matching font faces from font face set using the font and text
+ // arguments passed to the function, and let font face list be the return value (ignoring
+ // the found faces flag). If a syntax error was returned, reject promise with a SyntaxError
+ // exception and terminate these steps.
+
+ let trusted = TrustedPromise::new(promise.clone());
+ // Step 4. Queue a task to run the following steps synchronously:
+ self.global()
+ .task_manager()
+ .font_loading_task_source()
+ .queue(task!(resolve_font_face_set_load_task: move || {
+ let promise = trusted.root();
+
+ // TODO: Step 4.1. For all of the font faces in the font face list, call their load()
+ // method.
+
+ // TODO: Step 4.2. Resolve promise with the result of waiting for all of the
+ // [[FontStatusPromise]]s of each font face in the font face list, in order.
+ let matched_fonts = Vec::<&FontFace>::new();
+ promise.resolve_native(&matched_fonts);
+ }));
+
+ // Step 2. Return promise. Complete the rest of these steps asynchronously.
+ promise
+ }
}
diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs
index b0ea5d3aeb6..7127828224e 100644
--- a/components/script/dom/mod.rs
+++ b/components/script/dom/mod.rs
@@ -321,6 +321,7 @@ pub(crate) mod filelist;
pub(crate) mod filereader;
pub(crate) mod filereadersync;
pub(crate) mod focusevent;
+pub(crate) mod fontface;
pub(crate) mod fontfaceset;
pub(crate) mod formdata;
pub(crate) mod formdataevent;
diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs
index 375e6621992..f0b4b430ed6 100644
--- a/components/script/dom/window.rs
+++ b/components/script/dom/window.rs
@@ -634,6 +634,10 @@ impl Window {
) -> EventStatus {
event.dispatch(self.upcast(), true, can_gc)
}
+
+ pub(crate) fn font_context(&self) -> &Arc<FontContext> {
+ &self.font_context
+ }
}
// https://html.spec.whatwg.org/multipage/#atob
diff --git a/components/script/network_listener.rs b/components/script/network_listener.rs
index 5e662b8ef76..0e18af5dd16 100644
--- a/components/script/network_listener.rs
+++ b/components/script/network_listener.rs
@@ -73,7 +73,7 @@ pub(crate) fn submit_timing_data(
}
impl<Listener: PreInvoke + Send + 'static> NetworkListener<Listener> {
- pub(crate) fn notify<A: Action<Listener> + Send + 'static>(&self, action: A) {
+ pub(crate) fn notify<A: Action<Listener> + Send + 'static>(&mut self, action: A) {
self.task_source.queue(ListenerTask {
context: self.context.clone(),
action,
@@ -83,11 +83,11 @@ impl<Listener: PreInvoke + Send + 'static> NetworkListener<Listener> {
// helps type inference
impl<Listener: FetchResponseListener + PreInvoke + Send + 'static> NetworkListener<Listener> {
- pub(crate) fn notify_fetch(&self, action: FetchResponseMsg) {
+ pub(crate) fn notify_fetch(&mut self, action: FetchResponseMsg) {
self.notify(action);
}
- pub(crate) fn into_callback(self) -> BoxedFetchCallback {
+ pub(crate) fn into_callback(mut self) -> BoxedFetchCallback {
Box::new(move |response_msg| self.notify_fetch(response_msg))
}
}
diff --git a/components/script/script_runtime.rs b/components/script/script_runtime.rs
index a51871eadf4..71e93671f47 100644
--- a/components/script/script_runtime.rs
+++ b/components/script/script_runtime.rs
@@ -104,6 +104,7 @@ pub(crate) enum ScriptThreadEventCategory {
DevtoolsMsg,
DocumentEvent,
FileRead,
+ FontLoading,
FormPlannedNavigation,
HistoryEvent,
ImageCacheMsg,
@@ -139,6 +140,7 @@ impl From<ScriptThreadEventCategory> for ProfilerCategory {
ScriptThreadEventCategory::EnterFullscreen => ProfilerCategory::ScriptEnterFullscreen,
ScriptThreadEventCategory::ExitFullscreen => ProfilerCategory::ScriptExitFullscreen,
ScriptThreadEventCategory::FileRead => ProfilerCategory::ScriptFileRead,
+ ScriptThreadEventCategory::FontLoading => ProfilerCategory::ScriptFontLoading,
ScriptThreadEventCategory::FormPlannedNavigation => {
ProfilerCategory::ScriptPlannedNavigation
},
@@ -181,6 +183,7 @@ impl From<ScriptThreadEventCategory> for ScriptHangAnnotation {
ScriptThreadEventCategory::DocumentEvent => ScriptHangAnnotation::DocumentEvent,
ScriptThreadEventCategory::InputEvent => ScriptHangAnnotation::InputEvent,
ScriptThreadEventCategory::FileRead => ScriptHangAnnotation::FileRead,
+ ScriptThreadEventCategory::FontLoading => ScriptHangAnnotation::FontLoading,
ScriptThreadEventCategory::FormPlannedNavigation => {
ScriptHangAnnotation::FormPlannedNavigation
},
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index b0066c88768..22a4e2e9e9b 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -1614,6 +1614,9 @@ impl ScriptThread {
ScriptThreadEventCategory::FileRead => {
time_profile!(ProfilerCategory::ScriptFileRead, None, profiler_chan, f)
},
+ ScriptThreadEventCategory::FontLoading => {
+ time_profile!(ProfilerCategory::ScriptFontLoading, None, profiler_chan, f)
+ },
ScriptThreadEventCategory::FormPlannedNavigation => time_profile!(
ProfilerCategory::ScriptPlannedNavigation,
None,
diff --git a/components/script/task_manager.rs b/components/script/task_manager.rs
index eeae042b6ee..4854ef7cd56 100644
--- a/components/script/task_manager.rs
+++ b/components/script/task_manager.rs
@@ -134,6 +134,7 @@ impl TaskManager {
task_source_functions!(self, canvas_blob_task_source, Canvas);
task_source_functions!(self, dom_manipulation_task_source, DOMManipulation);
task_source_functions!(self, file_reading_task_source, FileReading);
+ task_source_functions!(self, font_loading_task_source, FontLoading);
task_source_functions!(self, gamepad_task_source, Gamepad);
task_source_functions!(self, media_element_task_source, MediaElement);
task_source_functions!(self, networking_task_source, Networking);
diff --git a/components/script/task_source.rs b/components/script/task_source.rs
index 0030dcb8c30..3d9993f6082 100644
--- a/components/script/task_source.rs
+++ b/components/script/task_source.rs
@@ -27,6 +27,8 @@ pub(crate) enum TaskSourceName {
Canvas,
DOMManipulation,
FileReading,
+ /// <https://drafts.csswg.org/css-font-loading/#task-source>
+ FontLoading,
HistoryTraversal,
Networking,
PerformanceTimeline,
@@ -48,6 +50,7 @@ impl From<TaskSourceName> for ScriptThreadEventCategory {
TaskSourceName::Canvas => ScriptThreadEventCategory::ScriptEvent,
TaskSourceName::DOMManipulation => ScriptThreadEventCategory::ScriptEvent,
TaskSourceName::FileReading => ScriptThreadEventCategory::FileRead,
+ TaskSourceName::FontLoading => ScriptThreadEventCategory::FontLoading,
TaskSourceName::HistoryTraversal => ScriptThreadEventCategory::HistoryEvent,
TaskSourceName::Networking => ScriptThreadEventCategory::NetworkEvent,
TaskSourceName::PerformanceTimeline => {
@@ -71,6 +74,7 @@ impl TaskSourceName {
TaskSourceName::Canvas,
TaskSourceName::DOMManipulation,
TaskSourceName::FileReading,
+ TaskSourceName::FontLoading,
TaskSourceName::HistoryTraversal,
TaskSourceName::Networking,
TaskSourceName::PerformanceTimeline,
diff --git a/components/script_bindings/codegen/Bindings.conf b/components/script_bindings/codegen/Bindings.conf
index bdc57b39746..57e268c9f10 100644
--- a/components/script_bindings/codegen/Bindings.conf
+++ b/components/script_bindings/codegen/Bindings.conf
@@ -175,6 +175,10 @@ DOMInterfaces = {
'canGc': ['Abort'],
},
+'FontFaceSet': {
+ 'canGc': ['Load'],
+},
+
'GamepadHapticActuator': {
'inRealms': ['PlayEffect', 'Reset'],
'canGc': ['PlayEffect', 'Reset'],
@@ -577,6 +581,10 @@ DOMInterfaces = {
}
Dictionaries = {
+'FontFaceDescriptors': {
+ 'derives': ['Clone', 'MallocSizeOf']
+},
+
'GPUCanvasConfiguration': {
'derives': ['Clone']
},
diff --git a/components/script_bindings/webidls/FontFace.webidl b/components/script_bindings/webidls/FontFace.webidl
new file mode 100644
index 00000000000..e2b1cc7f02c
--- /dev/null
+++ b/components/script_bindings/webidls/FontFace.webidl
@@ -0,0 +1,54 @@
+/* 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 https://mozilla.org/MPL/2.0/. */
+
+typedef DOMString CSSOMString;
+
+dictionary FontFaceDescriptors {
+ CSSOMString style = "normal";
+ CSSOMString weight = "normal";
+ CSSOMString stretch = "normal";
+ CSSOMString unicodeRange = "U+0-10FFFF";
+ CSSOMString featureSettings = "normal";
+ CSSOMString variationSettings = "normal";
+ CSSOMString display = "auto";
+ CSSOMString ascentOverride = "normal";
+ CSSOMString descentOverride = "normal";
+ CSSOMString lineGapOverride = "normal";
+};
+
+enum FontFaceLoadStatus { "unloaded", "loading", "loaded", "error" };
+
+// https://drafts.csswg.org/css-font-loading/#fontface-interface
+[Exposed=(Window /*, Worker */), Pref="dom_fontface_enabled"] // TODO: Add support for FontFace in Workers.
+interface FontFace {
+ constructor(CSSOMString family, (CSSOMString or BufferSource) source,
+ optional FontFaceDescriptors descriptors = {});
+ [SetterThrows]
+ attribute CSSOMString family;
+ [SetterThrows]
+ attribute CSSOMString style;
+ [SetterThrows]
+ attribute CSSOMString weight;
+ [SetterThrows]
+ attribute CSSOMString stretch;
+ [SetterThrows]
+ attribute CSSOMString unicodeRange;
+ [SetterThrows]
+ attribute CSSOMString featureSettings;
+ [SetterThrows]
+ attribute CSSOMString variationSettings;
+ [SetterThrows]
+ attribute CSSOMString display;
+ [SetterThrows]
+ attribute CSSOMString ascentOverride;
+ [SetterThrows]
+ attribute CSSOMString descentOverride;
+ [SetterThrows]
+ attribute CSSOMString lineGapOverride;
+
+ readonly attribute FontFaceLoadStatus status;
+
+ Promise<FontFace> load();
+ readonly attribute Promise<FontFace> loaded;
+};
diff --git a/components/script_bindings/webidls/FontFaceSet.webidl b/components/script_bindings/webidls/FontFaceSet.webidl
index f114b2ac9b4..1bac3489ef6 100644
--- a/components/script_bindings/webidls/FontFaceSet.webidl
+++ b/components/script_bindings/webidls/FontFaceSet.webidl
@@ -19,12 +19,13 @@ enum FontFaceSetLoadStatus { "loading" , "loaded" };
*/
// https://drafts.csswg.org/css-font-loading/#FontFaceSet-interface
-[Exposed=(Window,Worker)]
+[Exposed=(Window /*, Worker */)]
interface FontFaceSet : EventTarget {
// constructor(sequence<FontFace> initialFaces);
// setlike<FontFace>;
- // FontFaceSet add(FontFace font);
+ [Pref="dom_fontface_enabled"]
+ FontFaceSet add(FontFace font);
// boolean delete(FontFace font);
// undefined clear();
@@ -35,7 +36,8 @@ interface FontFaceSet : EventTarget {
// check and start loads if appropriate
// and fulfill promise when all loads complete
- // Promise<sequence<FontFace>> load(DOMString font, optional DOMString text = " ");
+ [Pref="dom_fontface_enabled"]
+ Promise<sequence<FontFace>> load(DOMString font, optional DOMString text = " ");
// return whether all fonts in the fontlist are loaded
// (does not initiate load if not available)
diff --git a/components/shared/background_hang_monitor/lib.rs b/components/shared/background_hang_monitor/lib.rs
index ae7e587226a..223ace39c4b 100644
--- a/components/shared/background_hang_monitor/lib.rs
+++ b/components/shared/background_hang_monitor/lib.rs
@@ -21,6 +21,7 @@ pub enum ScriptHangAnnotation {
DevtoolsMsg,
DocumentEvent,
FileRead,
+ FontLoading,
FormPlannedNavigation,
ImageCacheMsg,
InputEvent,
diff --git a/components/shared/fonts/lib.rs b/components/shared/fonts/lib.rs
index 032dcfd19e0..9ba8d9425ee 100644
--- a/components/shared/fonts/lib.rs
+++ b/components/shared/fonts/lib.rs
@@ -17,4 +17,4 @@ int_range_index! {
struct ByteIndex(isize)
}
-pub type WebFontLoadFinishedCallback = Arc<dyn Fn(bool) + Send + Sync + 'static>;
+pub type StylesheetWebFontLoadFinishedCallback = Arc<dyn Fn(bool) + Send + Sync + 'static>;
diff --git a/components/shared/net/lib.rs b/components/shared/net/lib.rs
index bbdad23fa6f..b6c44cad75f 100644
--- a/components/shared/net/lib.rs
+++ b/components/shared/net/lib.rs
@@ -524,7 +524,7 @@ enum ToFetchThreadMessage {
FetchResponse(FetchResponseMsg),
}
-pub type BoxedFetchCallback = Box<dyn Fn(FetchResponseMsg) + Send + 'static>;
+pub type BoxedFetchCallback = Box<dyn FnMut(FetchResponseMsg) + Send + 'static>;
/// A thread to handle fetches in a Servo process. This thread is responsible for
/// listening for new fetch requests as well as updates on those operations and forwarding
@@ -601,7 +601,7 @@ impl FetchThread {
matches!(fetch_response_msg, FetchResponseMsg::ProcessResponseEOF(..));
self.active_fetches
- .get(&request_id)
+ .get_mut(&request_id)
.expect("Got fetch response for unknown fetch")(
fetch_response_msg
);
diff --git a/components/shared/profile/time.rs b/components/shared/profile/time.rs
index ccb842a2224..78c380b70dc 100644
--- a/components/shared/profile/time.rs
+++ b/components/shared/profile/time.rs
@@ -90,43 +90,44 @@ pub enum ProfilerCategory {
ScriptEvent = 0x66,
ScriptFileRead = 0x67,
- ScriptImageCacheMsg = 0x68,
- ScriptInputEvent = 0x69,
- ScriptNetworkEvent = 0x6a,
+ ScriptFontLoading = 0x68,
+ ScriptImageCacheMsg = 0x69,
+ ScriptInputEvent = 0x6a,
+ ScriptNetworkEvent = 0x6b,
/// The script thread is parsing HTML, rather than doing other work like evaluating scripts or doing layout.
- ScriptParseHTML = 0x6b,
-
- ScriptPlannedNavigation = 0x6c,
- ScriptResize = 0x6d,
- ScriptRendering = 0x6e,
- ScriptSetScrollState = 0x6f,
- ScriptSetViewport = 0x70,
- ScriptTimerEvent = 0x71,
- ScriptStylesheetLoad = 0x72,
- ScriptUpdateReplacedElement = 0x73,
- ScriptWebSocketEvent = 0x74,
- ScriptWorkerEvent = 0x75,
- ScriptServiceWorkerEvent = 0x76,
+ ScriptParseHTML = 0x6c,
+
+ ScriptPlannedNavigation = 0x6d,
+ ScriptResize = 0x6e,
+ ScriptRendering = 0x6f,
+ ScriptSetScrollState = 0x70,
+ ScriptSetViewport = 0x71,
+ ScriptTimerEvent = 0x72,
+ ScriptStylesheetLoad = 0x73,
+ ScriptUpdateReplacedElement = 0x74,
+ ScriptWebSocketEvent = 0x75,
+ ScriptWorkerEvent = 0x76,
+ ScriptServiceWorkerEvent = 0x77,
/// The script thread is parsing XML, rather than doing other work like evaluating scripts or doing layout.
- ScriptParseXML = 0x77,
+ ScriptParseXML = 0x78,
- ScriptEnterFullscreen = 0x78,
- ScriptExitFullscreen = 0x79,
- ScriptWorkletEvent = 0x7a,
- ScriptPerformanceEvent = 0x7b,
- ScriptHistoryEvent = 0x7c,
- ScriptPortMessage = 0x7d,
- ScriptWebGPUMsg = 0x7e,
+ ScriptEnterFullscreen = 0x79,
+ ScriptExitFullscreen = 0x7a,
+ ScriptWorkletEvent = 0x7b,
+ ScriptPerformanceEvent = 0x7c,
+ ScriptHistoryEvent = 0x7d,
+ ScriptPortMessage = 0x7e,
+ ScriptWebGPUMsg = 0x7f,
/// Web performance metrics.
- TimeToFirstPaint = 0x80,
- TimeToFirstContentfulPaint = 0x81,
- TimeToInteractive = 0x82,
+ TimeToFirstPaint = 0x90,
+ TimeToFirstContentfulPaint = 0x91,
+ TimeToInteractive = 0x92,
- IpcReceiver = 0x83,
- IpcBytesReceiver = 0x84,
+ IpcReceiver = 0x93,
+ IpcBytesReceiver = 0x94,
}
impl ProfilerCategory {
@@ -151,6 +152,7 @@ impl ProfilerCategory {
ProfilerCategory::ScriptEvaluate => "ScriptEvaluate",
ProfilerCategory::ScriptEvent => "ScriptEvent",
ProfilerCategory::ScriptFileRead => "ScriptFileRead",
+ ProfilerCategory::ScriptFontLoading => "ScriptFontLoading",
ProfilerCategory::ScriptImageCacheMsg => "ScriptImageCacheMsg",
ProfilerCategory::ScriptInputEvent => "ScriptInputEvent",
ProfilerCategory::ScriptNetworkEvent => "ScriptNetworkEvent",
diff --git a/tests/wpt/meta/__dir__.ini b/tests/wpt/meta/__dir__.ini
index b5b2db98287..f69cd94922b 100644
--- a/tests/wpt/meta/__dir__.ini
+++ b/tests/wpt/meta/__dir__.ini
@@ -1 +1 @@
-prefs: ["dom_imagebitmap_enabled:true", "dom_offscreen_canvas_enabled:true", "dom_shadowdom_enabled:true", "dom_xpath_enabled:true", "dom_intersection_observer_enabled:true", "dom_resize_observer_enabled:true", "dom_notification_enabled:true"]
+prefs: ["dom_imagebitmap_enabled:true", "dom_offscreen_canvas_enabled:true", "dom_shadowdom_enabled:true", "dom_xpath_enabled:true", "dom_intersection_observer_enabled:true", "dom_resize_observer_enabled:true", "dom_notification_enabled:true", "dom_fontface_enabled:true"]
diff --git a/tests/wpt/meta/css/css-cascade/layer-cssom-order-reverse.html.ini b/tests/wpt/meta/css/css-cascade/layer-cssom-order-reverse.html.ini
index 9bfa3e73791..6694f4405f2 100644
--- a/tests/wpt/meta/css/css-cascade/layer-cssom-order-reverse.html.ini
+++ b/tests/wpt/meta/css/css-cascade/layer-cssom-order-reverse.html.ini
@@ -1,6 +1,3 @@
[layer-cssom-order-reverse.html]
- [Insert layer invalidates @font-face]
- expected: FAIL
-
[Delete layer invalidates @font-face]
expected: FAIL
diff --git a/tests/wpt/meta/css/css-cascade/layer-font-face-override.html.ini b/tests/wpt/meta/css/css-cascade/layer-font-face-override.html.ini
index b453839c49f..6195b20bcec 100644
--- a/tests/wpt/meta/css/css-cascade/layer-font-face-override.html.ini
+++ b/tests/wpt/meta/css/css-cascade/layer-font-face-override.html.ini
@@ -1,9 +1,11 @@
[layer-font-face-override.html]
+ bug: https://github.com/servo/servo/issues/35520
+
[@font-face unlayered overrides layered]
- expected: FAIL
+ expected: [FAIL, PASS]
[@font-face override between layers]
- expected: FAIL
+ expected: [FAIL, PASS]
[@font-face override update with appended sheet 1]
expected: FAIL
diff --git a/tests/wpt/meta/css/css-fonts/fallback-remote-to-data-url.html.ini b/tests/wpt/meta/css/css-fonts/fallback-remote-to-data-url.html.ini
index 08229124c2f..28cbce9fd3a 100644
--- a/tests/wpt/meta/css/css-fonts/fallback-remote-to-data-url.html.ini
+++ b/tests/wpt/meta/css/css-fonts/fallback-remote-to-data-url.html.ini
@@ -1,2 +1,3 @@
[fallback-remote-to-data-url.html]
- expected: ERROR
+ [We should use the inline custom font to render the page when the primary remote font is loading]
+ expected: FAIL
diff --git a/tests/wpt/meta/css/css-fonts/font-display/font-display-change.html.ini b/tests/wpt/meta/css/css-fonts/font-display/font-display-change.html.ini
index 8643675f6b3..5e3c6fc0042 100644
--- a/tests/wpt/meta/css/css-fonts/font-display/font-display-change.html.ini
+++ b/tests/wpt/meta/css/css-fonts/font-display/font-display-change.html.ini
@@ -1,2 +1,3 @@
[font-display-change.html]
- expected: TIMEOUT
+ bug: https://github.com/servo/servo/issues/35521
+ expected: [FAIL, TIMEOUT, PASS]
diff --git a/tests/wpt/meta/css/css-fonts/format-specifiers-variations.html.ini b/tests/wpt/meta/css/css-fonts/format-specifiers-variations.html.ini
index 041f8eb820b..4cd865a3c24 100644
--- a/tests/wpt/meta/css/css-fonts/format-specifiers-variations.html.ini
+++ b/tests/wpt/meta/css/css-fonts/format-specifiers-variations.html.ini
@@ -1,16 +1,4 @@
[format-specifiers-variations.html]
- [Load Ahem with format woff]
- expected: FAIL
-
- [Load Ahem with format truetype]
- expected: FAIL
-
- [Load Ahem with format opentype]
- expected: FAIL
-
- [Load Ahem with format woff2]
- expected: FAIL
-
[Load Ahem with format woff-variations]
expected: FAIL
@@ -22,123 +10,3 @@
[Load Ahem with format woff2-variations]
expected: FAIL
-
- [Do not load Ahem with format xyzwoff]
- expected: FAIL
-
- [Do not load Ahem with format xyztruetype]
- expected: FAIL
-
- [Do not load Ahem with format xyzopentype]
- expected: FAIL
-
- [Do not load Ahem with format xyzwoff2]
- expected: FAIL
-
- [Do not load Ahem with format xyzwoff-variations]
- expected: FAIL
-
- [Do not load Ahem with format xyztruetype-variations]
- expected: FAIL
-
- [Do not load Ahem with format xyzopentype-variations]
- expected: FAIL
-
- [Do not load Ahem with format xyzwoff2-variations]
- expected: FAIL
-
- [Do not load Ahem with format woffxyz]
- expected: FAIL
-
- [Do not load Ahem with format truetypexyz]
- expected: FAIL
-
- [Do not load Ahem with format opentypexyz]
- expected: FAIL
-
- [Do not load Ahem with format woff2xyz]
- expected: FAIL
-
- [Do not load Ahem with format woff-variationsxyz]
- expected: FAIL
-
- [Do not load Ahem with format truetype-variationsxyz]
- expected: FAIL
-
- [Do not load Ahem with format opentype-variationsxyz]
- expected: FAIL
-
- [Do not load Ahem with format woff2-variationsxyz]
- expected: FAIL
-
- [Do not load Ahem with format wo]
- expected: FAIL
-
- [Do not load Ahem with format truety]
- expected: FAIL
-
- [Do not load Ahem with format openty]
- expected: FAIL
-
- [Do not load Ahem with format wof]
- expected: FAIL
-
- [Do not load Ahem with format woff-variatio]
- expected: FAIL
-
- [Do not load Ahem with format truetype-variatio]
- expected: FAIL
-
- [Do not load Ahem with format opentype-variatio]
- expected: FAIL
-
- [Do not load Ahem with format woff2-variatio]
- expected: FAIL
-
- [Do not load Ahem with format ff]
- expected: FAIL
-
- [Do not load Ahem with format uetype]
- expected: FAIL
-
- [Do not load Ahem with format entype]
- expected: FAIL
-
- [Do not load Ahem with format ff2]
- expected: FAIL
-
- [Do not load Ahem with format ff-variations]
- expected: FAIL
-
- [Do not load Ahem with format uetype-variations]
- expected: FAIL
-
- [Do not load Ahem with format entype-variations]
- expected: FAIL
-
- [Do not load Ahem with format ff2-variations]
- expected: FAIL
-
- [Do not load Ahem with format wff]
- expected: FAIL
-
- [Do not load Ahem with format tretype]
- expected: FAIL
-
- [Do not load Ahem with format opntype]
- expected: FAIL
-
- [Do not load Ahem with format wff2]
- expected: FAIL
-
- [Do not load Ahem with format woff-ariations]
- expected: FAIL
-
- [Do not load Ahem with format truetye-variations]
- expected: FAIL
-
- [Do not load Ahem with format opentye-variations]
- expected: FAIL
-
- [Do not load Ahem with format woff2variations]
- expected: FAIL
diff --git a/tests/wpt/meta/css/css-fonts/parsing/font-size-adjust-computed.html.ini b/tests/wpt/meta/css/css-fonts/parsing/font-size-adjust-computed.html.ini
index be39e77c50d..7236e490ccf 100644
--- a/tests/wpt/meta/css/css-fonts/parsing/font-size-adjust-computed.html.ini
+++ b/tests/wpt/meta/css/css-fonts/parsing/font-size-adjust-computed.html.ini
@@ -38,5 +38,20 @@
[Property font-size-adjust value 'ic-height from-font']
expected: FAIL
- [CSS Fonts Module Level 5: getComputedStyle().fontSizeAdjust]
+ [Property font-size-adjust value 'calc(0.5)']
+ expected: FAIL
+
+ [Property font-size-adjust value 'ex-height calc(0.5)']
+ expected: FAIL
+
+ [Property font-size-adjust value 'cap-height calc(0.5)']
+ expected: FAIL
+
+ [Property font-size-adjust value 'cap-height calc(0.5 + 1)']
+ expected: FAIL
+
+ [Property font-size-adjust value 'cap-height calc(-0.5)']
+ expected: FAIL
+
+ [Property font-size-adjust value 'cap-height calc(10 + (sign(20cqw - 10px) * 5))']
expected: FAIL
diff --git a/tests/wpt/meta/css/css-fonts/variations/font-parse-numeric-stretch-style-weight.html.ini b/tests/wpt/meta/css/css-fonts/variations/font-parse-numeric-stretch-style-weight.html.ini
deleted file mode 100644
index 13ae17b2eb2..00000000000
--- a/tests/wpt/meta/css/css-fonts/variations/font-parse-numeric-stretch-style-weight.html.ini
+++ /dev/null
@@ -1,156 +0,0 @@
-[font-parse-numeric-stretch-style-weight.html]
- [Valid value 100 matches 100 for weight in @font-face.]
- expected: FAIL
-
- [Valid value 700 matches 700 for weight in @font-face.]
- expected: FAIL
-
- [Valid value 900 matches 900 for weight in @font-face.]
- expected: FAIL
-
- [Valid value bold matches bold for weight in @font-face.]
- expected: FAIL
-
- [Valid value normal matches normal for weight in @font-face.]
- expected: FAIL
-
- [Valid value 100 400 matches 100 400 for weight in @font-face.]
- expected: FAIL
-
- [Valid value 100 101.5 matches 100 101.5 for weight in @font-face.]
- expected: FAIL
-
- [Valid value 999.8 999.9 matches 999.8 999.9 for weight in @font-face.]
- expected: FAIL
-
- [Valid value 500 400 matches 500 400 for weight in @font-face.]
- expected: FAIL
-
- [Valid value 0% matches 0% for stretch in @font-face.]
- expected: FAIL
-
- [Valid value calc(0% - 10%) matches calc(-10%) for stretch in @font-face.]
- expected: FAIL
-
- [Valid value 100% matches 100% for stretch in @font-face.]
- expected: FAIL
-
- [Valid value 110% matches 110% for stretch in @font-face.]
- expected: FAIL
-
- [Valid value 111.5% matches 111.5% for stretch in @font-face.]
- expected: FAIL
-
- [Valid value 50% 200% matches 50% 200% for stretch in @font-face.]
- expected: FAIL
-
- [Valid value 0.1% 1% matches 0.1% 1% for stretch in @font-face.]
- expected: FAIL
-
- [Valid value 900% 901% matches 900% 901% for stretch in @font-face.]
- expected: FAIL
-
- [Valid value ultra-condensed matches ultra-condensed for stretch in @font-face.]
- expected: FAIL
-
- [Valid value ultra-expanded matches ultra-expanded for stretch in @font-face.]
- expected: FAIL
-
- [Valid value normal matches normal for style in @font-face.]
- expected: FAIL
-
- [Valid value italic matches italic for style in @font-face.]
- expected: FAIL
-
- [Valid value oblique matches oblique for style in @font-face.]
- expected: FAIL
-
- [Valid value oblique 10deg matches oblique 10deg for style in @font-face.]
- expected: FAIL
-
- [Valid value oblique 10deg 20deg matches oblique 10deg 20deg for style in @font-face.]
- expected: FAIL
-
- [Value 0 must not be accepted as weight in @font-face.]
- expected: FAIL
-
- [Value 0.9 must not be accepted as weight in @font-face.]
- expected: FAIL
-
- [Value -100 200 must not be accepted as weight in @font-face.]
- expected: FAIL
-
- [Value 100 -200 must not be accepted as weight in @font-face.]
- expected: FAIL
-
- [Value 100 1001 must not be accepted as weight in @font-face.]
- expected: FAIL
-
- [Value 1001 must not be accepted as weight in @font-face.]
- expected: FAIL
-
- [Value 1000.5 must not be accepted as weight in @font-face.]
- expected: FAIL
-
- [Value 100 200 300 must not be accepted as weight in @font-face.]
- expected: FAIL
-
- [Value a must not be accepted as weight in @font-face.]
- expected: FAIL
-
- [Value a b c must not be accepted as weight in @font-face.]
- expected: FAIL
-
- [Value -0.5% must not be accepted as stretch in @font-face.]
- expected: FAIL
-
- [Value -1% must not be accepted as stretch in @font-face.]
- expected: FAIL
-
- [Value 60% 70% 80% must not be accepted as stretch in @font-face.]
- expected: FAIL
-
- [Value a% must not be accepted as stretch in @font-face.]
- expected: FAIL
-
- [Value a b c must not be accepted as stretch in @font-face.]
- expected: FAIL
-
- [Value 0.1 must not be accepted as stretch in @font-face.]
- expected: FAIL
-
- [Value -60% 80% must not be accepted as stretch in @font-face.]
- expected: FAIL
-
- [Value ultra-expannnned must not be accepted as stretch in @font-face.]
- expected: FAIL
-
- [Value 50% 0 must not be accepted as stretch in @font-face.]
- expected: FAIL
-
- [Value oblique 100deg must not be accepted as style in @font-face.]
- expected: FAIL
-
- [Value oblique italic must not be accepted as style in @font-face.]
- expected: FAIL
-
- [Value oblique -91deg must not be accepted as style in @font-face.]
- expected: FAIL
-
- [Value oblique 0 must not be accepted as style in @font-face.]
- expected: FAIL
-
- [Value oblique 10 must not be accepted as style in @font-face.]
- expected: FAIL
-
- [Value iiitalic must not be accepted as style in @font-face.]
- expected: FAIL
-
- [Value 90deg must not be accepted as style in @font-face.]
- expected: FAIL
-
- [Value 11 must not be accepted as style in @font-face.]
- expected: FAIL
-
- [Value italic 90deg must not be accepted as style in @font-face.]
- expected: FAIL
diff --git a/tests/wpt/meta/css/css-masking/clip-path/clip-path-svg-text-font-loading.html.ini b/tests/wpt/meta/css/css-masking/clip-path/clip-path-svg-text-font-loading.html.ini
index 103381380a3..3ea1cb4f41d 100644
--- a/tests/wpt/meta/css/css-masking/clip-path/clip-path-svg-text-font-loading.html.ini
+++ b/tests/wpt/meta/css/css-masking/clip-path/clip-path-svg-text-font-loading.html.ini
@@ -1,2 +1,2 @@
[clip-path-svg-text-font-loading.html]
- expected: TIMEOUT
+ expected: FAIL
diff --git a/tests/wpt/meta/css/css-values/ch-empty-pseudo-recalc-on-font-load.html.ini b/tests/wpt/meta/css/css-values/ch-empty-pseudo-recalc-on-font-load.html.ini
index e9b278c3ee8..64b77b15066 100644
--- a/tests/wpt/meta/css/css-values/ch-empty-pseudo-recalc-on-font-load.html.ini
+++ b/tests/wpt/meta/css/css-values/ch-empty-pseudo-recalc-on-font-load.html.ini
@@ -1,5 +1,4 @@
[ch-empty-pseudo-recalc-on-font-load.html]
- expected: ERROR
[ch in pseudo-element ::before should be recalculated after loading a web font]
expected: FAIL
diff --git a/tests/wpt/meta/css/css-values/ch-pseudo-recalc-on-font-load.html.ini b/tests/wpt/meta/css/css-values/ch-pseudo-recalc-on-font-load.html.ini
index 8bb7ac740bb..53b82763175 100644
--- a/tests/wpt/meta/css/css-values/ch-pseudo-recalc-on-font-load.html.ini
+++ b/tests/wpt/meta/css/css-values/ch-pseudo-recalc-on-font-load.html.ini
@@ -1,5 +1,4 @@
[ch-pseudo-recalc-on-font-load.html]
- expected: ERROR
[ch in pseudo-element ::before should be recalculated after loading a web font]
expected: FAIL
diff --git a/tests/wpt/meta/css/css-values/ch-recalc-on-font-load.html.ini b/tests/wpt/meta/css/css-values/ch-recalc-on-font-load.html.ini
deleted file mode 100644
index 507454f0afa..00000000000
--- a/tests/wpt/meta/css/css-values/ch-recalc-on-font-load.html.ini
+++ /dev/null
@@ -1,10 +0,0 @@
-[ch-recalc-on-font-load.html]
- expected: ERROR
- [ch in a normal div should be recalculated after loading a web font]
- expected: TIMEOUT
-
- [ch in display:contents should be recalculated after loading a web font]
- expected: TIMEOUT
-
- [ch in display:none should be recalculated after loading a web font]
- expected: TIMEOUT
diff --git a/tests/wpt/meta/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.html.ini b/tests/wpt/meta/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.html.ini
deleted file mode 100644
index 3e7da9f9a61..00000000000
--- a/tests/wpt/meta/html/canvas/offscreen/text/2d.text.draw.fill.maxWidth.fontface.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[2d.text.draw.fill.maxWidth.fontface.html]
- [fillText works on @font-face fonts]
- expected: FAIL
-
diff --git a/tests/wpt/meta/html/canvas/offscreen/text/2d.text.measure.width.empty.html.ini b/tests/wpt/meta/html/canvas/offscreen/text/2d.text.measure.width.empty.html.ini
index e7133922d3d..2d3a3958f5c 100644
--- a/tests/wpt/meta/html/canvas/offscreen/text/2d.text.measure.width.empty.html.ini
+++ b/tests/wpt/meta/html/canvas/offscreen/text/2d.text.measure.width.empty.html.ini
@@ -1,6 +1,3 @@
[2d.text.measure.width.empty.html]
[The empty string has zero width for OffscreenCanvas]
expected: FAIL
-
- [The empty string has zero width]
- expected: FAIL
diff --git a/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.condensed.html.ini b/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.condensed.html.ini
new file mode 100644
index 00000000000..f1389317ba4
--- /dev/null
+++ b/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.condensed.html.ini
@@ -0,0 +1,2 @@
+[canvas.2d.fontStretch.condensed.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.expanded.html.ini b/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.expanded.html.ini
new file mode 100644
index 00000000000..dc035a8d7a6
--- /dev/null
+++ b/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.expanded.html.ini
@@ -0,0 +1,2 @@
+[canvas.2d.fontStretch.expanded.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.extra-condensed.html.ini b/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.extra-condensed.html.ini
new file mode 100644
index 00000000000..b21e1ff7942
--- /dev/null
+++ b/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.extra-condensed.html.ini
@@ -0,0 +1,2 @@
+[canvas.2d.fontStretch.extra-condensed.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.extra-expanded.html.ini b/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.extra-expanded.html.ini
new file mode 100644
index 00000000000..2a3dae38d26
--- /dev/null
+++ b/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.extra-expanded.html.ini
@@ -0,0 +1,2 @@
+[canvas.2d.fontStretch.extra-expanded.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.normal.html.ini b/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.normal.html.ini
new file mode 100644
index 00000000000..06249acdefc
--- /dev/null
+++ b/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.normal.html.ini
@@ -0,0 +1,2 @@
+[canvas.2d.fontStretch.normal.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.semi-condensed.html.ini b/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.semi-condensed.html.ini
new file mode 100644
index 00000000000..82112139c28
--- /dev/null
+++ b/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.semi-condensed.html.ini
@@ -0,0 +1,2 @@
+[canvas.2d.fontStretch.semi-condensed.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.semi-expanded.html.ini b/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.semi-expanded.html.ini
new file mode 100644
index 00000000000..5d96cf42cd9
--- /dev/null
+++ b/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.semi-expanded.html.ini
@@ -0,0 +1,2 @@
+[canvas.2d.fontStretch.semi-expanded.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.ultra-condensed.html.ini b/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.ultra-condensed.html.ini
new file mode 100644
index 00000000000..4655dc92135
--- /dev/null
+++ b/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.ultra-condensed.html.ini
@@ -0,0 +1,2 @@
+[canvas.2d.fontStretch.ultra-condensed.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.ultra-expanded.html.ini b/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.ultra-expanded.html.ini
new file mode 100644
index 00000000000..118ea5fd38c
--- /dev/null
+++ b/tests/wpt/meta/html/canvas/offscreen/text/canvas.2d.fontStretch.ultra-expanded.html.ini
@@ -0,0 +1,2 @@
+[canvas.2d.fontStretch.ultra-expanded.html]
+ expected: FAIL
diff --git a/tests/wpt/meta/webidl/current-realm.html.ini b/tests/wpt/meta/webidl/current-realm.html.ini
index a2c668873b7..f8e0202efb1 100644
--- a/tests/wpt/meta/webidl/current-realm.html.ini
+++ b/tests/wpt/meta/webidl/current-realm.html.ini
@@ -4,6 +4,3 @@
[getImageData]
expected: FAIL
-
- [FontFace's load()]
- expected: FAIL
diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json
index e1671aa476a..63bd8acee17 100644
--- a/tests/wpt/mozilla/meta/MANIFEST.json
+++ b/tests/wpt/mozilla/meta/MANIFEST.json
@@ -13513,7 +13513,7 @@
]
],
"interfaces.worker.js": [
- "fc621bbafeec167942f802caae43b9f2ef23b29b",
+ "f708bfb25594a239be31671e9fd15d6771309a12",
[
"mozilla/interfaces.worker.html",
{}
diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js
index fc621bbafee..f708bfb2559 100644
--- a/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js
+++ b/tests/wpt/mozilla/tests/mozilla/interfaces.worker.js
@@ -34,7 +34,6 @@ test_interfaces([
"FileReader",
"FileReaderSync",
"FinalizationRegistry",
- "FontFaceSet",
"FormData",
"Headers",
"History",