mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Make the parser accept @font-face rules without font-family or src.
Fix #16165. Also, it turns out that the CSSFontFaceRule IDL specified in the css-fonts spec is not web-compatible. Instead browsers implement a .style attribute like in CSSStyleRule: https://github.com/w3c/csswg-drafts/issues/825 This in turn requires preserving data about which descriptors were set or not (distinguishing unset from set to a value that happens to be the initial value), so this commit also makes every field `Option<_>`.
This commit is contained in:
parent
15fc9f3bbf
commit
61812d4d9d
7 changed files with 169 additions and 168 deletions
|
@ -16,7 +16,6 @@ use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser};
|
|||
use parser::{ParserContext, log_css_error, Parse};
|
||||
use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
|
||||
use std::fmt;
|
||||
use std::iter;
|
||||
use style_traits::{ToCss, OneOrMoreCommaSeparated};
|
||||
use values::specified::url::SpecifiedUrl;
|
||||
|
||||
|
@ -74,14 +73,12 @@ impl ToCss for UrlSource {
|
|||
/// Parse the block inside a `@font-face` rule.
|
||||
///
|
||||
/// Note that the prelude parsing code lives in the `stylesheets` module.
|
||||
pub fn parse_font_face_block(context: &ParserContext, input: &mut Parser)
|
||||
-> Result<FontFaceData, ()> {
|
||||
let mut rule = FontFaceData::initial();
|
||||
pub fn parse_font_face_block(context: &ParserContext, input: &mut Parser) -> FontFaceRuleData {
|
||||
let mut rule = FontFaceRuleData::empty();
|
||||
{
|
||||
let parser = FontFaceRuleParser {
|
||||
context: context,
|
||||
rule: &mut rule,
|
||||
missing: MissingDescriptors::new(),
|
||||
};
|
||||
let mut iter = DeclarationListParser::new(input, parser);
|
||||
while let Some(declaration) = iter.next() {
|
||||
|
@ -92,24 +89,27 @@ pub fn parse_font_face_block(context: &ParserContext, input: &mut Parser)
|
|||
log_css_error(iter.input, pos, &*message, context);
|
||||
}
|
||||
}
|
||||
if iter.parser.missing.any() {
|
||||
return Err(())
|
||||
}
|
||||
}
|
||||
Ok(rule)
|
||||
rule
|
||||
}
|
||||
|
||||
/// A @font-face rule that is known to have font-family and src declarations.
|
||||
#[cfg(feature = "servo")]
|
||||
pub struct FontFace<'a>(&'a FontFaceRuleData);
|
||||
|
||||
/// A list of effective sources that we send over through IPC to the font cache.
|
||||
#[cfg(feature = "servo")]
|
||||
#[derive(Clone, Debug)]
|
||||
#[cfg_attr(feature = "servo", derive(Deserialize, Serialize))]
|
||||
pub struct EffectiveSources(Vec<Source>);
|
||||
|
||||
impl FontFaceData {
|
||||
#[cfg(feature = "servo")]
|
||||
impl<'a> FontFace<'a> {
|
||||
/// Returns the list of effective sources for that font-face, that is the
|
||||
/// sources which don't list any format hint, or the ones which list at
|
||||
/// least "truetype" or "opentype".
|
||||
pub fn effective_sources(&self) -> EffectiveSources {
|
||||
EffectiveSources(self.sources.iter().rev().filter(|source| {
|
||||
EffectiveSources(self.sources().iter().rev().filter(|source| {
|
||||
if let Source::Url(ref url_source) = **source {
|
||||
let hints = &url_source.format_hints;
|
||||
// We support only opentype fonts and truetype is an alias for
|
||||
|
@ -125,7 +125,8 @@ impl FontFaceData {
|
|||
}
|
||||
}
|
||||
|
||||
impl iter::Iterator for EffectiveSources {
|
||||
#[cfg(feature = "servo")]
|
||||
impl Iterator for EffectiveSources {
|
||||
type Item = Source;
|
||||
fn next(&mut self) -> Option<Source> {
|
||||
self.0.pop()
|
||||
|
@ -134,8 +135,7 @@ impl iter::Iterator for EffectiveSources {
|
|||
|
||||
struct FontFaceRuleParser<'a, 'b: 'a> {
|
||||
context: &'a ParserContext<'b>,
|
||||
rule: &'a mut FontFaceData,
|
||||
missing: MissingDescriptors,
|
||||
rule: &'a mut FontFaceRuleData,
|
||||
}
|
||||
|
||||
/// Default methods reject all at rules.
|
||||
|
@ -172,82 +172,40 @@ impl Parse for Source {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! font_face_descriptors {
|
||||
macro_rules! font_face_descriptors_common {
|
||||
(
|
||||
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, )*
|
||||
]
|
||||
$( #[$doc: meta] $name: tt $ident: ident: $ty: ty, )*
|
||||
) => {
|
||||
/// Data inside a `@font-face` rule.
|
||||
///
|
||||
/// https://drafts.csswg.org/css-fonts/#font-face-rule
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct FontFaceData {
|
||||
pub struct FontFaceRuleData {
|
||||
$(
|
||||
#[$m_doc]
|
||||
pub $m_ident: $m_ty,
|
||||
)*
|
||||
$(
|
||||
#[$o_doc]
|
||||
pub $o_ident: $o_ty,
|
||||
#[$doc]
|
||||
pub $ident: Option<$ty>,
|
||||
)*
|
||||
}
|
||||
|
||||
struct MissingDescriptors {
|
||||
$(
|
||||
$m_ident: bool,
|
||||
)*
|
||||
}
|
||||
|
||||
impl MissingDescriptors {
|
||||
fn new() -> Self {
|
||||
MissingDescriptors {
|
||||
impl FontFaceRuleData {
|
||||
fn empty() -> Self {
|
||||
FontFaceRuleData {
|
||||
$(
|
||||
$m_ident: true,
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
fn any(&self) -> bool {
|
||||
$(
|
||||
self.$m_ident
|
||||
)||*
|
||||
}
|
||||
}
|
||||
|
||||
impl FontFaceData {
|
||||
fn initial() -> Self {
|
||||
FontFaceData {
|
||||
$(
|
||||
$m_ident: $m_initial,
|
||||
)*
|
||||
$(
|
||||
$o_ident: $o_initial,
|
||||
$ident: None,
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCssWithGuard for FontFaceData {
|
||||
impl ToCssWithGuard for FontFaceRuleData {
|
||||
// Serialization of FontFaceRule is not specced.
|
||||
fn to_css<W>(&self, _guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
|
||||
where W: fmt::Write {
|
||||
dest.write_str("@font-face {\n")?;
|
||||
$(
|
||||
dest.write_str(concat!(" ", $m_name, ": "))?;
|
||||
ToCss::to_css(&self.$m_ident, 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, ": "))?;
|
||||
ToCss::to_css(&self.$o_ident, dest)?;
|
||||
if let Some(ref value) = self.$ident {
|
||||
dest.write_str(concat!(" ", $name, ": "))?;
|
||||
ToCss::to_css(value, dest)?;
|
||||
dest.write_str(";\n")?;
|
||||
}
|
||||
)*
|
||||
|
@ -261,13 +219,7 @@ macro_rules! font_face_descriptors {
|
|||
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)?,
|
||||
$name => self.rule.$ident = Some(Parse::parse(self.context, input)?),
|
||||
)*
|
||||
_ => return Err(())
|
||||
}
|
||||
|
@ -277,18 +229,67 @@ macro_rules! font_face_descriptors {
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! font_face_descriptors {
|
||||
(
|
||||
mandatory descriptors = [
|
||||
$( #[$m_doc: meta] $m_name: tt $m_ident: ident: $m_ty: ty, )*
|
||||
]
|
||||
optional descriptors = [
|
||||
$( #[$o_doc: meta] $o_name: tt $o_ident: ident: $o_ty: ty = $o_initial: expr, )*
|
||||
]
|
||||
) => {
|
||||
font_face_descriptors_common! {
|
||||
$( #[$m_doc] $m_name $m_ident: $m_ty, )*
|
||||
$( #[$o_doc] $o_name $o_ident: $o_ty, )*
|
||||
}
|
||||
|
||||
impl FontFaceRuleData {
|
||||
/// Per https://github.com/w3c/csswg-drafts/issues/1133 an @font-face rule
|
||||
/// is valid as far as the CSS parser is concerned even if it doesn’t have
|
||||
/// a font-family or src declaration.
|
||||
///
|
||||
/// However both are required for the rule to represent an actual font face.
|
||||
#[cfg(feature = "servo")]
|
||||
pub fn font_face(&self) -> Option<FontFace> {
|
||||
if $( self.$m_ident.is_some() )&&* {
|
||||
Some(FontFace(self))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "servo")]
|
||||
impl<'a> FontFace<'a> {
|
||||
$(
|
||||
#[$m_doc]
|
||||
pub fn $m_ident(&self) -> &$m_ty {
|
||||
self.0 .$m_ident.as_ref().unwrap()
|
||||
}
|
||||
)*
|
||||
$(
|
||||
#[$o_doc]
|
||||
pub fn $o_ident(&self) -> $o_ty {
|
||||
if let Some(ref value) = self.0 .$o_ident {
|
||||
value.clone()
|
||||
} else {
|
||||
$o_initial
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// css-name rust_identifier: Type = initial_value,
|
||||
#[cfg(feature = "gecko")]
|
||||
font_face_descriptors! {
|
||||
mandatory descriptors = [
|
||||
/// The name of this font face
|
||||
"font-family" family: FamilyName = FamilyName {
|
||||
name: atom!(""),
|
||||
quoted: true,
|
||||
},
|
||||
"font-family" family: FamilyName,
|
||||
|
||||
/// The alternative sources for this font face.
|
||||
"src" sources: Vec<Source> = Vec::new(),
|
||||
"src" sources: Vec<Source>,
|
||||
]
|
||||
optional descriptors = [
|
||||
/// The style of this font face
|
||||
|
@ -304,6 +305,8 @@ font_face_descriptors! {
|
|||
"unicode-range" unicode_range: Vec<UnicodeRange> = vec![
|
||||
UnicodeRange { start: 0, end: 0x10FFFF }
|
||||
],
|
||||
|
||||
// FIXME: add font-feature-settings, font-language-override, and font-display
|
||||
]
|
||||
}
|
||||
|
||||
|
@ -311,13 +314,10 @@ font_face_descriptors! {
|
|||
font_face_descriptors! {
|
||||
mandatory descriptors = [
|
||||
/// The name of this font face
|
||||
"font-family" family: FamilyName = FamilyName {
|
||||
name: atom!(""),
|
||||
quoted: true,
|
||||
},
|
||||
"font-family" family: FamilyName,
|
||||
|
||||
/// The alternative sources for this font face.
|
||||
"src" sources: Vec<Source> = Vec::new(),
|
||||
"src" sources: Vec<Source>,
|
||||
]
|
||||
optional descriptors = [
|
||||
]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue