mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
Auto merge of #15356 - servo:font-face-descriptors, r=Manishearth
Add support for more @font-face descriptors <!-- Please describe your changes on the following line: --> Part of https://bugzilla.mozilla.org/show_bug.cgi?id=1290237. I’ll add conversions to `nsCSSValue` separately because that requires new C++ functions in the stylo repository. r? @bholley --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [ ] These changes fix #__ (github issue number if applicable). <!-- Either: --> - [ ] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/15356) <!-- Reviewable:end -->
This commit is contained in:
commit
48f3cc8325
10 changed files with 370 additions and 162 deletions
|
@ -25,7 +25,7 @@ use std::sync::{Arc, Mutex};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::u32;
|
use std::u32;
|
||||||
use style::font_face::{EffectiveSources, Source};
|
use style::font_face::{EffectiveSources, Source};
|
||||||
use style::properties::longhands::font_family::computed_value::FontFamily;
|
use style::properties::longhands::font_family::computed_value::{FontFamily, FamilyName};
|
||||||
use webrender_traits;
|
use webrender_traits;
|
||||||
|
|
||||||
/// A list of font templates that make up a given font family.
|
/// A list of font templates that make up a given font family.
|
||||||
|
@ -269,7 +269,7 @@ impl FontCache {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Source::Local(ref font) => {
|
Source::Local(ref font) => {
|
||||||
let font_face_name = LowercaseString::new(font.name());
|
let font_face_name = LowercaseString::new(&font.0);
|
||||||
let templates = &mut self.web_families.get_mut(&family_name).unwrap();
|
let templates = &mut self.web_families.get_mut(&family_name).unwrap();
|
||||||
let mut found = false;
|
let mut found = false;
|
||||||
for_each_variation(&font_face_name, |path| {
|
for_each_variation(&font_face_name, |path| {
|
||||||
|
@ -461,8 +461,8 @@ impl FontCacheThread {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_web_font(&self, family: FontFamily, sources: EffectiveSources, sender: IpcSender<()>) {
|
pub fn add_web_font(&self, family: FamilyName, sources: EffectiveSources, sender: IpcSender<()>) {
|
||||||
self.chan.send(Command::AddWebFont(LowercaseString::new(family.name()), sources, sender)).unwrap();
|
self.chan.send(Command::AddWebFont(LowercaseString::new(&family.0), sources, sender)).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn exit(&self) {
|
pub fn exit(&self) {
|
||||||
|
|
|
@ -8,13 +8,14 @@
|
||||||
|
|
||||||
#![deny(missing_docs)]
|
#![deny(missing_docs)]
|
||||||
|
|
||||||
use computed_values::font_family::FontFamily;
|
#[cfg(feature = "gecko")]
|
||||||
|
use computed_values::{font_style, font_weight, font_stretch};
|
||||||
|
use computed_values::font_family::FamilyName;
|
||||||
use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser};
|
use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser};
|
||||||
use parser::{ParserContext, log_css_error, Parse};
|
use parser::{ParserContext, log_css_error, Parse};
|
||||||
use properties::longhands::font_family::parse_one_family;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use style_traits::ToCss;
|
use style_traits::{ToCss, OneOrMoreCommaSeparated};
|
||||||
use values::specified::url::SpecifiedUrl;
|
use values::specified::url::SpecifiedUrl;
|
||||||
|
|
||||||
/// A source for a font-face rule.
|
/// A source for a font-face rule.
|
||||||
|
@ -24,7 +25,7 @@ pub enum Source {
|
||||||
/// A `url()` source.
|
/// A `url()` source.
|
||||||
Url(UrlSource),
|
Url(UrlSource),
|
||||||
/// A `local()` source.
|
/// A `local()` source.
|
||||||
Local(FontFamily),
|
Local(FamilyName),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToCss for Source {
|
impl ToCss for Source {
|
||||||
|
@ -45,6 +46,8 @@ impl ToCss for Source {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl OneOrMoreCommaSeparated for Source {}
|
||||||
|
|
||||||
/// A `UrlSource` represents a font-face source that has been specified with a
|
/// A `UrlSource` represents a font-face source that has been specified with a
|
||||||
/// `url()` function.
|
/// `url()` function.
|
||||||
///
|
///
|
||||||
|
@ -66,76 +69,32 @@ impl ToCss for UrlSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A `@font-face` rule.
|
|
||||||
///
|
|
||||||
/// https://drafts.csswg.org/css-fonts/#font-face-rule
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
|
||||||
pub struct FontFaceRule {
|
|
||||||
/// The font family specified with the `font-family` property declaration.
|
|
||||||
pub family: FontFamily,
|
|
||||||
/// The list of sources specified with the different `src` property
|
|
||||||
/// declarations.
|
|
||||||
pub sources: Vec<Source>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToCss for FontFaceRule {
|
|
||||||
// Serialization of FontFaceRule is not specced.
|
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
|
||||||
where W: fmt::Write,
|
|
||||||
{
|
|
||||||
try!(dest.write_str("@font-face { font-family: "));
|
|
||||||
try!(self.family.to_css(dest));
|
|
||||||
try!(dest.write_str(";"));
|
|
||||||
|
|
||||||
if self.sources.len() > 0 {
|
|
||||||
try!(dest.write_str(" src: "));
|
|
||||||
let mut iter = self.sources.iter();
|
|
||||||
try!(iter.next().unwrap().to_css(dest));
|
|
||||||
for source in iter {
|
|
||||||
try!(dest.write_str(", "));
|
|
||||||
try!(source.to_css(dest));
|
|
||||||
}
|
|
||||||
try!(dest.write_str(";"));
|
|
||||||
}
|
|
||||||
|
|
||||||
dest.write_str(" }")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse the block inside a `@font-face` rule.
|
/// Parse the block inside a `@font-face` rule.
|
||||||
///
|
///
|
||||||
/// Note that the prelude parsing code lives in the `stylesheets` module.
|
/// Note that the prelude parsing code lives in the `stylesheets` module.
|
||||||
pub fn parse_font_face_block(context: &ParserContext, input: &mut Parser)
|
pub fn parse_font_face_block(context: &ParserContext, input: &mut Parser)
|
||||||
-> Result<FontFaceRule, ()> {
|
-> Result<FontFaceRule, ()> {
|
||||||
let mut family = None;
|
let mut rule = FontFaceRule::initial();
|
||||||
let mut src = None;
|
{
|
||||||
let mut iter = DeclarationListParser::new(input, FontFaceRuleParser { context: context });
|
let parser = FontFaceRuleParser {
|
||||||
while let Some(declaration) = iter.next() {
|
context: context,
|
||||||
match declaration {
|
rule: &mut rule,
|
||||||
Err(range) => {
|
missing: MissingDescriptors::new(),
|
||||||
|
};
|
||||||
|
let mut iter = DeclarationListParser::new(input, parser);
|
||||||
|
while let Some(declaration) = iter.next() {
|
||||||
|
if let Err(range) = declaration {
|
||||||
let pos = range.start;
|
let pos = range.start;
|
||||||
let message = format!("Unsupported @font-face descriptor declaration: '{}'",
|
let message = format!("Unsupported @font-face descriptor declaration: '{}'",
|
||||||
iter.input.slice(range));
|
iter.input.slice(range));
|
||||||
log_css_error(iter.input, pos, &*message, context);
|
log_css_error(iter.input, pos, &*message, context);
|
||||||
}
|
}
|
||||||
Ok(FontFaceDescriptorDeclaration::Family(value)) => {
|
}
|
||||||
family = Some(value);
|
if iter.parser.missing.any() {
|
||||||
}
|
return Err(())
|
||||||
Ok(FontFaceDescriptorDeclaration::Src(value)) => {
|
|
||||||
src = Some(value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
match (family, src) {
|
Ok(rule)
|
||||||
(Some(family), Some(src)) => {
|
|
||||||
Ok(FontFaceRule {
|
|
||||||
family: family,
|
|
||||||
sources: src,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
_ => Err(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A list of effective sources that we send over through IPC to the font cache.
|
/// A list of effective sources that we send over through IPC to the font cache.
|
||||||
|
@ -171,63 +130,237 @@ impl iter::Iterator for EffectiveSources {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum FontFaceDescriptorDeclaration {
|
|
||||||
Family(FontFamily),
|
|
||||||
Src(Vec<Source>),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct FontFaceRuleParser<'a, 'b: 'a> {
|
struct FontFaceRuleParser<'a, 'b: 'a> {
|
||||||
context: &'a ParserContext<'b>,
|
context: &'a ParserContext<'b>,
|
||||||
|
rule: &'a mut FontFaceRule,
|
||||||
|
missing: MissingDescriptors,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Default methods reject all at rules.
|
/// Default methods reject all at rules.
|
||||||
impl<'a, 'b> AtRuleParser for FontFaceRuleParser<'a, 'b> {
|
impl<'a, 'b> AtRuleParser for FontFaceRuleParser<'a, 'b> {
|
||||||
type Prelude = ();
|
type Prelude = ();
|
||||||
type AtRule = FontFaceDescriptorDeclaration;
|
type AtRule = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Parse for Source {
|
||||||
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Source, ()> {
|
||||||
|
if input.try(|input| input.expect_function_matching("local")).is_ok() {
|
||||||
|
return input.parse_nested_block(|input| {
|
||||||
|
FamilyName::parse(context, input)
|
||||||
|
}).map(Source::Local)
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, 'b> DeclarationParser for FontFaceRuleParser<'a, 'b> {
|
let url = SpecifiedUrl::parse(context, input)?;
|
||||||
type Declaration = FontFaceDescriptorDeclaration;
|
|
||||||
|
|
||||||
fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<FontFaceDescriptorDeclaration, ()> {
|
// Parsing optional format()
|
||||||
match_ignore_ascii_case! { name,
|
let format_hints = if input.try(|input| input.expect_function_matching("format")).is_ok() {
|
||||||
"font-family" => {
|
input.parse_nested_block(|input| {
|
||||||
Ok(FontFaceDescriptorDeclaration::Family(try!(
|
input.parse_comma_separated(|input| {
|
||||||
parse_one_family(input))))
|
Ok(input.expect_string()?.into_owned())
|
||||||
},
|
})
|
||||||
"src" => {
|
})?
|
||||||
Ok(FontFaceDescriptorDeclaration::Src(try!(input.parse_comma_separated(|input| {
|
} else {
|
||||||
parse_one_src(self.context, input)
|
vec![]
|
||||||
}))))
|
};
|
||||||
},
|
|
||||||
_ => Err(())
|
Ok(Source::Url(UrlSource {
|
||||||
|
url: url,
|
||||||
|
format_hints: format_hints,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! font_face_descriptors {
|
||||||
|
(
|
||||||
|
mandatory descriptors = [
|
||||||
|
$( #[$m_doc: meta] $m_name: tt $m_ident: ident: $m_ty: ty = $m_initial: expr, )*
|
||||||
|
]
|
||||||
|
optional descriptors = [
|
||||||
|
$( #[$o_doc: meta] $o_name: tt $o_ident: ident: $o_ty: ty = $o_initial: expr, )*
|
||||||
|
]
|
||||||
|
) => {
|
||||||
|
/// A `@font-face` rule.
|
||||||
|
///
|
||||||
|
/// https://drafts.csswg.org/css-fonts/#font-face-rule
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||||
|
pub struct FontFaceRule {
|
||||||
|
$(
|
||||||
|
#[$m_doc]
|
||||||
|
pub $m_ident: $m_ty,
|
||||||
|
)*
|
||||||
|
$(
|
||||||
|
#[$o_doc]
|
||||||
|
pub $o_ident: $o_ty,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MissingDescriptors {
|
||||||
|
$(
|
||||||
|
$m_ident: bool,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MissingDescriptors {
|
||||||
|
fn new() -> Self {
|
||||||
|
MissingDescriptors {
|
||||||
|
$(
|
||||||
|
$m_ident: true,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn any(&self) -> bool {
|
||||||
|
$(
|
||||||
|
self.$m_ident
|
||||||
|
)||*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FontFaceRule {
|
||||||
|
fn initial() -> Self {
|
||||||
|
FontFaceRule {
|
||||||
|
$(
|
||||||
|
$m_ident: $m_initial,
|
||||||
|
)*
|
||||||
|
$(
|
||||||
|
$o_ident: $o_initial,
|
||||||
|
)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for FontFaceRule {
|
||||||
|
// Serialization of FontFaceRule is not specced.
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||||
|
where W: fmt::Write,
|
||||||
|
{
|
||||||
|
dest.write_str("@font-face {\n")?;
|
||||||
|
$(
|
||||||
|
dest.write_str(concat!(" ", $m_name, ": "))?;
|
||||||
|
self.$m_ident.to_css(dest)?;
|
||||||
|
dest.write_str(";\n")?;
|
||||||
|
)*
|
||||||
|
$(
|
||||||
|
// Because of parse_font_face_block,
|
||||||
|
// this condition is always true for "src" and "font-family".
|
||||||
|
// But it can be false for other descriptors.
|
||||||
|
if self.$o_ident != $o_initial {
|
||||||
|
dest.write_str(concat!(" ", $o_name, ": "))?;
|
||||||
|
self.$o_ident.to_css(dest)?;
|
||||||
|
dest.write_str(";\n")?;
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
dest.write_str("}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> DeclarationParser for FontFaceRuleParser<'a, 'b> {
|
||||||
|
type Declaration = ();
|
||||||
|
|
||||||
|
fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<(), ()> {
|
||||||
|
match_ignore_ascii_case! { name,
|
||||||
|
$(
|
||||||
|
$m_name => {
|
||||||
|
self.rule.$m_ident = Parse::parse(self.context, input)?;
|
||||||
|
self.missing.$m_ident = false
|
||||||
|
},
|
||||||
|
)*
|
||||||
|
$(
|
||||||
|
$o_name => self.rule.$o_ident = Parse::parse(self.context, input)?,
|
||||||
|
)*
|
||||||
|
_ => return Err(())
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_one_src(context: &ParserContext, input: &mut Parser) -> Result<Source, ()> {
|
/// css-name rust_identifier: Type = initial_value,
|
||||||
if input.try(|input| input.expect_function_matching("local")).is_ok() {
|
#[cfg(feature = "gecko")]
|
||||||
return Ok(Source::Local(try!(input.parse_nested_block(parse_one_family))))
|
font_face_descriptors! {
|
||||||
|
mandatory descriptors = [
|
||||||
|
/// The name of this font face
|
||||||
|
"font-family" family: FamilyName = FamilyName(atom!("")),
|
||||||
|
|
||||||
|
/// The alternative sources for this font face.
|
||||||
|
"src" sources: Vec<Source> = Vec::new(),
|
||||||
|
]
|
||||||
|
optional descriptors = [
|
||||||
|
/// The style of this font face
|
||||||
|
"font-style" style: font_style::T = font_style::T::normal,
|
||||||
|
|
||||||
|
/// The weight of this font face
|
||||||
|
"font-weight" weight: font_weight::T = font_weight::T::Weight400 /* normal */,
|
||||||
|
|
||||||
|
/// The stretch of this font face
|
||||||
|
"font-stretch" stretch: font_stretch::T = font_stretch::T::normal,
|
||||||
|
|
||||||
|
/// The ranges of code points outside of which this font face should not be used.
|
||||||
|
"unicode-range" unicode_range: Vec<unicode_range::Range> = vec![
|
||||||
|
unicode_range::Range { start: 0, end: unicode_range::MAX }
|
||||||
|
],
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "servo")]
|
||||||
|
font_face_descriptors! {
|
||||||
|
mandatory descriptors = [
|
||||||
|
/// The name of this font face
|
||||||
|
"font-family" family: FamilyName = FamilyName(atom!("")),
|
||||||
|
|
||||||
|
/// The alternative sources for this font face.
|
||||||
|
"src" sources: Vec<Source> = Vec::new(),
|
||||||
|
]
|
||||||
|
optional descriptors = [
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// https://drafts.csswg.org/css-fonts/#unicode-range-desc
|
||||||
|
#[cfg(feature = "gecko")]
|
||||||
|
pub mod unicode_range {
|
||||||
|
use cssparser::{Parser, Token};
|
||||||
|
use parser::{ParserContext, Parse};
|
||||||
|
use std::fmt;
|
||||||
|
use style_traits::{ToCss, OneOrMoreCommaSeparated};
|
||||||
|
|
||||||
|
/// Maximum value of the end of a range
|
||||||
|
pub const MAX: u32 = ::std::char::MAX as u32;
|
||||||
|
|
||||||
|
/// A single range: https://drafts.csswg.org/css-fonts/#urange-value
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct Range {
|
||||||
|
/// Start of the range, inclusive
|
||||||
|
pub start: u32,
|
||||||
|
|
||||||
|
/// End of the range, inclusive
|
||||||
|
pub end: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
let url = try!(SpecifiedUrl::parse(context, input));
|
impl OneOrMoreCommaSeparated for Range {}
|
||||||
|
|
||||||
// Parsing optional format()
|
impl Parse for Range {
|
||||||
let format_hints = if input.try(|input| input.expect_function_matching("format")).is_ok() {
|
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
try!(input.parse_nested_block(|input| {
|
// FIXME: The unicode-range token has been removed from the CSS Syntax spec,
|
||||||
input.parse_comma_separated(|input| {
|
// cssparser should be updated accordingly
|
||||||
Ok((try!(input.expect_string())).into_owned())
|
// and implement https://drafts.csswg.org/css-syntax/#urange instead
|
||||||
})
|
match input.next() {
|
||||||
}))
|
Ok(Token::UnicodeRange(start, end)) => {
|
||||||
} else {
|
if end <= MAX && start <= end {
|
||||||
vec![]
|
Ok(Range { start: start, end: end })
|
||||||
};
|
} else {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Source::Url(UrlSource {
|
impl ToCss for Range {
|
||||||
url: url,
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
format_hints: format_hints,
|
Token::UnicodeRange(self.start, self.end).to_css(dest)
|
||||||
}))
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ use error_reporting::ParseErrorReporter;
|
||||||
#[cfg(feature = "gecko")]
|
#[cfg(feature = "gecko")]
|
||||||
use gecko_bindings::sugar::refptr::{GeckoArcPrincipal, GeckoArcURI};
|
use gecko_bindings::sugar::refptr::{GeckoArcPrincipal, GeckoArcURI};
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
|
use style_traits::OneOrMoreCommaSeparated;
|
||||||
use stylesheets::{MemoryHoleReporter, Origin};
|
use stylesheets::{MemoryHoleReporter, Origin};
|
||||||
|
|
||||||
/// Extra data that the style backend may need to parse stylesheets.
|
/// Extra data that the style backend may need to parse stylesheets.
|
||||||
|
@ -102,3 +103,9 @@ pub trait Parse : Sized {
|
||||||
/// Returns an error on failure.
|
/// Returns an error on failure.
|
||||||
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()>;
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Parse for Vec<T> where T: Parse + OneOrMoreCommaSeparated {
|
||||||
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
|
input.parse_comma_separated(|input| T::parse(context, input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -944,7 +944,7 @@ fn static_assert() {
|
||||||
for family in &v.0 {
|
for family in &v.0 {
|
||||||
match *family {
|
match *family {
|
||||||
FontFamily::FamilyName(ref name) => {
|
FontFamily::FamilyName(ref name) => {
|
||||||
unsafe { Gecko_FontFamilyList_AppendNamed(list, name.as_ptr()); }
|
unsafe { Gecko_FontFamilyList_AppendNamed(list, name.0.as_ptr()); }
|
||||||
}
|
}
|
||||||
FontFamily::Generic(ref name) => {
|
FontFamily::Generic(ref name) => {
|
||||||
let family_type =
|
let family_type =
|
||||||
|
|
|
@ -380,6 +380,13 @@
|
||||||
-> Result<SpecifiedValue, ()> {
|
-> Result<SpecifiedValue, ()> {
|
||||||
SpecifiedValue::parse(input)
|
SpecifiedValue::parse(input)
|
||||||
}
|
}
|
||||||
|
impl Parse for SpecifiedValue {
|
||||||
|
#[inline]
|
||||||
|
fn parse(_context: &ParserContext, input: &mut Parser)
|
||||||
|
-> Result<SpecifiedValue, ()> {
|
||||||
|
SpecifiedValue::parse(input)
|
||||||
|
}
|
||||||
|
}
|
||||||
</%def>
|
</%def>
|
||||||
% if vector:
|
% if vector:
|
||||||
<%call expr="vector_longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
|
<%call expr="vector_longhand(name, keyword=Keyword(name, values, **keyword_kwargs), **kwargs)">
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
additional_methods=[Method("compute_font_hash", is_mut=True)]) %>
|
additional_methods=[Method("compute_font_hash", is_mut=True)]) %>
|
||||||
<%helpers:longhand name="font-family" animatable="False" need_index="True"
|
<%helpers:longhand name="font-family" animatable="False" need_index="True"
|
||||||
spec="https://drafts.csswg.org/css-fonts/#propdef-font-family">
|
spec="https://drafts.csswg.org/css-fonts/#propdef-font-family">
|
||||||
use self::computed_value::FontFamily;
|
use self::computed_value::{FontFamily, FamilyName};
|
||||||
use values::NoViewportPercentage;
|
use values::NoViewportPercentage;
|
||||||
use values::computed::ComputedValueAsSpecified;
|
use values::computed::ComputedValueAsSpecified;
|
||||||
pub use self::computed_value::T as SpecifiedValue;
|
pub use self::computed_value::T as SpecifiedValue;
|
||||||
|
@ -28,15 +28,19 @@
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
|
||||||
pub enum FontFamily {
|
pub enum FontFamily {
|
||||||
FamilyName(Atom),
|
FamilyName(FamilyName),
|
||||||
Generic(Atom),
|
Generic(Atom),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||||
|
#[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
|
||||||
|
pub struct FamilyName(pub Atom);
|
||||||
|
|
||||||
impl FontFamily {
|
impl FontFamily {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn atom(&self) -> &Atom {
|
pub fn atom(&self) -> &Atom {
|
||||||
match *self {
|
match *self {
|
||||||
FontFamily::FamilyName(ref name) => name,
|
FontFamily::FamilyName(ref name) => &name.0,
|
||||||
FontFamily::Generic(ref name) => name,
|
FontFamily::Generic(ref name) => name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,18 +71,22 @@
|
||||||
"monospace" => return FontFamily::Generic(atom!("monospace")),
|
"monospace" => return FontFamily::Generic(atom!("monospace")),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
FontFamily::FamilyName(input)
|
FontFamily::FamilyName(FamilyName(input))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToCss for FamilyName {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
dest.write_char('"')?;
|
||||||
|
write!(CssStringWriter::new(dest), "{}", self.0)?;
|
||||||
|
dest.write_char('"')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToCss for FontFamily {
|
impl ToCss for FontFamily {
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
match *self {
|
match *self {
|
||||||
FontFamily::FamilyName(ref name) => {
|
FontFamily::FamilyName(ref name) => name.to_css(dest),
|
||||||
dest.write_char('"')?;
|
|
||||||
write!(CssStringWriter::new(dest), "{}", name)?;
|
|
||||||
dest.write_char('"')
|
|
||||||
}
|
|
||||||
|
|
||||||
// All generic values accepted by the parser are known to not require escaping.
|
// All generic values accepted by the parser are known to not require escaping.
|
||||||
FontFamily::Generic(ref name) => write!(dest, "{}", name),
|
FontFamily::Generic(ref name) => write!(dest, "{}", name),
|
||||||
|
@ -107,56 +115,78 @@
|
||||||
pub fn get_initial_value() -> computed_value::T {
|
pub fn get_initial_value() -> computed_value::T {
|
||||||
computed_value::T(vec![FontFamily::Generic(atom!("serif"))])
|
computed_value::T(vec![FontFamily::Generic(atom!("serif"))])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <family-name>#
|
/// <family-name>#
|
||||||
/// <family-name> = <string> | [ <ident>+ ]
|
/// <family-name> = <string> | [ <ident>+ ]
|
||||||
/// TODO: <generic-family>
|
/// TODO: <generic-family>
|
||||||
pub fn parse(_context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
pub fn parse(context: &ParserContext, input: &mut Parser) -> Result<SpecifiedValue, ()> {
|
||||||
input.parse_comma_separated(parse_one_family).map(SpecifiedValue)
|
Vec::<FontFamily>::parse(context, input).map(SpecifiedValue)
|
||||||
}
|
}
|
||||||
pub fn parse_one_family(input: &mut Parser) -> Result<FontFamily, ()> {
|
|
||||||
if let Ok(value) = input.try(|input| input.expect_string()) {
|
|
||||||
return Ok(FontFamily::FamilyName(Atom::from(&*value)))
|
|
||||||
}
|
|
||||||
let first_ident = try!(input.expect_ident());
|
|
||||||
|
|
||||||
// FIXME(bholley): The fast thing to do here would be to look up the
|
impl Parse for Vec<FontFamily> {
|
||||||
// string (as lowercase) in the static atoms table. We don't have an
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
// API to do that yet though, so we do the simple thing for now.
|
input.parse_comma_separated(|input| FontFamily::parse(context, input))
|
||||||
let mut css_wide_keyword = false;
|
}
|
||||||
match_ignore_ascii_case! { first_ident,
|
}
|
||||||
"serif" => return Ok(FontFamily::Generic(atom!("serif"))),
|
|
||||||
"sans-serif" => return Ok(FontFamily::Generic(atom!("sans-serif"))),
|
|
||||||
"cursive" => return Ok(FontFamily::Generic(atom!("cursive"))),
|
|
||||||
"fantasy" => return Ok(FontFamily::Generic(atom!("fantasy"))),
|
|
||||||
"monospace" => return Ok(FontFamily::Generic(atom!("monospace"))),
|
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-fonts/#propdef-font-family
|
/// `FamilyName::parse` is based on `FontFamily::parse` and not the other way around
|
||||||
// "Font family names that happen to be the same as a keyword value
|
/// because we want the former to exclude generic family keywords.
|
||||||
// (‘inherit’, ‘serif’, ‘sans-serif’, ‘monospace’, ‘fantasy’, and ‘cursive’)
|
impl Parse for FamilyName {
|
||||||
// must be quoted to prevent confusion with the keywords with the same names.
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
// The keywords ‘initial’ and ‘default’ are reserved for future use
|
match FontFamily::parse(context, input) {
|
||||||
// and must also be quoted when used as font names.
|
Ok(FontFamily::FamilyName(name)) => Ok(name),
|
||||||
// UAs must not consider these keywords as matching the <family-name> type."
|
Ok(FontFamily::Generic(_)) |
|
||||||
"inherit" => css_wide_keyword = true,
|
Err(()) => Err(())
|
||||||
"initial" => css_wide_keyword = true,
|
}
|
||||||
"unset" => css_wide_keyword = true,
|
|
||||||
"default" => css_wide_keyword = true,
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut value = first_ident.into_owned();
|
impl Parse for FontFamily {
|
||||||
// These keywords are not allowed by themselves.
|
fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
// The only way this value can be valid with with another keyword.
|
if let Ok(value) = input.try(|input| input.expect_string()) {
|
||||||
if css_wide_keyword {
|
return Ok(FontFamily::FamilyName(FamilyName(Atom::from(&*value))))
|
||||||
let ident = input.expect_ident()?;
|
}
|
||||||
value.push_str(" ");
|
let first_ident = try!(input.expect_ident());
|
||||||
value.push_str(&ident);
|
|
||||||
|
// FIXME(bholley): The fast thing to do here would be to look up the
|
||||||
|
// string (as lowercase) in the static atoms table. We don't have an
|
||||||
|
// API to do that yet though, so we do the simple thing for now.
|
||||||
|
let mut css_wide_keyword = false;
|
||||||
|
match_ignore_ascii_case! { first_ident,
|
||||||
|
"serif" => return Ok(FontFamily::Generic(atom!("serif"))),
|
||||||
|
"sans-serif" => return Ok(FontFamily::Generic(atom!("sans-serif"))),
|
||||||
|
"cursive" => return Ok(FontFamily::Generic(atom!("cursive"))),
|
||||||
|
"fantasy" => return Ok(FontFamily::Generic(atom!("fantasy"))),
|
||||||
|
"monospace" => return Ok(FontFamily::Generic(atom!("monospace"))),
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-fonts/#propdef-font-family
|
||||||
|
// "Font family names that happen to be the same as a keyword value
|
||||||
|
// (‘inherit’, ‘serif’, ‘sans-serif’, ‘monospace’, ‘fantasy’, and ‘cursive’)
|
||||||
|
// must be quoted to prevent confusion with the keywords with the same names.
|
||||||
|
// The keywords ‘initial’ and ‘default’ are reserved for future use
|
||||||
|
// and must also be quoted when used as font names.
|
||||||
|
// UAs must not consider these keywords as matching the <family-name> type."
|
||||||
|
"inherit" => css_wide_keyword = true,
|
||||||
|
"initial" => css_wide_keyword = true,
|
||||||
|
"unset" => css_wide_keyword = true,
|
||||||
|
"default" => css_wide_keyword = true,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut value = first_ident.into_owned();
|
||||||
|
// These keywords are not allowed by themselves.
|
||||||
|
// The only way this value can be valid with with another keyword.
|
||||||
|
if css_wide_keyword {
|
||||||
|
let ident = input.expect_ident()?;
|
||||||
|
value.push_str(" ");
|
||||||
|
value.push_str(&ident);
|
||||||
|
}
|
||||||
|
while let Ok(ident) = input.try(|input| input.expect_ident()) {
|
||||||
|
value.push_str(" ");
|
||||||
|
value.push_str(&ident);
|
||||||
|
}
|
||||||
|
Ok(FontFamily::FamilyName(FamilyName(Atom::from(value))))
|
||||||
}
|
}
|
||||||
while let Ok(ident) = input.try(|input| input.expect_ident()) {
|
|
||||||
value.push_str(" ");
|
|
||||||
value.push_str(&ident);
|
|
||||||
}
|
|
||||||
Ok(FontFamily::FamilyName(Atom::from(value)))
|
|
||||||
}
|
}
|
||||||
</%helpers:longhand>
|
</%helpers:longhand>
|
||||||
|
|
||||||
|
@ -243,6 +273,20 @@ ${helpers.single_keyword("font-variant-caps",
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Used in @font-face, where relative keywords are not allowed.
|
||||||
|
impl Parse for computed_value::T {
|
||||||
|
fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
|
||||||
|
match parse(context, input)? {
|
||||||
|
% for weight in range(100, 901, 100):
|
||||||
|
SpecifiedValue::Weight${weight} => Ok(computed_value::T::Weight${weight}),
|
||||||
|
% endfor
|
||||||
|
SpecifiedValue::Bolder |
|
||||||
|
SpecifiedValue::Lighter => Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub mod computed_value {
|
pub mod computed_value {
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)]
|
#[derive(PartialEq, Eq, Copy, Clone, Hash, Debug)]
|
||||||
|
|
|
@ -12,8 +12,10 @@
|
||||||
${'font-variant-position' if product == 'gecko' else ''}
|
${'font-variant-position' if product == 'gecko' else ''}
|
||||||
${'font-language-override' if product == 'none' else ''}"
|
${'font-language-override' if product == 'none' else ''}"
|
||||||
spec="https://drafts.csswg.org/css-fonts-3/#propdef-font">
|
spec="https://drafts.csswg.org/css-fonts-3/#propdef-font">
|
||||||
|
use parser::Parse;
|
||||||
use properties::longhands::{font_style, font_variant, font_weight, font_stretch};
|
use properties::longhands::{font_style, font_variant, font_weight, font_stretch};
|
||||||
use properties::longhands::{font_size, line_height, font_family};
|
use properties::longhands::{font_size, line_height, font_family};
|
||||||
|
use properties::longhands::font_family::computed_value::FontFamily;
|
||||||
|
|
||||||
pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
|
pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
|
||||||
let mut nb_normals = 0;
|
let mut nb_normals = 0;
|
||||||
|
@ -69,7 +71,7 @@
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let family = try!(input.parse_comma_separated(font_family::parse_one_family));
|
let family = Vec::<FontFamily>::parse(context, input)?;
|
||||||
Ok(Longhands {
|
Ok(Longhands {
|
||||||
font_style: style,
|
font_style: style,
|
||||||
font_variant: variant,
|
font_variant: variant,
|
||||||
|
|
|
@ -59,4 +59,4 @@ pub mod cursor;
|
||||||
pub mod values;
|
pub mod values;
|
||||||
pub mod viewport;
|
pub mod viewport;
|
||||||
|
|
||||||
pub use values::ToCss;
|
pub use values::{ToCss, OneOrMoreCommaSeparated};
|
||||||
|
|
|
@ -24,6 +24,21 @@ pub trait ToCss {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Marker trait to automatically implement ToCss for Vec<T>.
|
||||||
|
pub trait OneOrMoreCommaSeparated {}
|
||||||
|
|
||||||
|
impl<T> ToCss for Vec<T> where T: ToCss + OneOrMoreCommaSeparated {
|
||||||
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
|
let mut iter = self.iter();
|
||||||
|
iter.next().unwrap().to_css(dest)?;
|
||||||
|
for item in iter {
|
||||||
|
dest.write_str(", ")?;
|
||||||
|
item.to_css(dest)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ToCss for Au {
|
impl ToCss for Au {
|
||||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
|
||||||
write!(dest, "{}px", self.to_f64_px())
|
write!(dest, "{}px", self.to_f64_px())
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
use gfx::font_cache_thread::FontCacheThread;
|
use gfx::font_cache_thread::FontCacheThread;
|
||||||
use ipc_channel::ipc;
|
use ipc_channel::ipc;
|
||||||
use style::computed_values::font_family::FontFamily;
|
use style::computed_values::font_family::{FontFamily, FamilyName};
|
||||||
use style::font_face::{FontFaceRule, Source};
|
use style::font_face::{FontFaceRule, Source};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -12,8 +12,8 @@ fn test_local_web_font() {
|
||||||
let (inp_chan, _) = ipc::channel().unwrap();
|
let (inp_chan, _) = ipc::channel().unwrap();
|
||||||
let (out_chan, out_receiver) = ipc::channel().unwrap();
|
let (out_chan, out_receiver) = ipc::channel().unwrap();
|
||||||
let font_cache_thread = FontCacheThread::new(inp_chan, None);
|
let font_cache_thread = FontCacheThread::new(inp_chan, None);
|
||||||
let family_name = FontFamily::FamilyName(From::from("test family"));
|
let family_name = FamilyName(From::from("test family"));
|
||||||
let variant_name = FontFamily::FamilyName(From::from("test font face"));
|
let variant_name = FamilyName(From::from("test font face"));
|
||||||
let font_face_rule = FontFaceRule {
|
let font_face_rule = FontFaceRule {
|
||||||
family: family_name.clone(),
|
family: family_name.clone(),
|
||||||
sources: vec![Source::Local(variant_name)],
|
sources: vec![Source::Local(variant_name)],
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue