diff options
author | Jon Leighton <j@jonathanleighton.com> | 2018-02-26 13:41:20 +0000 |
---|---|---|
committer | Jon Leighton <j@jonathanleighton.com> | 2018-05-19 14:33:36 +1000 |
commit | 691c6c6f1a50ca78850fc56d00c8759797c7cc96 (patch) | |
tree | 007d7942cb63a25ba723bb11a599060b1936002d | |
parent | 15a677c639d454fe2565fc7d1051965bc54ea45e (diff) | |
download | servo-691c6c6f1a50ca78850fc56d00c8759797c7cc96.tar.gz servo-691c6c6f1a50ca78850fc56d00c8759797c7cc96.zip |
Implement font fallback
Prior to this change, if none of the fonts specified in CSS contained a
glyph for a codepoint, we tried only one fallback font. If that font
didn't contain the glyph, we'd give up.
With this change, we try multiple fonts in turn. The font names we try
differ across each platform, and based on the codepoint we're trying to
match. The current implementation is heavily inspired by the analogous
code in Gecko, but I've used to ucd lib to make it more readable,
whereas Gecko matches raw unicode ranges.
This fixes some of the issues reported in #17267, although colour emoji
support is not implemented.
== Notes on changes to WPT metadata ==
=== css/css-text/i18n/css3-text-line-break-opclns-* ===
A bunch of these have started failing on macos when they previously
passed.
These tests check that the browser automatically inserts line breaks
near certain characters that are classified as "opening and closing
punctuation". The idea is that if we have e.g. an opening parenthesis,
it does not make sense for it to appear at the end of a line box; it
should "stick" to the next character and go into the next line box.
Before this change, a lot of these codepoints rendered as a missing
glyph on Mac and Linux. In some cases, that meant that the test was
passing.
After this change, a bunch of these codepoints are now rendering glyphs
on Mac (but not Linux). In some cases, the test should continue to pass
where it previously did when rendering with the missing glyph.
However, it seems this has also exposed a layout bug. The "ref" div in
these tests contains a <br> element, and it seems that this, combined
with these punctuation characters, makes the spacing between glyphs ever
so slightly different to the "test" div. (Speculation: might be
something to do with shaping?)
Therefore I've had to mark a bunch of these tests failing on mac.
=== css/css-text/i18n/css3-text-line-break-baspglwj-* ===
Some of these previously passed on Mac due to a missing glyph. Now that
we're rendering the correct glyph, they are failing.
=== css/css-text/word-break/word-break-normal-bo-000.html ===
The characters now render correctly on Mac, and the test is passing. But
we do not find a suitable fallback font on Linux, so it is still failing
on that platform.
=== css/css-text/word-break/word-break-break-all-007.html ===
This was previously passing on Mac, but only because missing character
glyphs were rendered. Now that a fallback font is able to be found, it
(correctly) fails.
=== mozilla/tests/css/font_fallback_* ===
These are new tests added in this commit. 01 and 02 are marked failing
on Linux because the builders don't have the appropriate fonts installed
(that will be a follow-up).
Fix build errors from rebase
FontTemplateDescriptor can no longer just derive(Hash). We need to
implement it on each component part, because the components now
generally wrap floats, which do not impl Hash because of NaN. However in
this case we know that we won't have a NaN, so it is safe to manually
impl Hash.
81 files changed, 1187 insertions, 355 deletions
diff --git a/Cargo.lock b/Cargo.lock index fda43b567a4..b24fd443ae2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1051,6 +1051,7 @@ dependencies = [ "style 0.0.1", "time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "truetype 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ucd 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-bidi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-script 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "webrender_api 0.57.2 (git+https://github.com/servo/webrender)", @@ -3320,6 +3321,11 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] +name = "ucd" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] name = "uluru" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -4071,6 +4077,7 @@ dependencies = [ "checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" "checksum truetype 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)" = "acec30350633d6dac9dc1a625786b6cbe9150664be941aac2c35ad7199eab877" "checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" +"checksum ucd 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fe4fa6e588762366f1eb4991ce59ad1b93651d0b769dfb4e4d1c5c4b943d1159" "checksum uluru 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "519130f0ea964ba540a9d8af1373738c2226f1d465eda07e61db29feb5479db9" "checksum unicase 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "13a5906ca2b98c799f4b1ab4557b76367ebd6ae5ef14930ec841c74aed5f3764" "checksum unicode-bidi 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a6a2c4e3710edd365cd7e78383153ed739fa31af19f9172f72d3575060f5a43a" diff --git a/components/gfx/Cargo.toml b/components/gfx/Cargo.toml index eed47db1458..6ea0d21e2a4 100644 --- a/components/gfx/Cargo.toml +++ b/components/gfx/Cargo.toml @@ -42,6 +42,7 @@ unicode-bidi = {version = "0.3", features = ["with_serde"]} unicode-script = {version = "0.2", features = ["harfbuzz"]} webrender_api = {git = "https://github.com/servo/webrender", features = ["ipc"]} xi-unicode = "0.1.0" +ucd = "0.1.1" [target.'cfg(target_os = "macos")'.dependencies] byteorder = "1.0" diff --git a/components/gfx/font.rs b/components/gfx/font.rs index 2cf92e6a328..f4864448a37 100644 --- a/components/gfx/font.rs +++ b/components/gfx/font.rs @@ -9,12 +9,14 @@ use font_template::FontTemplateDescriptor; use ordered_float::NotNaN; use platform::font::{FontHandle, FontTable}; use platform::font_context::FontContextHandle; +pub use platform::font_list::fallback_font_families; use platform::font_template::FontTemplateData; use servo_atoms::Atom; use smallvec::SmallVec; use std::borrow::ToOwned; use std::cell::RefCell; use std::collections::HashMap; +use std::iter; use std::rc::Rc; use std::str; use std::sync::Arc; @@ -117,7 +119,7 @@ pub struct FontMetrics { /// template at a particular size, with a particular font-variant-caps applied, etc. This contrasts /// with `FontTemplateDescriptor` in that the latter represents only the parameters inherent in the /// font data (weight, stretch, etc.). -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct FontDescriptor { pub template_descriptor: FontTemplateDescriptor, pub variant: font_variant_caps::T, @@ -329,6 +331,7 @@ pub type FontRef = Rc<RefCell<Font>>; pub struct FontGroup { descriptor: FontDescriptor, families: SmallVec<[FontGroupFamily; 8]>, + last_matching_fallback: Option<FontRef>, } impl FontGroup { @@ -337,10 +340,14 @@ impl FontGroup { let families = style.font_family.0.iter() - .map(|family| FontGroupFamily::new(descriptor.clone(), family.clone())) + .map(|family| FontGroupFamily::new(descriptor.clone(), &family)) .collect(); - FontGroup { descriptor, families } + FontGroup { + descriptor, + families, + last_matching_fallback: None, + } } /// Finds the first font, or else the first fallback font, which contains a glyph for @@ -352,15 +359,35 @@ impl FontGroup { mut font_context: &mut FontContext<S>, codepoint: char ) -> Option<FontRef> { - self.find(&mut font_context, |font| font.borrow().has_glyph_for(codepoint)) - .or_else(|| self.first(&mut font_context)) + let has_glyph = |font: &FontRef| font.borrow().has_glyph_for(codepoint); + + let font = self.find(&mut font_context, |font| has_glyph(font)); + if font.is_some() { + return font + } + + if let Some(ref fallback) = self.last_matching_fallback { + if has_glyph(&fallback) { + return self.last_matching_fallback.clone() + } + } + + let font = self.find_fallback(&mut font_context, Some(codepoint), has_glyph); + if font.is_some() { + self.last_matching_fallback = font.clone(); + return font + } + + self.first(&mut font_context) } + /// Find the first available font in the group, or the first available fallback font. pub fn first<S: FontSource>( &mut self, mut font_context: &mut FontContext<S> ) -> Option<FontRef> { self.find(&mut font_context, |_| true) + .or_else(|| self.find_fallback(&mut font_context, None, |_| true)) } /// Find a font which returns true for `predicate`. This method mutates because we may need to @@ -368,19 +395,42 @@ impl FontGroup { fn find<S, P>( &mut self, mut font_context: &mut FontContext<S>, - mut predicate: P + predicate: P, ) -> Option<FontRef> where S: FontSource, - P: FnMut(&FontRef) -> bool + P: FnMut(&FontRef) -> bool, { self.families.iter_mut() .filter_map(|family| family.font(&mut font_context)) - .find(|f| predicate(f)) - .or_else(|| { - font_context.fallback_font(&self.descriptor) - .into_iter().find(predicate) - }) + .find(predicate) + } + + /// Attempts to find a suitable fallback font which matches the `predicate`. The default + /// family (i.e. "serif") will be tried first, followed by platform-specific family names. + /// If a `codepoint` is provided, then its Unicode block may be used to refine the list of + /// family names which will be tried. + fn find_fallback<S, P>( + &mut self, + font_context: &mut FontContext<S>, + codepoint: Option<char>, + predicate: P, + ) -> Option<FontRef> + where + S: FontSource, + P: FnMut(&FontRef) -> bool, + { + iter::once(FontFamilyDescriptor::default()) + .chain( + fallback_font_families(codepoint).into_iter().map(|family| { + FontFamilyDescriptor::new( + FontFamilyName::from(family), + FontSearchScope::Local, + ) + }) + ) + .filter_map(|family| font_context.font(&self.descriptor, &family)) + .find(predicate) } } @@ -389,17 +439,22 @@ impl FontGroup { /// only if actually needed. #[derive(Debug)] struct FontGroupFamily { - descriptor: FontDescriptor, - family: SingleFontFamily, + font_descriptor: FontDescriptor, + family_descriptor: FontFamilyDescriptor, loaded: bool, font: Option<FontRef>, } impl FontGroupFamily { - fn new(descriptor: FontDescriptor, family: SingleFontFamily) -> FontGroupFamily { + fn new(font_descriptor: FontDescriptor, family: &SingleFontFamily) -> FontGroupFamily { + let family_descriptor = FontFamilyDescriptor::new( + FontFamilyName::from(family), + FontSearchScope::Any + ); + FontGroupFamily { - descriptor, - family, + font_descriptor, + family_descriptor, loaded: false, font: None, } @@ -410,7 +465,7 @@ impl FontGroupFamily { /// subsequent calls. fn font<S: FontSource>(&mut self, font_context: &mut FontContext<S>) -> Option<FontRef> { if !self.loaded { - self.font = font_context.font(&self.descriptor, &self.family); + self.font = font_context.font(&self.font_descriptor, &self.family_descriptor); self.loaded = true; } @@ -451,3 +506,74 @@ pub fn get_and_reset_text_shaping_performance_counter() -> usize { TEXT_SHAPING_PERFORMANCE_COUNTER.store(0, Ordering::SeqCst); value } + +/// The scope within which we will look for a font. +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub enum FontSearchScope { + /// All fonts will be searched, including those specified via `@font-face` rules. + Any, + + /// Only local system fonts will be searched. + Local, +} + +/// A font family name used in font selection. +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub enum FontFamilyName { + /// A specific name such as `"Arial"` + Specific(Atom), + + /// A generic name such as `sans-serif` + Generic(Atom), +} + +impl FontFamilyName { + pub fn name(&self) -> &str { + match *self { + FontFamilyName::Specific(ref name) => name, + FontFamilyName::Generic(ref name) => name, + } + } +} + +impl<'a> From<&'a SingleFontFamily> for FontFamilyName { + fn from(other: &'a SingleFontFamily) -> FontFamilyName { + match *other { + SingleFontFamily::FamilyName(ref family_name) => + FontFamilyName::Specific(family_name.name.clone()), + + SingleFontFamily::Generic(ref generic_name) => + FontFamilyName::Generic(generic_name.clone()), + } + } +} + +impl<'a> From<&'a str> for FontFamilyName { + fn from(other: &'a str) -> FontFamilyName { + FontFamilyName::Specific(Atom::from(other)) + } +} + +/// The font family parameters for font selection. +#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] +pub struct FontFamilyDescriptor { + pub name: FontFamilyName, + pub scope: FontSearchScope, +} + +impl FontFamilyDescriptor { + fn new(name: FontFamilyName, scope: FontSearchScope) -> FontFamilyDescriptor { + FontFamilyDescriptor { name, scope } + } + + fn default() -> FontFamilyDescriptor { + FontFamilyDescriptor { + name: FontFamilyName::Generic(atom!("serif")), + scope: FontSearchScope::Local, + } + } + + pub fn name(&self) -> &str { + self.name.name() + } +} diff --git a/components/gfx/font_cache_thread.rs b/components/gfx/font_cache_thread.rs index 39375191ae1..523837c78fb 100644 --- a/components/gfx/font_cache_thread.rs +++ b/components/gfx/font_cache_thread.rs @@ -3,6 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use app_units::Au; +use font::{FontFamilyDescriptor, FontFamilyName, FontSearchScope}; use font_context::FontSource; use font_template::{FontTemplate, FontTemplateDescriptor}; use fontsan; @@ -13,7 +14,6 @@ use platform::font_context::FontContextHandle; use platform::font_list::SANS_SERIF_FONT_FAMILY; use platform::font_list::for_each_available_family; use platform::font_list::for_each_variation; -use platform::font_list::last_resort_font_families; use platform::font_list::system_default_family; use platform::font_template::FontTemplateData; use servo_atoms::Atom; @@ -24,7 +24,7 @@ use std::collections::HashMap; use std::ops::Deref; use std::sync::{Arc, Mutex}; use style::font_face::{EffectiveSources, Source}; -use style::values::computed::font::{SingleFontFamily, FamilyName}; +use style::values::computed::font::FamilyName; use webrender_api; /// A list of font templates that make up a given font family. @@ -103,8 +103,7 @@ impl FontTemplates { /// Commands that the FontContext sends to the font cache thread. #[derive(Debug, Deserialize, Serialize)] pub enum Command { - GetFontTemplate(SingleFontFamily, FontTemplateDescriptor, IpcSender<Reply>), - GetLastResortFontTemplate(FontTemplateDescriptor, IpcSender<Reply>), + GetFontTemplate(FontTemplateDescriptor, FontFamilyDescriptor, IpcSender<Reply>), GetFontInstance(webrender_api::FontKey, Au, IpcSender<webrender_api::FontInstanceKey>), AddWebFont(LowercaseString, EffectiveSources, IpcSender<()>), AddDownloadedWebFont(LowercaseString, ServoUrl, Vec<u8>, IpcSender<()>), @@ -123,7 +122,7 @@ pub enum Reply { struct FontCache { port: IpcReceiver<Command>, channel_to_self: IpcSender<Command>, - generic_fonts: HashMap<SingleFontFamily, LowercaseString>, + generic_fonts: HashMap<FontFamilyName, LowercaseString>, local_families: HashMap<LowercaseString, FontTemplates>, web_families: HashMap<LowercaseString, FontTemplates>, font_context: FontContextHandle, @@ -133,27 +132,28 @@ struct FontCache { font_instances: HashMap<(webrender_api::FontKey, Au), webrender_api::FontInstanceKey>, } -fn populate_generic_fonts() -> HashMap<SingleFontFamily, LowercaseString> { +fn populate_generic_fonts() -> HashMap<FontFamilyName, LowercaseString> { let mut generic_fonts = HashMap::with_capacity(5); - append_map(&mut generic_fonts, SingleFontFamily::Generic(atom!("serif")), "Times New Roman"); - append_map(&mut generic_fonts, SingleFontFamily::Generic(atom!("sans-serif")), SANS_SERIF_FONT_FAMILY); - append_map(&mut generic_fonts, SingleFontFamily::Generic(atom!("cursive")), "Apple Chancery"); - append_map(&mut generic_fonts, SingleFontFamily::Generic(atom!("fantasy")), "Papyrus"); - append_map(&mut generic_fonts, SingleFontFamily::Generic(atom!("monospace")), "Menlo"); - - fn append_map(generic_fonts: &mut HashMap<SingleFontFamily, LowercaseString>, - font_family: SingleFontFamily, - mapped_name: &str) { - let family_name = { - let opt_system_default = system_default_family(font_family.name()); - match opt_system_default { - Some(system_default) => LowercaseString::new(&system_default), - None => LowercaseString::new(mapped_name) - } + append_map(&mut generic_fonts, "serif", "Times New Roman"); + append_map(&mut generic_fonts, "sans-serif", SANS_SERIF_FONT_FAMILY); + append_map(&mut generic_fonts, "cursive", "Apple Chancery"); + append_map(&mut generic_fonts, "fantasy", "Papyrus"); + append_map(&mut generic_fonts, "monospace", "Menlo"); + + fn append_map( + generic_fonts: &mut HashMap<FontFamilyName, LowercaseString>, + generic_name: &str, + mapped_name: &str, + ) { + let family_name = match system_default_family(generic_name) { + Some(system_default) => LowercaseString::new(&system_default), + None => LowercaseString::new(mapped_name) }; - generic_fonts.insert(font_family, family_name); + let generic_name = FontFamilyName::Generic(Atom::from(generic_name)); + + generic_fonts.insert(generic_name, family_name); } @@ -166,14 +166,10 @@ impl FontCache { let msg = self.port.recv().unwrap(); match msg { - Command::GetFontTemplate(family, descriptor, result) => { - let maybe_font_template = self.find_font_template(&family, &descriptor); + Command::GetFontTemplate(template_descriptor, family_descriptor, result) => { + let maybe_font_template = self.find_font_template(&template_descriptor, &family_descriptor); let _ = result.send(Reply::GetFontTemplateReply(maybe_font_template)); } - Command::GetLastResortFontTemplate(descriptor, result) => { - let font_template = self.last_resort_font_template(&descriptor); - let _ = result.send(Reply::GetFontTemplateReply(Some(font_template))); - } Command::GetFontInstance(font_key, size, result) => { let webrender_api = &self.webrender_api; @@ -320,23 +316,28 @@ impl FontCache { }); } - fn transform_family(&self, family: &SingleFontFamily) -> LowercaseString { - match self.generic_fonts.get(family) { - None => LowercaseString::new(family.name()), + fn transform_family(&self, family_name: &FontFamilyName) -> LowercaseString { + match self.generic_fonts.get(family_name) { + None => LowercaseString::from(family_name), Some(mapped_family) => (*mapped_family).clone() } } - fn find_font_in_local_family(&mut self, family_name: &LowercaseString, desc: &FontTemplateDescriptor) - -> Option<Arc<FontTemplateData>> { + fn find_font_in_local_family( + &mut self, + template_descriptor: &FontTemplateDescriptor, + family_name: &FontFamilyName, + ) -> Option<Arc<FontTemplateData>> { + let family_name = self.transform_family(family_name); + // TODO(Issue #188): look up localized font family names if canonical name not found // look up canonical name - if self.local_families.contains_key(family_name) { - debug!("FontList: Found font family with name={}", &**family_name); - let s = self.local_families.get_mut(family_name).unwrap(); + if self.local_families.contains_key(&family_name) { + debug!("FontList: Found font family with name={}", &*family_name); + let s = self.local_families.get_mut(&family_name).unwrap(); if s.templates.is_empty() { - for_each_variation(family_name, |path| { + for_each_variation(&family_name, |path| { s.add_template(Atom::from(&*path), None); }); } @@ -344,20 +345,23 @@ impl FontCache { // TODO(Issue #192: handle generic font families, like 'serif' and 'sans-serif'. // if such family exists, try to match style to a font - s.find_font_for_style(desc, &self.font_context) + s.find_font_for_style(template_descriptor, &self.font_context) } else { - debug!("FontList: Couldn't find font family with name={}", &**family_name); + debug!("FontList: Couldn't find font family with name={}", &*family_name); None } } - fn find_font_in_web_family(&mut self, family: &SingleFontFamily, desc: &FontTemplateDescriptor) - -> Option<Arc<FontTemplateData>> { - let family_name = LowercaseString::new(family.name()); + fn find_font_in_web_family( + &mut self, + template_descriptor: &FontTemplateDescriptor, + family_name: &FontFamilyName, + ) -> Option<Arc<FontTemplateData>> { + let family_name = LowercaseString::from(family_name); if self.web_families.contains_key(&family_name) { let templates = self.web_families.get_mut(&family_name).unwrap(); - templates.find_font_for_style(desc, &self.font_context) + templates.find_font_for_style(template_descriptor, &self.font_context) } else { None } @@ -385,32 +389,21 @@ impl FontCache { } } - fn find_font_template(&mut self, family: &SingleFontFamily, desc: &FontTemplateDescriptor) - -> Option<FontTemplateInfo> { - let template = self.find_font_in_web_family(family, desc) - .or_else(|| { - let transformed_family = self.transform_family(family); - self.find_font_in_local_family(&transformed_family, desc) - }); - - template.map(|template| { - self.get_font_template_info(template) - }) - } - - fn last_resort_font_template(&mut self, desc: &FontTemplateDescriptor) - -> FontTemplateInfo { - let last_resort = last_resort_font_families(); - - for family in &last_resort { - let family = LowercaseString::new(family); - let maybe_font_in_family = self.find_font_in_local_family(&family, desc); - if let Some(family) = maybe_font_in_family { - return self.get_font_template_info(family) + fn find_font_template( + &mut self, + template_descriptor: &FontTemplateDescriptor, + family_descriptor: &FontFamilyDescriptor, + ) -> Option<FontTemplateInfo> { + match family_descriptor.scope { + FontSearchScope::Any => { + self.find_font_in_web_family(&template_descriptor, &family_descriptor.name) + .or_else(|| self.find_font_in_local_family(&template_descriptor, &family_descriptor.name)) } - } - panic!("Unable to find any fonts that match (do you have fallback fonts installed?)"); + FontSearchScope::Local => { + self.find_font_in_local_family(&template_descriptor, &family_descriptor.name) + } + }.map(|t| self.get_font_template_info(t)) } } @@ -480,11 +473,14 @@ impl FontSource for FontCacheThread { instance_key.unwrap() } - fn find_font_template(&mut self, family: SingleFontFamily, desc: FontTemplateDescriptor) - -> Option<FontTemplateInfo> { + fn font_template( + &mut self, + template_descriptor: FontTemplateDescriptor, + family_descriptor: FontFamilyDescriptor, + ) -> Option<FontTemplateInfo> { let (response_chan, response_port) = ipc::channel().expect("failed to create IPC channel"); - self.chan.send(Command::GetFontTemplate(family, desc, response_chan)) + self.chan.send(Command::GetFontTemplate(template_descriptor, family_descriptor, response_chan)) .expect("failed to send message to font cache thread"); let reply = response_port.recv(); @@ -501,27 +497,6 @@ impl FontSource for FontCacheThread { } } } - - fn last_resort_font_template(&mut self, desc: FontTemplateDescriptor) - -> FontTemplateInfo { - let (response_chan, response_port) = - ipc::channel().expect("failed to create IPC channel"); - self.chan.send(Command::GetLastResortFontTemplate(desc, response_chan)) - .expect("failed to send message to font cache thread"); - - let reply = response_port.recv(); - if reply.is_err() { - let font_thread_has_closed = self.chan.send(Command::Ping).is_err(); - assert!(font_thread_has_closed, "Failed to receive a response from live font cache"); - panic!("Font cache thread has already exited."); - } - - match reply.unwrap() { - Reply::GetFontTemplateReply(data) => { - data.unwrap() - } - } - } } #[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] @@ -537,6 +512,12 @@ impl LowercaseString { } } +impl<'a> From<&'a FontFamilyName> for LowercaseString { + fn from(family_name: &'a FontFamilyName) -> LowercaseString { + LowercaseString::new(family_name.name()) + } +} + impl Deref for LowercaseString { type Target = str; diff --git a/components/gfx/font_context.rs b/components/gfx/font_context.rs index a680f59dc44..6567e223e86 100644 --- a/components/gfx/font_context.rs +++ b/components/gfx/font_context.rs @@ -4,14 +4,13 @@ use app_units::Au; use fnv::FnvHasher; -use font::{Font, FontDescriptor, FontGroup, FontHandleMethods, FontRef}; +use font::{Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontHandleMethods, FontRef}; use font_cache_thread::FontTemplateInfo; use font_template::FontTemplateDescriptor; use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use platform::font::FontHandle; pub use platform::font_context::FontContextHandle; use servo_arc::Arc; -use servo_atoms::Atom; use std::cell::RefCell; use std::collections::HashMap; use std::default::Default; @@ -20,42 +19,10 @@ use std::rc::Rc; use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; use style::computed_values::font_variant_caps::T as FontVariantCaps; use style::properties::style_structs::Font as FontStyleStruct; -use style::values::computed::font::SingleFontFamily; use webrender_api; static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h) -#[derive(Debug)] -struct FontCacheEntry { - family: Atom, - font: Option<FontRef>, -} - -impl FontCacheEntry { - fn matches(&self, descriptor: &FontDescriptor, family: &SingleFontFamily) -> bool { - if self.family != *family.atom() { - return false - } - - if let Some(ref font) = self.font { - (*font).borrow().descriptor == *descriptor - } else { - true - } - } -} - -#[derive(Debug)] -struct FallbackFontCacheEntry { - font: FontRef, -} - -impl FallbackFontCacheEntry { - fn matches(&self, descriptor: &FontDescriptor) -> bool { - self.font.borrow().descriptor == *descriptor - } -} - /// An epoch for the font context cache. The cache is flushed if the current epoch does not match /// this one. static FONT_CACHE_EPOCH: AtomicUsize = ATOMIC_USIZE_INIT; @@ -63,13 +30,11 @@ static FONT_CACHE_EPOCH: AtomicUsize = ATOMIC_USIZE_INIT; pub trait FontSource { fn get_font_instance(&mut self, key: webrender_api::FontKey, size: Au) -> webrender_api::FontInstanceKey; - fn find_font_template( + fn font_template( &mut self, - family: SingleFontFamily, - desc: FontTemplateDescriptor + template_descriptor: FontTemplateDescriptor, + family_descriptor: FontFamilyDescriptor, ) -> Option<FontTemplateInfo>; - - fn last_resort_font_template(&mut self, desc: FontTemplateDescriptor) -> FontTemplateInfo; } /// The FontContext represents the per-thread/thread state necessary for @@ -84,10 +49,7 @@ pub struct FontContext<S: FontSource> { // TODO: The font context holds a strong ref to the cached fonts // so they will never be released. Find out a good time to drop them. // See bug https://github.com/servo/servo/issues/3300 - // - // GWTODO: Check on real pages if this is faster as Vec() or HashMap(). - font_cache: Vec<FontCacheEntry>, - fallback_font_cache: Vec<FallbackFontCacheEntry>, + font_cache: HashMap<FontCacheKey, Option<FontRef>>, font_group_cache: HashMap<FontGroupCacheKey, Rc<RefCell<FontGroup>>, BuildHasherDefault<FnvHasher>>, @@ -101,33 +63,12 @@ impl<S: FontSource> FontContext<S> { FontContext { platform_handle: handle, font_source, - font_cache: vec!(), - fallback_font_cache: vec!(), + font_cache: HashMap::new(), font_group_cache: HashMap::with_hasher(Default::default()), epoch: 0, } } - /// Create a `Font` for use in layout calculations, from a `FontTemplateInfo` returned by the - /// cache thread (which contains the underlying font data) and a `FontDescriptor` which - /// contains the styling parameters. - fn create_font(&mut self, info: FontTemplateInfo, descriptor: FontDescriptor) -> Result<Font, ()> { - // TODO: (Bug #3463): Currently we only support fake small-caps - // painting. We should also support true small-caps (where the - // font supports it) in the future. - let actual_pt_size = match descriptor.variant { - FontVariantCaps::SmallCaps => descriptor.pt_size.scale_by(SMALL_CAPS_SCALE_FACTOR), - FontVariantCaps::Normal => descriptor.pt_size, - }; - - let handle = FontHandle::new_from_template(&self.platform_handle, - info.font_template, - Some(actual_pt_size))?; - - let font_instance_key = self.font_source.get_font_instance(info.font_key, actual_pt_size); - Ok(Font::new(handle, descriptor.to_owned(), actual_pt_size, font_instance_key)) - } - fn expire_font_caches_if_necessary(&mut self) { let current_epoch = FONT_CACHE_EPOCH.load(Ordering::SeqCst); if current_epoch == self.epoch { @@ -135,7 +76,6 @@ impl<S: FontSource> FontContext<S> { } self.font_cache.clear(); - self.fallback_font_cache.clear(); self.font_group_cache.clear(); self.epoch = current_epoch } @@ -160,76 +100,61 @@ impl<S: FontSource> FontContext<S> { font_group } - /// Returns a reference to an existing font cache entry matching `descriptor` and `family`, if - /// there is one. - fn font_cache_entry(&self, descriptor: &FontDescriptor, family: &SingleFontFamily) -> Option<&FontCacheEntry> { - self.font_cache.iter() - .find(|cache_entry| cache_entry.matches(&descriptor, &family)) - } + /// Returns a font matching the parameters. Fonts are cached, so repeated calls will return a + /// reference to the same underlying `Font`. + pub fn font( + &mut self, + font_descriptor: &FontDescriptor, + family_descriptor: &FontFamilyDescriptor, + ) -> Option<FontRef> { + let cache_key = FontCacheKey { + font_descriptor: font_descriptor.clone(), + family_descriptor: family_descriptor.clone(), + }; - /// Creates a new font cache entry matching `descriptor` and `family`. - fn create_font_cache_entry(&mut self, descriptor: &FontDescriptor, family: &SingleFontFamily) -> FontCacheEntry { - let font = - self.font_source.find_font_template(family.clone(), descriptor.template_descriptor.clone()) - .and_then(|template_info| - self.create_font(template_info, descriptor.to_owned()).ok() + self.font_cache.get(&cache_key).map(|v| v.clone()).unwrap_or_else(|| { + debug!( + "FontContext::font cache miss for font_descriptor={:?} family_descriptor={:?}", + font_descriptor, + family_descriptor + ); + + let font = + self.font_source.font_template( + font_descriptor.template_descriptor.clone(), + family_descriptor.clone(), ) + .and_then(|template_info| self.create_font(template_info, font_descriptor.to_owned()).ok()) .map(|font| Rc::new(RefCell::new(font))); - FontCacheEntry { family: family.atom().to_owned(), font } - } - - /// Returns a font from `family` matching the `descriptor`. Fonts are cached, so repeated calls - /// will return a reference to the same underlying `Font`. - pub fn font(&mut self, descriptor: &FontDescriptor, family: &SingleFontFamily) -> Option<FontRef> { - if let Some(entry) = self.font_cache_entry(descriptor, family) { - return entry.font.clone() - } - - let entry = self.create_font_cache_entry(descriptor, family); - let font = entry.font.clone(); - self.font_cache.push(entry); - font - } - - /// Returns a reference to an existing fallback font cache entry matching `descriptor`, if - /// there is one. - fn fallback_font_cache_entry(&self, descriptor: &FontDescriptor) -> Option<&FallbackFontCacheEntry> { - self.fallback_font_cache.iter() - .find(|cache_entry| cache_entry.matches(descriptor)) - } - - /// Creates a new fallback font cache entry matching `descriptor`. - fn create_fallback_font_cache_entry(&mut self, descriptor: &FontDescriptor) -> Option<FallbackFontCacheEntry> { - let template_info = self.font_source.last_resort_font_template(descriptor.template_descriptor.clone()); - - match self.create_font(template_info, descriptor.to_owned()) { - Ok(font) => - Some(FallbackFontCacheEntry { - font: Rc::new(RefCell::new(font)) - }), - - Err(_) => { - debug!("Failed to create fallback font!"); - None - } - } + self.font_cache.insert(cache_key, font.clone()); + font + }) } - /// Returns a fallback font matching the `descriptor`. Fonts are cached, so repeated calls will - /// return a reference to the same underlying `Font`. - pub fn fallback_font(&mut self, descriptor: &FontDescriptor) -> Option<FontRef> { - if let Some(cached_entry) = self.fallback_font_cache_entry(descriptor) { - return Some(cached_entry.font.clone()) + /// Create a `Font` for use in layout calculations, from a `FontTemplateData` returned by the + /// cache thread and a `FontDescriptor` which contains the styling parameters. + fn create_font( + &mut self, + info: FontTemplateInfo, + descriptor: FontDescriptor + ) -> Result<Font, ()> { + // TODO: (Bug #3463): Currently we only support fake small-caps + // painting. We should also support true small-caps (where the + // font supports it) in the future. + let actual_pt_size = match descriptor.variant { + FontVariantCaps::SmallCaps => descriptor.pt_size.scale_by(SMALL_CAPS_SCALE_FACTOR), + FontVariantCaps::Normal => descriptor.pt_size, }; - if let Some(entry) = self.create_fallback_font_cache_entry(descriptor) { - let font = entry.font.clone(); - self.fallback_font_cache.push(entry); - Some(font) - } else { - None - } + let handle = FontHandle::new_from_template( + &self.platform_handle, + info.font_template, + Some(actual_pt_size) + )?; + + let font_instance_key = self.font_source.get_font_instance(info.font_key, actual_pt_size); + Ok(Font::new(handle, descriptor.to_owned(), actual_pt_size, font_instance_key)) } } @@ -240,6 +165,12 @@ impl<S: FontSource> MallocSizeOf for FontContext<S> { } } +#[derive(Debug, Eq, Hash, PartialEq)] +struct FontCacheKey { + font_descriptor: FontDescriptor, + family_descriptor: FontFamilyDescriptor, +} + #[derive(Debug)] struct FontGroupCacheKey { style: Arc<FontStyleStruct>, diff --git a/components/gfx/font_template.rs b/components/gfx/font_template.rs index 13c531f4d43..8a72360d0dc 100644 --- a/components/gfx/font_template.rs +++ b/components/gfx/font_template.rs @@ -19,13 +19,18 @@ use style::values::computed::font::FontWeight; /// to be expanded or refactored when we support more of the font styling parameters. /// /// NB: If you change this, you will need to update `style::properties::compute_font_hash()`. -#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)] +#[derive(Clone, Copy, Debug, Deserialize, Hash, PartialEq, Serialize)] pub struct FontTemplateDescriptor { pub weight: FontWeight, pub stretch: FontStretch, pub style: FontStyle, } + +/// FontTemplateDescriptor contains floats, which are not Eq because of NaN. However, +/// we know they will never be NaN, so we can manually implement Eq. +impl Eq for FontTemplateDescriptor {} + fn style_to_number(s: &FontStyle) -> f32 { use style::values::generics::font::FontStyle as GenericFontStyle; @@ -66,7 +71,7 @@ impl FontTemplateDescriptor { // 0 <= weightPart <= 800 let weight_part = (self.weight.0 - other.weight.0).abs(); // 0 <= stretchPart <= 8 - let stretch_part = ((self.stretch.0).0 - (other.stretch.0).0).abs(); + let stretch_part = (self.stretch.value() - other.stretch.value()).abs(); style_part + weight_part + stretch_part } } diff --git a/components/gfx/lib.rs b/components/gfx/lib.rs index 156170c40cf..7d68847c4e6 100644 --- a/components/gfx/lib.rs +++ b/components/gfx/lib.rs @@ -45,14 +45,15 @@ extern crate ordered_float; extern crate range; #[macro_use] extern crate serde; extern crate servo_arc; -extern crate servo_url; #[macro_use] extern crate servo_atoms; +extern crate servo_url; #[cfg(feature = "unstable")] #[cfg(any(target_feature = "sse2", target_feature = "neon"))] extern crate simd; extern crate smallvec; extern crate style; extern crate time; +extern crate ucd; extern crate unicode_bidi; extern crate unicode_script; extern crate webrender_api; diff --git a/components/gfx/platform/freetype/android/font_list.rs b/components/gfx/platform/freetype/android/font_list.rs index 6d5a1ea3061..e26e6c84e60 100644 --- a/components/gfx/platform/freetype/android/font_list.rs +++ b/components/gfx/platform/freetype/android/font_list.rs @@ -6,6 +6,8 @@ use std::cell::RefCell; use std::fs::File; use std::io::{self, Read}; use std::path::Path; +use text::util::is_cjk; +use ucd::{Codepoint, UnicodeBlock}; use xml5ever::Attribute; use xml5ever::driver::parse_document; use xml5ever::rcdom::*; @@ -470,12 +472,61 @@ pub fn system_default_family(generic_name: &str) -> Option<String> { } } -pub fn last_resort_font_families() -> Vec<String> { - vec!( - "sans-serif".to_owned(), - "Droid Sans".to_owned(), - "serif".to_owned(), - ) +// Based on gfxAndroidPlatform::GetCommonFallbackFonts() in Gecko +pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> { + let mut families = vec!(); + + if let Some(block) = codepoint.and_then(|c| c.block()) { + match block { + UnicodeBlock::Armenian => { + families.push("Droid Sans Armenian"); + } + + UnicodeBlock::Hebrew => { + families.push("Droid Sans Hebrew"); + } + + UnicodeBlock::Arabic => { + families.push("Droid Sans Arabic"); + } + + UnicodeBlock::Devanagari => { + families.push("Noto Sans Devanagari"); + families.push("Droid Sans Devanagari"); + } + + UnicodeBlock::Tamil => { + families.push("Noto Sans Tamil"); + families.push("Droid Sans Tamil"); + } + + UnicodeBlock::Thai => { + families.push("Noto Sans Thai"); + families.push("Droid Sans Thai"); + } + + UnicodeBlock::Georgian | + UnicodeBlock::GeorgianSupplement => { + families.push("Droid Sans Georgian"); + } + + UnicodeBlock::Ethiopic | + UnicodeBlock::EthiopicSupplement => { + families.push("Droid Sans Ethiopic"); + } + + _ => { + if is_cjk(codepoint.unwrap()) { + families.push("MotoyaLMaru"); + families.push("Noto Sans CJK JP"); + families.push("Droid Sans Japanese"); + } + } + } + } + + families.push("Droid Sans Fallback"); + families } pub static SANS_SERIF_FONT_FAMILY: &'static str = "sans-serif"; diff --git a/components/gfx/platform/freetype/font.rs b/components/gfx/platform/freetype/font.rs index ef6dfdddd80..131d3deb8db 100644 --- a/components/gfx/platform/freetype/font.rs +++ b/components/gfx/platform/freetype/font.rs @@ -187,7 +187,7 @@ impl FontHandleMethods for FontHandle { } else { FontStretchKeyword::Normal }.compute(); - NonNegative(percentage) + FontStretch(NonNegative(percentage)) } fn glyph_index(&self, codepoint: char) -> Option<GlyphId> { diff --git a/components/gfx/platform/freetype/font_list.rs b/components/gfx/platform/freetype/font_list.rs index 9b85db624d8..ddafb94a031 100644 --- a/components/gfx/platform/freetype/font_list.rs +++ b/components/gfx/platform/freetype/font_list.rs @@ -10,10 +10,10 @@ use fontconfig::fontconfig::{FcFontSetList, FcObjectSetCreate, FcObjectSetDestro use fontconfig::fontconfig::{FcObjectSetAdd, FcPatternGetInteger}; use libc; use libc::{c_char, c_int}; -use std::borrow::ToOwned; use std::ffi::CString; use std::ptr; use super::c_str_to_string; +use text::util::is_cjk; static FC_FAMILY: &'static [u8] = b"family\0"; static FC_FILE: &'static [u8] = b"file\0"; @@ -132,12 +132,25 @@ pub fn system_default_family(generic_name: &str) -> Option<String> { } } -pub fn last_resort_font_families() -> Vec<String> { - vec!( - "Fira Sans".to_owned(), - "DejaVu Sans".to_owned(), - "Arial".to_owned() - ) -} - pub static SANS_SERIF_FONT_FAMILY: &'static str = "DejaVu Sans"; + +// Based on gfxPlatformGtk::GetCommonFallbackFonts() in Gecko +pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> { + let mut families = vec!( + "DejaVu Serif", + "FreeSerif", + "DejaVu Sans", + "FreeSans", + ); + + if let Some(codepoint) = codepoint { + if is_cjk(codepoint) { + families.push("TakaoPGothic"); + families.push("Droid Sans Fallback"); + families.push("WenQuanYi Micro Hei"); + families.push("NanumGothic"); + } + } + + families +} diff --git a/components/gfx/platform/macos/font.rs b/components/gfx/platform/macos/font.rs index 5eacf92ffd6..8025f1b33df 100644 --- a/components/gfx/platform/macos/font.rs +++ b/components/gfx/platform/macos/font.rs @@ -229,7 +229,7 @@ impl FontHandleMethods for FontHandle { use style::values::generics::NonNegative; let normalized = self.ctfont.all_traits().normalized_width(); // [-1.0, 1.0] - NonNegative(Percentage(normalized as f32 + 1.0)) + FontStretch(NonNegative(Percentage(normalized as f32 + 1.0))) } fn glyph_index(&self, codepoint: char) -> Option<GlyphId> { diff --git a/components/gfx/platform/macos/font_list.rs b/components/gfx/platform/macos/font_list.rs index d72111f9c1e..656cc043907 100644 --- a/components/gfx/platform/macos/font_list.rs +++ b/components/gfx/platform/macos/font_list.rs @@ -3,7 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use core_text; -use std::borrow::ToOwned; +use text::util::unicode_plane; +use ucd::{Codepoint, UnicodeBlock}; pub fn for_each_available_family<F>(mut callback: F) where F: FnMut(String) { let family_names = core_text::font_collection::get_family_names(); @@ -28,8 +29,162 @@ pub fn system_default_family(_generic_name: &str) -> Option<String> { None } -pub fn last_resort_font_families() -> Vec<String> { - vec!("Arial Unicode MS".to_owned(), "Arial".to_owned()) +// Based on gfxPlatformMac::GetCommonFallbackFonts() in Gecko +pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> { + let mut families = vec!("Lucida Grande"); + + if let Some(codepoint) = codepoint { + match unicode_plane(codepoint) { + // https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane + 0 => { + if let Some(block) = codepoint.block() { + match block { + UnicodeBlock::Arabic | + UnicodeBlock::Syriac | + UnicodeBlock::ArabicSupplement | + UnicodeBlock::Thaana | + UnicodeBlock::NKo => { + families.push("Geeza Pro"); + } + + UnicodeBlock::Devanagari => { + families.push("Devanagari Sangam MN"); + } + + UnicodeBlock::Gurmukhi => { + families.push("Gurmukhi MN"); + } + + UnicodeBlock::Gujarati => { + families.push("Gujarati Sangam MN"); + } + + UnicodeBlock::Tamil => { + families.push("Tamil MN"); + } + + UnicodeBlock::Lao => { + families.push("Lao MN"); + } + + UnicodeBlock::Tibetan => { + families.push("Songti SC"); + } + + UnicodeBlock::Myanmar => { + families.push("Myanmar MN"); + } + + UnicodeBlock::Ethiopic | + UnicodeBlock::EthiopicSupplement | + UnicodeBlock::EthiopicExtended | + UnicodeBlock::EthiopicExtendedA => { + families.push("Kefa"); + } + + UnicodeBlock::Cherokee => { + families.push("Plantagenet Cherokee"); + } + + UnicodeBlock::UnifiedCanadianAboriginalSyllabics | + UnicodeBlock::UnifiedCanadianAboriginalSyllabicsExtended => { + families.push("Euphemia UCAS"); + } + + UnicodeBlock::Mongolian | + UnicodeBlock::YiSyllables | + UnicodeBlock::YiRadicals => { + families.push("STHeiti"); + } + + UnicodeBlock::Khmer | + UnicodeBlock::KhmerSymbols => { + families.push("Khmer MN"); + } + + UnicodeBlock::TaiLe => { + families.push("Microsoft Tai Le"); + } + + UnicodeBlock::GeneralPunctuation | + UnicodeBlock::SuperscriptsandSubscripts | + UnicodeBlock::CurrencySymbols | + UnicodeBlock::CombiningDiacriticalMarksforSymbols | + UnicodeBlock::LetterlikeSymbols | + UnicodeBlock::NumberForms | + UnicodeBlock::Arrows | + UnicodeBlock::MathematicalOperators | + UnicodeBlock::MiscellaneousTechnical | + UnicodeBlock::ControlPictures | + UnicodeBlock::OpticalCharacterRecognition | + UnicodeBlock::EnclosedAlphanumerics | + UnicodeBlock::BoxDrawing | + UnicodeBlock::BlockElements | + UnicodeBlock::GeometricShapes | + UnicodeBlock::MiscellaneousSymbols | + UnicodeBlock::Dingbats | + UnicodeBlock::MiscellaneousMathematicalSymbolsA | + UnicodeBlock::SupplementalArrowsA | + UnicodeBlock::SupplementalArrowsB | + UnicodeBlock::MiscellaneousMathematicalSymbolsB | + UnicodeBlock::SupplementalMathematicalOperators | + UnicodeBlock::MiscellaneousSymbolsandArrows | + UnicodeBlock::SupplementalPunctuation => { + families.push("Hiragino Kaku Gothic ProN"); + families.push("Apple Symbols"); + families.push("Menlo"); + families.push("STIXGeneral"); + } + + UnicodeBlock::BraillePatterns => { + families.push("Apple Braille"); + } + + UnicodeBlock::Bopomofo | + UnicodeBlock::HangulCompatibilityJamo | + UnicodeBlock::Kanbun | + UnicodeBlock::BopomofoExtended | + UnicodeBlock::CJKStrokes | + UnicodeBlock::KatakanaPhoneticExtensions => { + families.push("Hiragino Sans GB"); + } + + UnicodeBlock::YijingHexagramSymbols | + UnicodeBlock::CyrillicExtendedB | + UnicodeBlock::Bamum | + UnicodeBlock::ModifierToneLetters | + UnicodeBlock::LatinExtendedD | + UnicodeBlock::ArabicPresentationFormsA | + UnicodeBlock::HalfwidthandFullwidthForms | + UnicodeBlock::Specials => { + families.push("Apple Symbols"); + } + + _ => {} + } + } + } + + // https://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Multilingual_Plane + 1 => { + families.push("Apple Symbols"); + families.push("STIXGeneral"); + } + + // https://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Ideographic_Plane + 2 => { + // Systems with MS Office may have these fonts + families.push("MingLiU-ExtB"); + families.push("SimSun-ExtB"); + } + + _ => {} + } + } + + families.push("Geneva"); + families.push("Arial Unicode MS"); + families } pub static SANS_SERIF_FONT_FAMILY: &'static str = "Helvetica"; diff --git a/components/gfx/platform/windows/font.rs b/components/gfx/platform/windows/font.rs index ed94d0fc5a2..e657996d119 100644 --- a/components/gfx/platform/windows/font.rs +++ b/components/gfx/platform/windows/font.rs @@ -163,7 +163,7 @@ impl FontInfo { let weight = StyleFontWeight(weight_val as f32); - let stretch = NonNegative(match min(9, max(1, width_val)) { + let stretch = StyleFontStretch(NonNegative(match min(9, max(1, width_val)) { 1 => FontStretchKeyword::UltraCondensed, 2 => FontStretchKeyword::ExtraCondensed, 3 => FontStretchKeyword::Condensed, @@ -174,7 +174,7 @@ impl FontInfo { 8 => FontStretchKeyword::ExtraExpanded, 9 => FontStretchKeyword::UltraExpanded, _ => return Err(()), - }.compute()); + }.compute())); let style = if italic_bool { GenericFontStyle::Italic @@ -212,7 +212,7 @@ impl FontInfo { // slightly blacker black FontWeight::ExtraBlack => 1000., }); - let stretch = NonNegative(match font.stretch() { + let stretch = StyleFontStretch(NonNegative(match font.stretch() { FontStretch::Undefined => FontStretchKeyword::Normal, FontStretch::UltraCondensed => FontStretchKeyword::UltraCondensed, FontStretch::ExtraCondensed => FontStretchKeyword::ExtraCondensed, @@ -223,7 +223,7 @@ impl FontInfo { FontStretch::Expanded => FontStretchKeyword::Expanded, FontStretch::ExtraExpanded => FontStretchKeyword::ExtraExpanded, FontStretch::UltraExpanded => FontStretchKeyword::UltraExpanded, - }.compute()); + }.compute())); Ok(FontInfo { family_name: font.family_name(), diff --git a/components/gfx/platform/windows/font_list.rs b/components/gfx/platform/windows/font_list.rs index 27dfd5e73fe..3271b77e7fb 100644 --- a/components/gfx/platform/windows/font_list.rs +++ b/components/gfx/platform/windows/font_list.rs @@ -7,6 +7,8 @@ use servo_atoms::Atom; use std::collections::HashMap; use std::sync::Mutex; use std::sync::atomic::{Ordering, AtomicUsize}; +use text::util::unicode_plane; +use ucd::{Codepoint, UnicodeBlock}; lazy_static! { static ref FONT_ATOM_COUNTER: AtomicUsize = AtomicUsize::new(1); @@ -19,10 +21,6 @@ pub fn system_default_family(_: &str) -> Option<String> { Some("Verdana".to_owned()) } -pub fn last_resort_font_families() -> Vec<String> { - vec!("Arial".to_owned()) -} - pub fn for_each_available_family<F>(mut callback: F) where F: FnMut(String) { let system_fc = FontCollection::system(); for family in system_fc.families_iter() { @@ -69,3 +67,270 @@ pub fn font_from_atom(ident: &Atom) -> Font { let fonts = FONT_ATOM_MAP.lock().unwrap(); FontCollection::system().get_font_from_descriptor(fonts.get(ident).unwrap()).unwrap() } + +// Based on gfxWindowsPlatform::GetCommonFallbackFonts() in Gecko +pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> { + let mut families = vec!("Arial"); + + if let Some(codepoint) = codepoint { + match unicode_plane(codepoint) { + // https://en.wikipedia.org/wiki/Plane_(Unicode)#Basic_Multilingual_Plane + 0 => { + if let Some(block) = codepoint.block() { + match block { + UnicodeBlock::CyrillicSupplement | + UnicodeBlock::Armenian | + UnicodeBlock::Hebrew => { + families.push("Estrangelo Edessa"); + families.push("Cambria"); + } + + UnicodeBlock::Arabic | + UnicodeBlock::ArabicSupplement => { + families.push("Microsoft Uighur"); + } + + UnicodeBlock::Syriac => { + families.push("Estrangelo Edessa"); + } + + UnicodeBlock::Thaana => { + families.push("MV Boli"); + } + + UnicodeBlock::NKo => { + families.push("Ebrima"); + } + + UnicodeBlock::Devanagari | + UnicodeBlock::Bengali => { + families.push("Nirmala UI"); + families.push("Utsaah"); + families.push("Aparajita"); + } + + UnicodeBlock::Gurmukhi | + UnicodeBlock::Gujarati | + UnicodeBlock::Oriya | + UnicodeBlock::Tamil | + UnicodeBlock::Telugu | + UnicodeBlock::Kannada | + UnicodeBlock::Malayalam | + UnicodeBlock::Sinhala | + UnicodeBlock::Lepcha | + UnicodeBlock::OlChiki | + UnicodeBlock::CyrillicExtendedC | + UnicodeBlock::SundaneseSupplement | + UnicodeBlock::VedicExtensions => { + families.push("Nirmala UI"); + } + + UnicodeBlock::Thai => { + families.push("Leelawadee UI"); + } + + UnicodeBlock::Lao => { + families.push("Lao UI"); + } + + UnicodeBlock::Myanmar | + UnicodeBlock::MyanmarExtendedA | + UnicodeBlock::MyanmarExtendedB => { + families.push("Myanmar Text"); + } + + UnicodeBlock::HangulJamo | + UnicodeBlock::HangulJamoExtendedA | + UnicodeBlock::HangulSyllables | + UnicodeBlock::HangulJamoExtendedB | + UnicodeBlock::HangulCompatibilityJamo => { + families.push("Malgun Gothic"); + } + + UnicodeBlock::Ethiopic | + UnicodeBlock::EthiopicSupplement | + UnicodeBlock::EthiopicExtended | + UnicodeBlock::EthiopicExtendedA => { + families.push("Nyala"); + } + + UnicodeBlock::Cherokee => { + families.push("Plantagenet Cherokee"); + } + + UnicodeBlock::UnifiedCanadianAboriginalSyllabics | + UnicodeBlock::UnifiedCanadianAboriginalSyllabicsExtended => { + families.push("Euphemia"); + families.push("Segoe UI"); + } + + UnicodeBlock::Khmer | + UnicodeBlock::KhmerSymbols => { + families.push("Khmer UI"); + families.push("Leelawadee UI"); + } + + UnicodeBlock::Mongolian => { + families.push("Mongolian Baiti"); + } + + UnicodeBlock::TaiLe => { + families.push("Microsoft Tai Le"); + } + + UnicodeBlock::NewTaiLue => { + families.push("Microsoft New Tai Lue"); + } + + UnicodeBlock::Buginese | + UnicodeBlock::TaiTham | + UnicodeBlock::CombiningDiacriticalMarksExtended => { + families.push("Leelawadee UI"); + } + + UnicodeBlock::GeneralPunctuation | + UnicodeBlock::SuperscriptsandSubscripts | + UnicodeBlock::CurrencySymbols | + UnicodeBlock::CombiningDiacriticalMarksforSymbols | + UnicodeBlock::LetterlikeSymbols | + UnicodeBlock::NumberForms | + UnicodeBlock::Arrows | + UnicodeBlock::MathematicalOperators | + UnicodeBlock::MiscellaneousTechnical | + UnicodeBlock::ControlPictures | + UnicodeBlock::OpticalCharacterRecognition | + UnicodeBlock::EnclosedAlphanumerics | + UnicodeBlock::BoxDrawing | + UnicodeBlock::BlockElements | + UnicodeBlock::GeometricShapes | + UnicodeBlock::MiscellaneousSymbols | + UnicodeBlock::Dingbats | + UnicodeBlock::MiscellaneousMathematicalSymbolsA | + UnicodeBlock::SupplementalArrowsA | + UnicodeBlock::SupplementalArrowsB | + UnicodeBlock::MiscellaneousMathematicalSymbolsB | + UnicodeBlock::SupplementalMathematicalOperators | + UnicodeBlock::MiscellaneousSymbolsandArrows | + UnicodeBlock::Glagolitic | + UnicodeBlock::LatinExtendedC | + UnicodeBlock::Coptic => { + families.push("Segoe UI"); + families.push("Segoe UI Symbol"); + families.push("Cambria"); + families.push("Meiryo"); + families.push("Lucida Sans Unicode"); + families.push("Ebrima"); + } + + UnicodeBlock::GeorgianSupplement | + UnicodeBlock::Tifinagh | + UnicodeBlock::CyrillicExtendedA | + UnicodeBlock::SupplementalPunctuation | + UnicodeBlock::CJKRadicalsSupplement | + UnicodeBlock::KangxiRadicals | + UnicodeBlock::IdeographicDescriptionCharacters => { + families.push("Segoe UI"); + families.push("Segoe UI Symbol"); + families.push("Meiryo"); + } + + UnicodeBlock::BraillePatterns => { + families.push("Segoe UI Symbol"); + } + + UnicodeBlock::CJKSymbolsandPunctuation | + UnicodeBlock::Hiragana | + UnicodeBlock::Katakana | + UnicodeBlock::Bopomofo | + UnicodeBlock::Kanbun | + UnicodeBlock::BopomofoExtended | + UnicodeBlock::CJKStrokes | + UnicodeBlock::KatakanaPhoneticExtensions | + UnicodeBlock::CJKUnifiedIdeographs => { + families.push("Microsoft YaHei"); + families.push("Yu Gothic"); + } + + UnicodeBlock::EnclosedCJKLettersandMonths => { + families.push("Malgun Gothic"); + } + + UnicodeBlock::YijingHexagramSymbols => { + families.push("Segoe UI Symbol"); + } + + UnicodeBlock::YiSyllables | + UnicodeBlock::YiRadicals => { + families.push("Microsoft Yi Baiti"); + families.push("Segoe UI"); + } + + UnicodeBlock::Vai | + UnicodeBlock::CyrillicExtendedB | + UnicodeBlock::Bamum | + UnicodeBlock::ModifierToneLetters | + UnicodeBlock::LatinExtendedD => { + families.push("Ebrima"); + families.push("Segoe UI"); + families.push("Cambria Math"); + } + + UnicodeBlock::SylotiNagri | + UnicodeBlock::CommonIndicNumberForms | + UnicodeBlock::Phagspa | + UnicodeBlock::Saurashtra | + UnicodeBlock::DevanagariExtended => { + families.push("Microsoft PhagsPa"); + families.push("Nirmala UI"); + } + + UnicodeBlock::KayahLi | + UnicodeBlock::Rejang | + UnicodeBlock::Javanese => { + families.push("Malgun Gothic"); + families.push("Javanese Text"); + families.push("Leelawadee UI"); + } + + UnicodeBlock::AlphabeticPresentationForms => { + families.push("Microsoft Uighur"); + families.push("Gabriola"); + families.push("Sylfaen"); + } + + UnicodeBlock::ArabicPresentationFormsA | + UnicodeBlock::ArabicPresentationFormsB => { + families.push("Traditional Arabic"); + families.push("Arabic Typesetting"); + } + + UnicodeBlock::VariationSelectors | + UnicodeBlock::VerticalForms | + UnicodeBlock::CombiningHalfMarks | + UnicodeBlock::CJKCompatibilityForms | + UnicodeBlock::SmallFormVariants | + UnicodeBlock::HalfwidthandFullwidthForms | + UnicodeBlock::Specials => { + families.push("Microsoft JhengHei"); + } + + _ => {} + } + } + } + + // https://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Multilingual_Plane + 1 => { + families.push("Segoe UI Symbol"); + families.push("Ebrima"); + families.push("Nirmala UI"); + families.push("Cambria Math"); + } + + _ => {} + } + } + + families.push("Arial Unicode MS"); + families +} diff --git a/components/gfx/tests/font_context.rs b/components/gfx/tests/font_context.rs index c918ef86e90..a16d3822997 100644 --- a/components/gfx/tests/font_context.rs +++ b/components/gfx/tests/font_context.rs @@ -10,7 +10,7 @@ extern crate style; extern crate webrender_api; use app_units::Au; -use gfx::font::FontHandleMethods; +use gfx::font::{fallback_font_families, FontFamilyDescriptor, FontHandleMethods}; use gfx::font_cache_thread::{FontTemplates, FontTemplateInfo}; use gfx::font_context::{FontContext, FontContextHandle, FontSource}; use gfx::font_template::FontTemplateDescriptor; @@ -24,29 +24,31 @@ use std::path::PathBuf; use std::rc::Rc; use style::properties::longhands::font_variant_caps::computed_value::T as FontVariantCaps; use style::properties::style_structs::Font as FontStyleStruct; -use style::values::computed::Percentage; use style::values::computed::font::{FamilyName, FamilyNameSyntax, FontFamily, FontFamilyList, FontSize}; -use style::values::computed::font::{FontWeight, SingleFontFamily}; -use style::values::generics::NonNegative; +use style::values::computed::font::{FontStretch, FontWeight, SingleFontFamily}; use style::values::generics::font::FontStyle; struct TestFontSource { handle: FontContextHandle, - families: HashMap<Atom, FontTemplates>, + families: HashMap<String, FontTemplates>, find_font_count: Rc<Cell<isize>>, } impl TestFontSource { fn new() -> TestFontSource { let mut csstest_ascii = FontTemplates::new(); - Self::add_face(&mut csstest_ascii, "csstest-ascii"); + Self::add_face(&mut csstest_ascii, "csstest-ascii", None); let mut csstest_basic = FontTemplates::new(); - Self::add_face(&mut csstest_basic, "csstest-basic-regular"); + Self::add_face(&mut csstest_basic, "csstest-basic-regular", None); + + let mut fallback = FontTemplates::new(); + Self::add_face(&mut fallback, "csstest-basic-regular", Some("fallback")); let mut families = HashMap::new(); - families.insert(Atom::from("CSSTest ASCII"), csstest_ascii); - families.insert(Atom::from("CSSTest Basic"), csstest_basic); + families.insert("CSSTest ASCII".to_owned(), csstest_ascii); + families.insert("CSSTest Basic".to_owned(), csstest_basic); + families.insert(fallback_font_families(None)[0].to_owned(), fallback); TestFontSource { handle: FontContextHandle::new(), @@ -55,7 +57,7 @@ impl TestFontSource { } } - fn add_face(family: &mut FontTemplates, name: &str) { + fn add_face(family: &mut FontTemplates, name: &str, identifier: Option<&str>) { let mut path: PathBuf = [ env!("CARGO_MANIFEST_DIR"), "tests", @@ -65,9 +67,10 @@ impl TestFontSource { path.push(format!("{}.ttf", name)); let file = File::open(path).unwrap(); + let identifier = Atom::from(identifier.unwrap_or(name)); family.add_template( - Atom::from(name), + identifier, Some(file.bytes().map(|b| b.unwrap()).collect()) ) } @@ -78,17 +81,17 @@ impl FontSource for TestFontSource { webrender_api::FontInstanceKey(webrender_api::IdNamespace(0), 0) } - fn find_font_template( + fn font_template( &mut self, - family: SingleFontFamily, - desc: FontTemplateDescriptor + template_descriptor: FontTemplateDescriptor, + family_descriptor: FontFamilyDescriptor, ) -> Option<FontTemplateInfo> { let handle = &self.handle; self.find_font_count.set(self.find_font_count.get() + 1); self.families - .get_mut(family.atom()) - .and_then(|family| family.find_font_for_style(&desc, handle)) + .get_mut(family_descriptor.name()) + .and_then(|family| family.find_font_for_style(&template_descriptor, handle)) .map(|template| { FontTemplateInfo { font_template: template, @@ -96,10 +99,6 @@ impl FontSource for TestFontSource { } }) } - - fn last_resort_font_template(&mut self, _desc: FontTemplateDescriptor) -> FontTemplateInfo { - unimplemented!(); - } } fn style() -> FontStyleStruct { @@ -109,7 +108,7 @@ fn style() -> FontStyleStruct { font_variant_caps: FontVariantCaps::Normal, font_weight: FontWeight::normal(), font_size: FontSize::medium(), - font_stretch: NonNegative(Percentage(1.)), + font_stretch: FontStretch::hundred(), hash: 0, }; style.compute_font_hash(); @@ -162,14 +161,37 @@ fn test_font_group_find_by_codepoint() { let group = context.font_group(Arc::new(style)); let font = group.borrow_mut().find_by_codepoint(&mut context, 'a').unwrap(); - assert_eq!(font.borrow().handle.family_name(), "CSSTest ASCII"); + assert_eq!(&*font.borrow().identifier(), "csstest-ascii"); assert_eq!(count.get(), 1, "only the first font in the list should have been loaded"); let font = group.borrow_mut().find_by_codepoint(&mut context, 'a').unwrap(); - assert_eq!(font.borrow().handle.family_name(), "CSSTest ASCII"); + assert_eq!(&*font.borrow().identifier(), "csstest-ascii"); assert_eq!(count.get(), 1, "we shouldn't load the same font a second time"); let font = group.borrow_mut().find_by_codepoint(&mut context, 'á').unwrap(); - assert_eq!(font.borrow().handle.family_name(), "CSSTest Basic"); + assert_eq!(&*font.borrow().identifier(), "csstest-basic-regular"); assert_eq!(count.get(), 2, "both fonts should now have been loaded"); } + +#[test] +fn test_font_fallback() { + let source = TestFontSource::new(); + let mut context = FontContext::new(source); + + let mut style = style(); + style.set_font_family(font_family(vec!("CSSTest ASCII"))); + + let group = context.font_group(Arc::new(style)); + + let font = group.borrow_mut().find_by_codepoint(&mut context, 'a').unwrap(); + assert_eq!( + &*font.borrow().identifier(), "csstest-ascii", + "a family in the group should be used if there is a matching glyph" + ); + + let font = group.borrow_mut().find_by_codepoint(&mut context, 'á').unwrap(); + assert_eq!( + &*font.borrow().identifier(), "fallback", + "a fallback font should be used if there is no matching glyph in the group" + ); +} diff --git a/components/gfx/tests/font_template.rs b/components/gfx/tests/font_template.rs index af31d8f229d..e5857ce4ef7 100644 --- a/components/gfx/tests/font_template.rs +++ b/components/gfx/tests/font_template.rs @@ -17,7 +17,7 @@ fn test_font_template_descriptor() { use std::io::prelude::*; use std::path::PathBuf; use style::values::computed::Percentage; - use style::values::computed::font::FontWeight; + use style::values::computed::font::{FontStretch, FontWeight}; use style::values::generics::NonNegative; use style::values::generics::font::FontStyle; @@ -45,25 +45,25 @@ fn test_font_template_descriptor() { assert_eq!(descriptor("DejaVuSans"), FontTemplateDescriptor { weight: FontWeight::normal(), - stretch: NonNegative(Percentage(1.)), + stretch: FontStretch::hundred(), style: FontStyle::Normal, }); assert_eq!(descriptor("DejaVuSans-Bold"), FontTemplateDescriptor { weight: FontWeight::bold(), - stretch: NonNegative(Percentage(1.)), + stretch: FontStretch::hundred(), style: FontStyle::Normal, }); assert_eq!(descriptor("DejaVuSans-Oblique"), FontTemplateDescriptor { weight: FontWeight::normal(), - stretch: NonNegative(Percentage(1.)), + stretch: FontStretch::hundred(), style: FontStyle::Italic, }); assert_eq!(descriptor("DejaVuSansCondensed-BoldOblique"), FontTemplateDescriptor { weight: FontWeight::bold(), - stretch: NonNegative(Percentage(0.875)), + stretch: FontStretch(NonNegative(Percentage(0.875))), style: FontStyle::Italic, }); } diff --git a/components/gfx/text/util.rs b/components/gfx/text/util.rs index 5be03cb2b78..f740c4a54e6 100644 --- a/components/gfx/text/util.rs +++ b/components/gfx/text/util.rs @@ -2,6 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +use ucd::{Codepoint, UnicodeBlock}; + #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum CompressionMode { CompressNone, @@ -114,3 +116,42 @@ pub fn is_bidi_control(c: char) -> bool { _ => false } } + +pub fn unicode_plane(codepoint: char) -> u32 { + (codepoint as u32) >> 16 +} + +pub fn is_cjk(codepoint: char) -> bool { + if let Some(block) = codepoint.block() { + match block { + UnicodeBlock::CJKRadicalsSupplement | + UnicodeBlock::KangxiRadicals | + UnicodeBlock::IdeographicDescriptionCharacters | + UnicodeBlock::CJKSymbolsandPunctuation | + UnicodeBlock::Hiragana | + UnicodeBlock::Katakana | + UnicodeBlock::Bopomofo | + UnicodeBlock::HangulCompatibilityJamo | + UnicodeBlock::Kanbun | + UnicodeBlock::BopomofoExtended | + UnicodeBlock::CJKStrokes | + UnicodeBlock::KatakanaPhoneticExtensions | + UnicodeBlock::EnclosedCJKLettersandMonths | + UnicodeBlock::CJKCompatibility | + UnicodeBlock::CJKUnifiedIdeographsExtensionA | + UnicodeBlock::YijingHexagramSymbols | + UnicodeBlock::CJKUnifiedIdeographs | + UnicodeBlock::CJKCompatibilityIdeographs | + UnicodeBlock::CJKCompatibilityForms | + UnicodeBlock::HalfwidthandFullwidthForms => { + return true + } + + _ => {} + } + } + + + // https://en.wikipedia.org/wiki/Plane_(Unicode)#Supplementary_Ideographic_Plane + unicode_plane(codepoint) == 2 +} diff --git a/components/style/properties/longhand/font.mako.rs b/components/style/properties/longhand/font.mako.rs index 7309aca8a38..0dca4a4c67e 100644 --- a/components/style/properties/longhand/font.mako.rs +++ b/components/style/properties/longhand/font.mako.rs @@ -84,7 +84,7 @@ ${helpers.predefined_type("font-synthesis", ${helpers.predefined_type( "font-stretch", "FontStretch", - initial_value="computed::NonNegativePercentage::hundred()", + initial_value="computed::FontStretch::hundred()", initial_specified_value="specified::FontStretch::normal()", animation_value_type="Percentage", flags="APPLIES_TO_FIRST_LETTER APPLIES_TO_FIRST_LINE APPLIES_TO_PLACEHOLDER", diff --git a/components/style/properties/properties.mako.rs b/components/style/properties/properties.mako.rs index 5e3d8eaeaaf..3e1da84d550 100644 --- a/components/style/properties/properties.mako.rs +++ b/components/style/properties/properties.mako.rs @@ -2305,13 +2305,10 @@ pub mod style_structs { pub fn compute_font_hash(&mut self) { // Corresponds to the fields in // `gfx::font_template::FontTemplateDescriptor`. - // - // FIXME(emilio): Where's font-style? let mut hasher: FnvHasher = Default::default(); - // We hash the floating point number with four decimal - // places. - hasher.write_u64((self.font_weight.0 * 10000.).trunc() as u64); - hasher.write_u64(((self.font_stretch.0).0 * 10000.).trunc() as u64); + self.font_weight.hash(&mut hasher); + self.font_stretch.hash(&mut hasher); + self.font_style.hash(&mut hasher); self.font_family.hash(&mut hasher); self.hash = hasher.finish() } diff --git a/components/style/values/computed/font.rs b/components/style/values/computed/font.rs index 1ca8400c16a..d486440163c 100644 --- a/components/style/values/computed/font.rs +++ b/components/style/values/computed/font.rs @@ -15,21 +15,20 @@ use gecko_bindings::sugar::refptr::RefPtr; #[cfg(feature = "gecko")] use malloc_size_of::{MallocSizeOf, MallocSizeOfOps}; use std::fmt::{self, Write}; -#[cfg(feature = "gecko")] use std::hash::{Hash, Hasher}; #[cfg(feature = "servo")] use std::slice; use style_traits::{CssWriter, ParseError, ToCss}; use values::CSSFloat; use values::animated::{ToAnimatedValue, ToAnimatedZero}; -use values::computed::{Angle, Context, Integer, NonNegativeLength, Number, ToComputedValue}; +use values::computed::{Angle, Context, Integer, NonNegative, NonNegativeLength, NonNegativePercentage}; +use values::computed::{Number, Percentage, ToComputedValue}; use values::generics::font::{self as generics, FeatureTagValue, FontSettings, VariationValue}; use values::specified::font::{self as specified, MIN_FONT_WEIGHT, MAX_FONT_WEIGHT}; use values::specified::length::{FontBaseSize, NoCalcLength}; pub use values::computed::Length as MozScriptMinSize; pub use values::specified::font::{FontSynthesis, MozScriptSizeMultiplier, XLang, XTextZoom}; -pub use values::computed::NonNegativePercentage as FontStretch; /// A value for the font-weight property per: /// @@ -41,6 +40,12 @@ pub use values::computed::NonNegativePercentage as FontStretch; #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] pub struct FontWeight(pub Number); +impl Hash for FontWeight { + fn hash<H: Hasher>(&self, hasher: &mut H) { + hasher.write_u64((self.0 * 10000.).trunc() as u64); + } +} + impl ToAnimatedValue for FontWeight { type AnimatedValue = Number; @@ -853,6 +858,12 @@ impl ToAnimatedValue for FontStyleAngle { } } +impl Hash for FontStyleAngle { + fn hash<H: Hasher>(&self, hasher: &mut H) { + hasher.write_u64((self.0.degrees() * 10000.).trunc() as u64); + } +} + /// The computed value of `font-style`. /// /// FIXME(emilio): Angle should be a custom type to handle clamping during @@ -916,3 +927,43 @@ impl ToCss for FontStyle { } } } + +/// A value for the font-stretch property per: +/// +/// https://drafts.csswg.org/css-fonts-4/#propdef-font-stretch +#[derive(Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, PartialEq, ToCss)] +#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] +pub struct FontStretch(pub NonNegativePercentage); + +impl FontStretch { + /// 100% + pub fn hundred() -> Self { + FontStretch(NonNegativePercentage::hundred()) + } + + /// The float value of the percentage + #[inline] + pub fn value(&self) -> CSSFloat { + ((self.0).0).0 + } +} + +impl ToAnimatedValue for FontStretch { + type AnimatedValue = Percentage; + + #[inline] + fn to_animated_value(self) -> Self::AnimatedValue { + (self.0).0 + } + + #[inline] + fn from_animated_value(animated: Self::AnimatedValue) -> Self { + FontStretch(NonNegative(animated)) + } +} + +impl Hash for FontStretch { + fn hash<H: Hasher>(&self, hasher: &mut H) { + hasher.write_u64((self.value() * 10000.).trunc() as u64); + } +} diff --git a/components/style/values/generics/font.rs b/components/style/values/generics/font.rs index f5e62e0ab5f..02df291065f 100644 --- a/components/style/values/generics/font.rs +++ b/components/style/values/generics/font.rs @@ -228,7 +228,7 @@ impl Default for KeywordSize { /// https://drafts.csswg.org/css-fonts-4/#font-style-prop #[allow(missing_docs)] #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, +#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, Hash, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToAnimatedValue, ToAnimatedZero)] pub enum FontStyle<Angle> { #[animation(error)] diff --git a/components/style/values/generics/mod.rs b/components/style/values/generics/mod.rs index 59d9748ea5b..0534e9512b4 100644 --- a/components/style/values/generics/mod.rs +++ b/components/style/values/generics/mod.rs @@ -156,7 +156,7 @@ impl SpecifiedValueInfo for CounterStyleOrNone { /// A wrapper of Non-negative values. #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] -#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, MallocSizeOf, +#[derive(Animate, Clone, ComputeSquaredDistance, Copy, Debug, Hash, MallocSizeOf, PartialEq, PartialOrd, SpecifiedValueInfo, ToAnimatedZero, ToComputedValue, ToCss)] pub struct NonNegative<T>(pub T); diff --git a/components/style/values/specified/font.rs b/components/style/values/specified/font.rs index 266d7c52c09..f26f5ab74a6 100644 --- a/components/style/values/specified/font.rs +++ b/components/style/values/specified/font.rs @@ -490,22 +490,22 @@ impl Parse for FontStretch { } impl ToComputedValue for FontStretch { - type ComputedValue = NonNegative<ComputedPercentage>; + type ComputedValue = computed::FontStretch; fn to_computed_value(&self, context: &Context) -> Self::ComputedValue { match *self { FontStretch::Stretch(ref percentage) => { - NonNegative(percentage.to_computed_value(context)) + computed::FontStretch(NonNegative(percentage.to_computed_value(context))) }, FontStretch::Keyword(ref kw) => { - NonNegative(kw.compute()) + computed::FontStretch(NonNegative(kw.compute())) }, FontStretch::System(_) => self.compute_system(context), } } fn from_computed_value(computed: &Self::ComputedValue) -> Self { - FontStretch::Stretch(Percentage::from_computed_value(&computed.0)) + FontStretch::Stretch(Percentage::from_computed_value(&(computed.0).0)) } } diff --git a/tests/wpt/metadata/css/CSS2/fonts/font-family-invalid-characters-001.xht.ini b/tests/wpt/metadata/css/CSS2/fonts/font-family-invalid-characters-001.xht.ini deleted file mode 100644 index 4d1ed22c544..00000000000 --- a/tests/wpt/metadata/css/CSS2/fonts/font-family-invalid-characters-001.xht.ini +++ /dev/null @@ -1,3 +0,0 @@ -[font-family-invalid-characters-001.xht] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata/css/CSS2/fonts/font-family-invalid-characters-003.xht.ini b/tests/wpt/metadata/css/CSS2/fonts/font-family-invalid-characters-003.xht.ini deleted file mode 100644 index eb1bdeed63f..00000000000 --- a/tests/wpt/metadata/css/CSS2/fonts/font-family-invalid-characters-003.xht.ini +++ /dev/null @@ -1,3 +0,0 @@ -[font-family-invalid-characters-003.xht] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata/css/CSS2/fonts/font-family-rule-001.xht.ini b/tests/wpt/metadata/css/CSS2/fonts/font-family-rule-001.xht.ini deleted file mode 100644 index d03c54d2aa5..00000000000 --- a/tests/wpt/metadata/css/CSS2/fonts/font-family-rule-001.xht.ini +++ /dev/null @@ -1,3 +0,0 @@ -[font-family-rule-001.xht] - type: reftest - expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-baspglwj-015.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-baspglwj-015.html.ini new file mode 100644 index 00000000000..b2944714a37 --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-baspglwj-015.html.ini @@ -0,0 +1,5 @@ +[css3-text-line-break-baspglwj-015.html] + type: testharness + [ ] + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-baspglwj-033.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-baspglwj-033.html.ini new file mode 100644 index 00000000000..6ae3d4d4574 --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-baspglwj-033.html.ini @@ -0,0 +1,5 @@ +[css3-text-line-break-baspglwj-033.html] + type: testharness + [ ] + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-baspglwj-034.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-baspglwj-034.html.ini new file mode 100644 index 00000000000..8fe184eb98e --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-baspglwj-034.html.ini @@ -0,0 +1,5 @@ +[css3-text-line-break-baspglwj-034.html] + type: testharness + [ ] + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-baspglwj-035.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-baspglwj-035.html.ini new file mode 100644 index 00000000000..caf9ac5e95c --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-baspglwj-035.html.ini @@ -0,0 +1,5 @@ +[css3-text-line-break-baspglwj-035.html] + type: testharness + [ ] + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-baspglwj-037.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-baspglwj-037.html.ini new file mode 100644 index 00000000000..95fd8784e41 --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-baspglwj-037.html.ini @@ -0,0 +1,5 @@ +[css3-text-line-break-baspglwj-037.html] + type: testharness + [ ] + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-006.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-006.html.ini index 197d51521a7..cdc62427d96 100644 --- a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-006.html.ini +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-006.html.ini @@ -1,4 +1,3 @@ [css3-text-line-break-opclns-006.html] type: reftest - expected: - if os == "linux": FAIL + expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-014.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-014.html.ini index 6cae1b5fd39..f1077a4ed9c 100644 --- a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-014.html.ini +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-014.html.ini @@ -1,4 +1,3 @@ [css3-text-line-break-opclns-014.html] type: reftest - expected: - if os == "linux": FAIL + expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-015.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-015.html.ini index ef5a5c13dcc..f979c174638 100644 --- a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-015.html.ini +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-015.html.ini @@ -1,4 +1,3 @@ [css3-text-line-break-opclns-015.html] type: reftest - expected: - if os == "linux": FAIL + expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-016.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-016.html.ini index c4bad14bb91..63c5f6a778b 100644 --- a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-016.html.ini +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-016.html.ini @@ -1,4 +1,3 @@ [css3-text-line-break-opclns-016.html] type: reftest - expected: - if os == "linux": FAIL + expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-018.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-018.html.ini index 3d29b63cd54..ebdd8cb09e1 100644 --- a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-018.html.ini +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-018.html.ini @@ -1,4 +1,3 @@ [css3-text-line-break-opclns-018.html] type: reftest - expected: - if os == "linux": FAIL + expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-019.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-019.html.ini index bb609b15f14..19c2a8dd998 100644 --- a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-019.html.ini +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-019.html.ini @@ -1,4 +1,3 @@ [css3-text-line-break-opclns-019.html] type: reftest - expected: - if os == "linux": FAIL + expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-020.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-020.html.ini index 79a3ff6e3ff..e7a42c9b605 100644 --- a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-020.html.ini +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-020.html.ini @@ -1,4 +1,3 @@ [css3-text-line-break-opclns-020.html] type: reftest - expected: - if os == "linux": FAIL + expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-023.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-023.html.ini index 800206f5900..4182bf08d8f 100644 --- a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-023.html.ini +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-023.html.ini @@ -1,4 +1,3 @@ [css3-text-line-break-opclns-023.html] type: reftest - expected: - if os == "linux": FAIL + expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-025.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-025.html.ini new file mode 100644 index 00000000000..917909cdca9 --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-025.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-025.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-026.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-026.html.ini new file mode 100644 index 00000000000..8e0d0a460cc --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-026.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-026.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-027.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-027.html.ini new file mode 100644 index 00000000000..8d54ade282c --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-027.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-027.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-028.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-028.html.ini new file mode 100644 index 00000000000..f32f2b8abaf --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-028.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-028.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-029.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-029.html.ini new file mode 100644 index 00000000000..b11cd8b20d6 --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-029.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-029.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-030.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-030.html.ini new file mode 100644 index 00000000000..4609487b38b --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-030.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-030.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-031.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-031.html.ini new file mode 100644 index 00000000000..8aff58db107 --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-031.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-031.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-032.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-032.html.ini new file mode 100644 index 00000000000..dc8cf7818ef --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-032.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-032.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-033.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-033.html.ini new file mode 100644 index 00000000000..156a5f02edc --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-033.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-033.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-034.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-034.html.ini new file mode 100644 index 00000000000..d16116ea6ee --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-034.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-034.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-035.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-035.html.ini new file mode 100644 index 00000000000..2dc431dcef5 --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-035.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-035.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-036.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-036.html.ini new file mode 100644 index 00000000000..b45db5927cb --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-036.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-036.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-037.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-037.html.ini new file mode 100644 index 00000000000..745f13596f4 --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-037.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-037.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-119.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-119.html.ini index a486bfd4dbb..f3c7fca720c 100644 --- a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-119.html.ini +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-119.html.ini @@ -1,4 +1,3 @@ [css3-text-line-break-opclns-119.html] type: reftest - expected: - if os == "linux": FAIL + expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-120.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-120.html.ini index c7c9eedf700..9625057e151 100644 --- a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-120.html.ini +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-120.html.ini @@ -1,4 +1,3 @@ [css3-text-line-break-opclns-120.html] type: reftest - expected: - if os == "linux": FAIL + expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-122.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-122.html.ini index 1bae83706db..bb96543f1c8 100644 --- a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-122.html.ini +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-122.html.ini @@ -1,4 +1,3 @@ [css3-text-line-break-opclns-122.html] type: reftest - expected: - if os == "linux": FAIL + expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-123.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-123.html.ini index c45d2eb11e8..a6463e07e65 100644 --- a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-123.html.ini +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-123.html.ini @@ -1,4 +1,3 @@ [css3-text-line-break-opclns-123.html] type: reftest - expected: - if os == "linux": FAIL + expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-124.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-124.html.ini index 75d6e2cbe81..d1e73c90bfd 100644 --- a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-124.html.ini +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-124.html.ini @@ -1,4 +1,3 @@ [css3-text-line-break-opclns-124.html] type: reftest - expected: - if os == "linux": FAIL + expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-125.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-125.html.ini index 5697632f9b4..d7965a69364 100644 --- a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-125.html.ini +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-125.html.ini @@ -1,4 +1,3 @@ [css3-text-line-break-opclns-125.html] type: reftest - expected: - if os == "linux": FAIL + expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-128.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-128.html.ini index 19932cac3b2..d0132636eb5 100644 --- a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-128.html.ini +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-128.html.ini @@ -1,4 +1,3 @@ [css3-text-line-break-opclns-128.html] type: reftest - expected: - if os == "linux": FAIL + expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-130.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-130.html.ini new file mode 100644 index 00000000000..90a9908eb57 --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-130.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-130.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-131.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-131.html.ini new file mode 100644 index 00000000000..5718f0b4cf1 --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-131.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-131.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-132.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-132.html.ini new file mode 100644 index 00000000000..8c1daa0e851 --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-132.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-132.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-133.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-133.html.ini new file mode 100644 index 00000000000..43683c29d36 --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-133.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-133.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-134.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-134.html.ini new file mode 100644 index 00000000000..6b1f4d8d0cf --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-134.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-134.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-135.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-135.html.ini new file mode 100644 index 00000000000..204d22ff55b --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-135.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-135.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-136.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-136.html.ini new file mode 100644 index 00000000000..0e0794ecf18 --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-136.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-136.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-137.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-137.html.ini new file mode 100644 index 00000000000..ffe57871b87 --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-137.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-137.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-138.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-138.html.ini new file mode 100644 index 00000000000..3941a5c2344 --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-138.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-138.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-139.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-139.html.ini new file mode 100644 index 00000000000..59e58c99d5a --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-139.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-139.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-140.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-140.html.ini new file mode 100644 index 00000000000..b719e73370e --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-140.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-140.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-141.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-141.html.ini new file mode 100644 index 00000000000..0ad2710e4d7 --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-141.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-141.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-142.html.ini b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-142.html.ini new file mode 100644 index 00000000000..cc85a3d0e94 --- /dev/null +++ b/tests/wpt/metadata/css/css-text/i18n/css3-text-line-break-opclns-142.html.ini @@ -0,0 +1,4 @@ +[css3-text-line-break-opclns-142.html] + type: reftest + expected: + if os == "mac": FAIL diff --git a/tests/wpt/metadata/css/css-text/word-break/word-break-break-all-007.html.ini b/tests/wpt/metadata/css/css-text/word-break/word-break-break-all-007.html.ini index 69aeb33957c..189f2d4dfec 100644 --- a/tests/wpt/metadata/css/css-text/word-break/word-break-break-all-007.html.ini +++ b/tests/wpt/metadata/css/css-text/word-break/word-break-break-all-007.html.ini @@ -1,4 +1,3 @@ [word-break-break-all-007.html] type: reftest - expected: - if os == "linux": FAIL + expected: FAIL diff --git a/tests/wpt/metadata/css/css-text/word-break/word-break-normal-bo-000.html.ini b/tests/wpt/metadata/css/css-text/word-break/word-break-normal-bo-000.html.ini index c7554ba00fb..fb935620095 100644 --- a/tests/wpt/metadata/css/css-text/word-break/word-break-normal-bo-000.html.ini +++ b/tests/wpt/metadata/css/css-text/word-break/word-break-normal-bo-000.html.ini @@ -1,3 +1,4 @@ [word-break-normal-bo-000.html] type: reftest - expected: FAIL + expected: + if os == "linux": FAIL diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 47eea45772b..7aebc31af5c 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -2267,6 +2267,42 @@ {} ] ], + "css/font_fallback_01.html": [ + [ + "/_mozilla/css/font_fallback_01.html", + [ + [ + "/_mozilla/css/font_fallback_failed_ref.html", + "!=" + ] + ], + {} + ] + ], + "css/font_fallback_02.html": [ + [ + "/_mozilla/css/font_fallback_02.html", + [ + [ + "/_mozilla/css/font_fallback_failed_ref.html", + "!=" + ] + ], + {} + ] + ], + "css/font_fallback_03.html": [ + [ + "/_mozilla/css/font_fallback_03.html", + [ + [ + "/_mozilla/css/font_fallback_failed_ref.html", + "!=" + ] + ], + {} + ] + ], "css/font_size.html": [ [ "/_mozilla/css/font_size.html", @@ -8248,6 +8284,11 @@ {} ] ], + "css/font_fallback_failed_ref.html": [ + [ + {} + ] + ], "css/font_size_ref.html": [ [ {} @@ -61500,6 +61541,22 @@ "0c8f70a37e6d56370576c7f5ba7f1df359db8f9c", "support" ], + "css/font_fallback_01.html": [ + "1dee8f90d8d1ee1f91e9ad9d1442a3abfec5d596", + "reftest" + ], + "css/font_fallback_02.html": [ + "60d61d9d51206c8b741ef7914447329722950d67", + "reftest" + ], + "css/font_fallback_03.html": [ + "4ef0d7e423624e93b3f028d9baa5a11f9e8f28b2", + "reftest" + ], + "css/font_fallback_failed_ref.html": [ + "ebc502a8b3e0fd4dd5eb79a1dae83ab3e6774178", + "support" + ], "css/font_size.html": [ "7c944acb4f7d909083888a355e6d664c23081b99", "reftest" diff --git a/tests/wpt/mozilla/meta/css/font_fallback_01.html.ini b/tests/wpt/mozilla/meta/css/font_fallback_01.html.ini new file mode 100644 index 00000000000..58e23d74cf3 --- /dev/null +++ b/tests/wpt/mozilla/meta/css/font_fallback_01.html.ini @@ -0,0 +1,4 @@ +[font_fallback_01.html] + type: reftest + expected: + if os == "linux": FAIL diff --git a/tests/wpt/mozilla/meta/css/font_fallback_02.html.ini b/tests/wpt/mozilla/meta/css/font_fallback_02.html.ini new file mode 100644 index 00000000000..e4e5cd5638c --- /dev/null +++ b/tests/wpt/mozilla/meta/css/font_fallback_02.html.ini @@ -0,0 +1,4 @@ +[font_fallback_02.html] + type: reftest + expected: + if os == "linux": FAIL diff --git a/tests/wpt/mozilla/tests/css/font_fallback_01.html b/tests/wpt/mozilla/tests/css/font_fallback_01.html new file mode 100644 index 00000000000..306c26fd6f8 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/font_fallback_01.html @@ -0,0 +1,4 @@ +<!doctype html> +<link rel="mismatch" href="font_fallback_failed_ref.html"> +<meta name="assert" content="A fallback font must be found for a hiragana character"> +の diff --git a/tests/wpt/mozilla/tests/css/font_fallback_02.html b/tests/wpt/mozilla/tests/css/font_fallback_02.html new file mode 100644 index 00000000000..0ca0a826865 --- /dev/null +++ b/tests/wpt/mozilla/tests/css/font_fallback_02.html @@ -0,0 +1,4 @@ +<!doctype html> +<link rel="mismatch" href="font_fallback_failed_ref.html"> +<meta name="assert" content="A fallback font must be found for a katakana character"> +コ diff --git a/tests/wpt/mozilla/tests/css/font_fallback_03.html b/tests/wpt/mozilla/tests/css/font_fallback_03.html new file mode 100644 index 00000000000..9b413df19df --- /dev/null +++ b/tests/wpt/mozilla/tests/css/font_fallback_03.html @@ -0,0 +1,4 @@ +<!doctype html> +<link rel="mismatch" href="font_fallback_failed_ref.html"> +<meta name="assert" content="A fallback font must be found for an emoji with text presentation"> +✂ diff --git a/tests/wpt/mozilla/tests/css/font_fallback_failed_ref.html b/tests/wpt/mozilla/tests/css/font_fallback_failed_ref.html new file mode 100644 index 00000000000..6cc621b90ea --- /dev/null +++ b/tests/wpt/mozilla/tests/css/font_fallback_failed_ref.html @@ -0,0 +1,3 @@ +<!doctype html> +<!-- This codepoint is in the Private Use Area, therefore we would expect it to render as a missing glyph. --> + |