From 0eae803e8ffc8cbfa1d3ebc4a0f6f2d9d6c1ef10 Mon Sep 17 00:00:00 2001 From: John Daggett Date: Mon, 20 May 2013 11:59:20 +0900 Subject: [PATCH] Bug 549861 - reland font-variant subproperties with DOM-peer review. r=khuey --- dom/base/nsDOMClassInfo.cpp | 6 + dom/base/nsDOMClassInfoClasses.h | 2 + dom/interfaces/base/domstubs.idl | 1 + dom/interfaces/css/moz.build | 1 + dom/interfaces/css/nsIDOMCSSRule.idl | 1 + .../en-US/chrome/layout/css.properties | 11 + .../mochitest/general/test_interfaces.html | 1 + gfx/src/nsFont.cpp | 272 +++++++- gfx/src/nsFont.h | 39 +- gfx/tests/gfxFontSelectionTests.h | 2 - gfx/tests/gfxTextRunPerfTest.cpp | 1 - gfx/tests/gfxWordCacheTest.cpp | 1 - gfx/thebes/Makefile.in | 1 + gfx/thebes/gfxFont.cpp | 121 +++- gfx/thebes/gfxFont.h | 25 +- gfx/thebes/gfxFontConstants.h | 161 +++++ gfx/thebes/gfxFontFeatures.h | 96 +++ gfx/thebes/gfxGraphiteShaper.cpp | 9 +- gfx/thebes/gfxHarfBuzzShaper.cpp | 28 +- layout/reftests/font-features/reftest.list | 32 + layout/style/Declaration.cpp | 42 +- layout/style/Rule.h | 3 +- layout/style/nsCSSFontDescList.h | 3 + layout/style/nsCSSKeywordList.h | 38 ++ layout/style/nsCSSParser.cpp | 581 +++++++++++++++++- layout/style/nsCSSPropAliasList.h | 2 + layout/style/nsCSSPropList.h | 101 +++ layout/style/nsCSSProps.cpp | 106 +++- layout/style/nsCSSProps.h | 11 +- layout/style/nsCSSRuleProcessor.cpp | 40 +- layout/style/nsCSSRuleProcessor.h | 3 + layout/style/nsCSSRules.cpp | 252 +++++++- layout/style/nsCSSRules.h | 61 +- layout/style/nsCSSValue.cpp | 75 ++- layout/style/nsComputedDOMStyle.cpp | 169 +++++ layout/style/nsComputedDOMStyle.h | 10 +- layout/style/nsRuleNode.cpp | 104 ++++ layout/style/nsStyleSet.cpp | 58 +- layout/style/nsStyleSet.h | 17 +- layout/style/nsStyleStruct.cpp | 10 + layout/style/nsStyleUtil.cpp | 112 ++++ layout/style/nsStyleUtil.h | 16 + layout/style/test/Makefile.in | 1 + layout/style/test/property_database.js | 125 +++- layout/style/test/test_bug377947.html | 15 + .../test/test_system_font_serialization.html | 8 +- modules/libpref/src/init/all.js | 13 + 47 files changed, 2696 insertions(+), 91 deletions(-) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index eb98b6652c2a..1c04c6843fff 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -897,6 +897,8 @@ static nsDOMClassInfoData sClassInfoData[] = { NS_DEFINE_CLASSINFO_DATA(LockedFile, nsEventTargetSH, EVENTTARGET_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(CSSFontFeatureValuesRule, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) #ifdef MOZ_TIME_MANAGER NS_DEFINE_CLASSINFO_DATA(MozTimeManager, nsDOMGenericSH, @@ -2266,6 +2268,10 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_ENTRY(nsIDOMLockedFile) DOM_CLASSINFO_MAP_END + DOM_CLASSINFO_MAP_BEGIN(CSSFontFeatureValuesRule, nsIDOMCSSFontFeatureValuesRule) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSFontFeatureValuesRule) + DOM_CLASSINFO_MAP_END + #ifdef MOZ_TIME_MANAGER DOM_CLASSINFO_MAP_BEGIN(MozTimeManager, nsIDOMMozTimeManager) DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozTimeManager) diff --git a/dom/base/nsDOMClassInfoClasses.h b/dom/base/nsDOMClassInfoClasses.h index c57c2c1d19d2..fe8d7d17c09f 100644 --- a/dom/base/nsDOMClassInfoClasses.h +++ b/dom/base/nsDOMClassInfoClasses.h @@ -232,6 +232,8 @@ DOMCI_CLASS(AsyncScrollEventDetail) DOMCI_CLASS(LockedFile) +DOMCI_CLASS(CSSFontFeatureValuesRule) + #ifdef MOZ_TIME_MANAGER DOMCI_CLASS(MozTimeManager) #endif diff --git a/dom/interfaces/base/domstubs.idl b/dom/interfaces/base/domstubs.idl index 9877a0bcd705..dc9fe948f4d2 100644 --- a/dom/interfaces/base/domstubs.idl +++ b/dom/interfaces/base/domstubs.idl @@ -69,6 +69,7 @@ interface nsIDOMCSSPrimitiveValue; interface nsIDOMCSSRule; interface nsIDOMCSSRuleList; interface nsIDOMMozCSSKeyframeRule; +interface nsIDOMCSSFontFeatureValuesRule; interface nsIDOMCSSStyleSheet; interface nsIDOMCSSStyleDeclaration; interface nsIDOMCounter; diff --git a/dom/interfaces/css/moz.build b/dom/interfaces/css/moz.build index dea142e01363..8a295ecdbbd3 100644 --- a/dom/interfaces/css/moz.build +++ b/dom/interfaces/css/moz.build @@ -8,6 +8,7 @@ XPIDL_SOURCES += [ 'nsIDOMCSSCharsetRule.idl', 'nsIDOMCSSConditionRule.idl', 'nsIDOMCSSFontFaceRule.idl', + 'nsIDOMCSSFontFeatureValuesRule.idl', 'nsIDOMCSSGroupingRule.idl', 'nsIDOMCSSImportRule.idl', 'nsIDOMCSSMediaRule.idl', diff --git a/dom/interfaces/css/nsIDOMCSSRule.idl b/dom/interfaces/css/nsIDOMCSSRule.idl index d334e7463d9f..94c81fc73c4e 100644 --- a/dom/interfaces/css/nsIDOMCSSRule.idl +++ b/dom/interfaces/css/nsIDOMCSSRule.idl @@ -32,6 +32,7 @@ interface nsIDOMCSSRule : nsISupports const unsigned short MOZ_KEYFRAME_RULE = 8; const unsigned short NAMESPACE_RULE = 10; const unsigned short SUPPORTS_RULE = 12; + const unsigned short FONT_FEATURE_VALUES_RULE = 14; readonly attribute unsigned short type; attribute DOMString cssText; diff --git a/dom/locales/en-US/chrome/layout/css.properties b/dom/locales/en-US/chrome/layout/css.properties index a82d1aeb9a04..463acdae6818 100644 --- a/dom/locales/en-US/chrome/layout/css.properties +++ b/dom/locales/en-US/chrome/layout/css.properties @@ -115,6 +115,17 @@ PEMQExpectedFeatureValue=Found invalid value for media feature. PEBadFontBlockStart=Expected '{' to begin @font-face rule but found '%1$S'. PEBadFontBlockEnd=Expected '}' to end @font-face rule but found '%1$S'. PEAnonBoxNotAlone=Did not expect anonymous box. +PEFFVUnexpectedEOF=Unexpected end of @font-feature-values rule. +PEFFVBlockStart=Expected opening { of @font-feature-values rule but found '%1$S'. +PEFFVValueSetStart=Expected opening { of feature value set but found '%1$S'. +PEFFVNoFamily=Expected font family list for @font-feature-values rule but found '%1$S'. +PEFFVUnexpectedBlockEnd=Expected '}' to end @font-feature-values rule but found '%1$S'. +PEFFVUnknownFontVariantPropValue=Unknown font-variant property value '%1$S'. +PEFFVExpectedIdent=Expected identifier but found '%1$S'. +PEFFVExpectedValue=Expected non-negative integer value but found '%1$S'. +PEFFVTooManyValues=Too many values for feature type '%1$S'. +PEFFVGenericInFamilyList=Family list cannot contain generic font family name. +PEFFVValueDefinitionTrailing=Expected end of value definition but found '%1$S'. PEBadDirValue=Expected 'ltr' or 'rtl' in direction selector but found '%1$S'. PESupportsConditionStartEOF2='not', '(', or function PESupportsConditionInParensEOF=')' diff --git a/dom/tests/mochitest/general/test_interfaces.html b/dom/tests/mochitest/general/test_interfaces.html index 45990b24b635..215647dda56d 100644 --- a/dom/tests/mochitest/general/test_interfaces.html +++ b/dom/tests/mochitest/general/test_interfaces.html @@ -492,6 +492,7 @@ var interfaceNamesInGlobalScope = "CloseEvent", "IDBCursorWithValue", "CSSFontFaceRule", + "CSSFontFeatureValuesRule", "XMLHttpRequestEventTarget", "CompositionEvent", "HTMLOutputElement", diff --git a/gfx/src/nsFont.cpp b/gfx/src/nsFont.cpp index e2915a23c14f..4e0183b23481 100644 --- a/gfx/src/nsFont.cpp +++ b/gfx/src/nsFont.cpp @@ -11,8 +11,7 @@ nsFont::nsFont(const char* aName, uint8_t aStyle, uint8_t aVariant, uint16_t aWeight, int16_t aStretch, uint8_t aDecoration, - nscoord aSize, float aSizeAdjust, - const nsString* aLanguageOverride) + nscoord aSize) { NS_ASSERTION(aName && IsASCII(nsDependentCString(aName)), "Must only pass ASCII names here"); @@ -24,16 +23,21 @@ nsFont::nsFont(const char* aName, uint8_t aStyle, uint8_t aVariant, stretch = aStretch; decorations = aDecoration; size = aSize; - sizeAdjust = aSizeAdjust; - if (aLanguageOverride) { - languageOverride = *aLanguageOverride; - } + sizeAdjust = 0.0; + kerning = NS_FONT_KERNING_AUTO; + synthesis = NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE; + + variantAlternates = 0; + variantCaps = NS_FONT_VARIANT_CAPS_NORMAL; + variantEastAsian = 0; + variantLigatures = 0; + variantNumeric = 0; + variantPosition = NS_FONT_VARIANT_POSITION_NORMAL; } -nsFont::nsFont(const nsString& aName, uint8_t aStyle, uint8_t aVariant, +nsFont::nsFont(const nsSubstring& aName, uint8_t aStyle, uint8_t aVariant, uint16_t aWeight, int16_t aStretch, uint8_t aDecoration, - nscoord aSize, float aSizeAdjust, - const nsString* aLanguageOverride) + nscoord aSize) : name(aName) { style = aStyle; @@ -43,10 +47,16 @@ nsFont::nsFont(const nsString& aName, uint8_t aStyle, uint8_t aVariant, stretch = aStretch; decorations = aDecoration; size = aSize; - sizeAdjust = aSizeAdjust; - if (aLanguageOverride) { - languageOverride = *aLanguageOverride; - } + sizeAdjust = 0.0; + kerning = NS_FONT_KERNING_AUTO; + synthesis = NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE; + + variantAlternates = 0; + variantCaps = NS_FONT_VARIANT_CAPS_NORMAL; + variantEastAsian = 0; + variantLigatures = 0; + variantNumeric = 0; + variantPosition = NS_FONT_VARIANT_POSITION_NORMAL; } nsFont::nsFont(const nsFont& aOther) @@ -60,8 +70,18 @@ nsFont::nsFont(const nsFont& aOther) decorations = aOther.decorations; size = aOther.size; sizeAdjust = aOther.sizeAdjust; - languageOverride = aOther.languageOverride; + kerning = aOther.kerning; + synthesis = aOther.synthesis; fontFeatureSettings = aOther.fontFeatureSettings; + languageOverride = aOther.languageOverride; + variantAlternates = aOther.variantAlternates; + variantCaps = aOther.variantCaps; + variantEastAsian = aOther.variantEastAsian; + variantLigatures = aOther.variantLigatures; + variantNumeric = aOther.variantNumeric; + variantPosition = aOther.variantPosition; + alternateValues = aOther.alternateValues; + featureValueLookup = aOther.featureValueLookup; } nsFont::nsFont() @@ -81,8 +101,18 @@ bool nsFont::BaseEquals(const nsFont& aOther) const (size == aOther.size) && (sizeAdjust == aOther.sizeAdjust) && name.Equals(aOther.name, nsCaseInsensitiveStringComparator()) && + (kerning == aOther.kerning) && + (synthesis == aOther.synthesis) && + (fontFeatureSettings == aOther.fontFeatureSettings) && (languageOverride == aOther.languageOverride) && - (fontFeatureSettings == aOther.fontFeatureSettings)) { + (variantAlternates == aOther.variantAlternates) && + (variantCaps == aOther.variantCaps) && + (variantEastAsian == aOther.variantEastAsian) && + (variantLigatures == aOther.variantLigatures) && + (variantNumeric == aOther.variantNumeric) && + (variantPosition == aOther.variantPosition) && + (alternateValues == aOther.alternateValues) && + (featureValueLookup == aOther.featureValueLookup)) { return true; } return false; @@ -109,16 +139,27 @@ nsFont& nsFont::operator=(const nsFont& aOther) decorations = aOther.decorations; size = aOther.size; sizeAdjust = aOther.sizeAdjust; - languageOverride = aOther.languageOverride; + kerning = aOther.kerning; + synthesis = aOther.synthesis; fontFeatureSettings = aOther.fontFeatureSettings; + languageOverride = aOther.languageOverride; + variantAlternates = aOther.variantAlternates; + variantCaps = aOther.variantCaps; + variantEastAsian = aOther.variantEastAsian; + variantLigatures = aOther.variantLigatures; + variantNumeric = aOther.variantNumeric; + variantPosition = aOther.variantPosition; + alternateValues = aOther.alternateValues; + featureValueLookup = aOther.featureValueLookup; return *this; } void -nsFont::AddFontFeaturesToStyle(gfxFontStyle *aStyle) const +nsFont::CopyAlternates(const nsFont& aOther) { - // simple copy for now, font-variant implementation will expand - aStyle->featureSettings.AppendElements(fontFeatureSettings); + variantAlternates = aOther.variantAlternates; + alternateValues = aOther.alternateValues; + featureValueLookup = aOther.featureValueLookup; } static bool IsGenericFontFamily(const nsString& aFamily) @@ -184,6 +225,199 @@ bool nsFont::EnumerateFamilies(nsFontFamilyEnumFunc aFunc, void* aData) const return true; } +// mapping from bitflag to font feature tag/value pair +// +// these need to be kept in sync with the constants listed +// in gfxFontConstants.h (e.g. NS_FONT_VARIANT_EAST_ASIAN_JIS78) + +// NS_FONT_VARIANT_EAST_ASIAN_xxx values +const gfxFontFeature eastAsianDefaults[] = { + { TRUETYPE_TAG('j','p','7','8'), 1 }, + { TRUETYPE_TAG('j','p','8','3'), 1 }, + { TRUETYPE_TAG('j','p','9','0'), 1 }, + { TRUETYPE_TAG('j','p','0','4'), 1 }, + { TRUETYPE_TAG('s','m','p','l'), 1 }, + { TRUETYPE_TAG('t','r','a','d'), 1 }, + { TRUETYPE_TAG('f','w','i','d'), 1 }, + { TRUETYPE_TAG('p','w','i','d'), 1 }, + { TRUETYPE_TAG('r','u','b','y'), 1 } +}; + +PR_STATIC_ASSERT(NS_ARRAY_LENGTH(eastAsianDefaults) == + eFeatureEastAsian_numFeatures); + +// NS_FONT_VARIANT_LIGATURES_xxx values +const gfxFontFeature ligDefaults[] = { + { TRUETYPE_TAG('l','i','g','a'), 1 }, + { TRUETYPE_TAG('l','i','g','a'), 0 }, + { TRUETYPE_TAG('d','l','i','g'), 1 }, + { TRUETYPE_TAG('d','l','i','g'), 0 }, + { TRUETYPE_TAG('h','l','i','g'), 1 }, + { TRUETYPE_TAG('h','l','i','g'), 0 }, + { TRUETYPE_TAG('c','a','l','t'), 1 }, + { TRUETYPE_TAG('c','a','l','t'), 0 } +}; + +PR_STATIC_ASSERT(NS_ARRAY_LENGTH(ligDefaults) == + eFeatureLigatures_numFeatures); + +// NS_FONT_VARIANT_NUMERIC_xxx values +const gfxFontFeature numericDefaults[] = { + { TRUETYPE_TAG('l','n','u','m'), 1 }, + { TRUETYPE_TAG('o','n','u','m'), 1 }, + { TRUETYPE_TAG('p','n','u','m'), 1 }, + { TRUETYPE_TAG('t','n','u','m'), 1 }, + { TRUETYPE_TAG('f','r','a','c'), 1 }, + { TRUETYPE_TAG('a','f','r','c'), 1 }, + { TRUETYPE_TAG('z','e','r','o'), 1 }, + { TRUETYPE_TAG('o','r','d','n'), 1 } +}; + +PR_STATIC_ASSERT(NS_ARRAY_LENGTH(numericDefaults) == + eFeatureNumeric_numFeatures); + +static void +AddFontFeaturesBitmask(uint32_t aValue, uint32_t aMin, uint32_t aMax, + const gfxFontFeature aFeatureDefaults[], + nsTArray& aFeaturesOut) + +{ + uint32_t i, m; + + for (i = 0, m = aMin; m <= aMax; i++, m <<= 1) { + if (m & aValue) { + const gfxFontFeature& feature = aFeatureDefaults[i]; + aFeaturesOut.AppendElement(feature); + } + } +} + +void nsFont::AddFontFeaturesToStyle(gfxFontStyle *aStyle) const +{ + // add in font-variant features + gfxFontFeature setting; + + // -- kerning + setting.mTag = TRUETYPE_TAG('k','e','r','n'); + switch (kerning) { + case NS_FONT_KERNING_NONE: + setting.mValue = 0; + aStyle->featureSettings.AppendElement(setting); + break; + case NS_FONT_KERNING_NORMAL: + setting.mValue = 1; + aStyle->featureSettings.AppendElement(setting); + break; + default: + // auto case implies use user agent default + break; + } + + // -- alternates + if (variantAlternates & NS_FONT_VARIANT_ALTERNATES_HISTORICAL) { + setting.mValue = 1; + setting.mTag = TRUETYPE_TAG('h','i','s','t'); + aStyle->featureSettings.AppendElement(setting); + } + + + // -- copy font-specific alternate info into style + // (this will be resolved after font-matching occurs) + aStyle->alternateValues.AppendElements(alternateValues); + aStyle->featureValueLookup = featureValueLookup; + + // -- caps + setting.mValue = 1; + switch (variantCaps) { + case NS_FONT_VARIANT_CAPS_ALLSMALL: + setting.mTag = TRUETYPE_TAG('c','2','s','c'); + aStyle->featureSettings.AppendElement(setting); + // fall through to the small-caps case + case NS_FONT_VARIANT_CAPS_SMALLCAPS: + setting.mTag = TRUETYPE_TAG('s','m','c','p'); + aStyle->featureSettings.AppendElement(setting); + break; + + case NS_FONT_VARIANT_CAPS_ALLPETITE: + setting.mTag = TRUETYPE_TAG('c','2','p','c'); + aStyle->featureSettings.AppendElement(setting); + // fall through to the petite-caps case + case NS_FONT_VARIANT_CAPS_PETITECAPS: + setting.mTag = TRUETYPE_TAG('p','c','a','p'); + aStyle->featureSettings.AppendElement(setting); + break; + + case NS_FONT_VARIANT_CAPS_TITLING: + setting.mTag = TRUETYPE_TAG('t','i','t','l'); + aStyle->featureSettings.AppendElement(setting); + break; + + case NS_FONT_VARIANT_CAPS_UNICASE: + setting.mTag = TRUETYPE_TAG('u','n','i','c'); + aStyle->featureSettings.AppendElement(setting); + break; + + default: + break; + } + + // -- east-asian + if (variantEastAsian) { + AddFontFeaturesBitmask(variantEastAsian, + NS_FONT_VARIANT_EAST_ASIAN_JIS78, + NS_FONT_VARIANT_EAST_ASIAN_RUBY, + eastAsianDefaults, aStyle->featureSettings); + } + + // -- ligatures + if (variantLigatures) { + AddFontFeaturesBitmask(variantLigatures, + NS_FONT_VARIANT_LIGATURES_COMMON, + NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL, + ligDefaults, aStyle->featureSettings); + + // special case common ligs, which also enable/disable clig + if (variantLigatures & NS_FONT_VARIANT_LIGATURES_COMMON) { + setting.mTag = TRUETYPE_TAG('c','l','i','g'); + setting.mValue = 1; + aStyle->featureSettings.AppendElement(setting); + } else if (variantLigatures & NS_FONT_VARIANT_LIGATURES_NO_COMMON) { + setting.mTag = TRUETYPE_TAG('c','l','i','g'); + setting.mValue = 0; + aStyle->featureSettings.AppendElement(setting); + } + } + + // -- numeric + if (variantNumeric) { + AddFontFeaturesBitmask(variantNumeric, + NS_FONT_VARIANT_NUMERIC_LINING, + NS_FONT_VARIANT_NUMERIC_ORDINAL, + numericDefaults, aStyle->featureSettings); + } + + // -- position + setting.mTag = 0; + setting.mValue = 1; + switch (variantPosition) { + case NS_FONT_VARIANT_POSITION_SUPER: + setting.mTag = TRUETYPE_TAG('s','u','p','s'); + aStyle->featureSettings.AppendElement(setting); + break; + + case NS_FONT_VARIANT_POSITION_SUB: + setting.mTag = TRUETYPE_TAG('s','u','b','s'); + aStyle->featureSettings.AppendElement(setting); + break; + + default: + break; + } + + // add in features from font-feature-settings + aStyle->featureSettings.AppendElements(fontFeatureSettings); +} + static bool FontEnumCallback(const nsString& aFamily, bool aGeneric, void *aData) { *((nsString*)aData) = aFamily; diff --git a/gfx/src/nsFont.h b/gfx/src/nsFont.h index 22627c1e3243..8491f962f764 100644 --- a/gfx/src/nsFont.h +++ b/gfx/src/nsFont.h @@ -12,6 +12,7 @@ #include "nsTArray.h" #include "gfxFontConstants.h" #include "gfxFontFeatures.h" +#include "nsAutoPtr.h" // XXX we need a method to enumerate all of the possible fonts on the // system across family, weight, style, size, etc. But not here! @@ -50,6 +51,22 @@ struct NS_GFX nsFont { // The variant of the font (normal, small-caps) uint8_t variant; + // Variant subproperties + // (currently -moz- versions, will replace variant above eventually) + uint8_t variantCaps; + uint8_t variantLigatures; + uint8_t variantNumeric; + uint8_t variantPosition; + + uint16_t variantEastAsian; + + // Some font-variant-alternates property values require + // font-specific settings defined via @font-feature-values rules. + // These are resolved *after* font matching occurs. + + // -- bitmask for both enumerated and functional propvals + uint16_t variantAlternates; + // The decorations on the font (underline, overline, // line-through). The decorations can be binary or'd together. uint8_t decorations; @@ -70,6 +87,12 @@ struct NS_GFX nsFont { // needs to be done. float sizeAdjust; + // -- list of value tags for font-specific alternate features + nsTArray alternateValues; + + // -- object used to look these up once the font is matched + nsRefPtr featureValueLookup; + // Font features from CSS font-feature-settings nsTArray fontFeatureSettings; @@ -78,17 +101,21 @@ struct NS_GFX nsFont { // (see http://www.microsoft.com/typography/otspec/languagetags.htm). nsString languageOverride; + // Kerning + uint8_t kerning; + + // Synthesis setting, controls use of fake bolding/italics + uint8_t synthesis; + // Initialize the font struct with an ASCII name nsFont(const char* aName, uint8_t aStyle, uint8_t aVariant, uint16_t aWeight, int16_t aStretch, uint8_t aDecoration, - nscoord aSize, float aSizeAdjust=0.0f, - const nsString* aLanguageOverride = nullptr); + nscoord aSize); // Initialize the font struct with a (potentially) unicode name - nsFont(const nsString& aName, uint8_t aStyle, uint8_t aVariant, + nsFont(const nsSubstring& aName, uint8_t aStyle, uint8_t aVariant, uint16_t aWeight, int16_t aStretch, uint8_t aDecoration, - nscoord aSize, float aSizeAdjust=0.0f, - const nsString* aLanguageOverride = nullptr); + nscoord aSize); // Make a copy of the given font nsFont(const nsFont& aFont); @@ -106,6 +133,8 @@ struct NS_GFX nsFont { nsFont& operator=(const nsFont& aOther); + void CopyAlternates(const nsFont& aOther); + // Add featureSettings into style void AddFontFeaturesToStyle(gfxFontStyle *aStyle) const; diff --git a/gfx/tests/gfxFontSelectionTests.h b/gfx/tests/gfxFontSelectionTests.h index efd69b01221a..8310fa95de76 100644 --- a/gfx/tests/gfxFontSelectionTests.h +++ b/gfx/tests/gfxFontSelectionTests.h @@ -80,7 +80,6 @@ SetupTests() NS_NewPermanentAtom(NS_LITERAL_STRING("en")), 0.0, false, false, false, - NS_LITERAL_STRING(""), NS_LITERAL_STRING("")); gfxFontStyle style_western_bold_16 (FONT_STYLE_NORMAL, @@ -90,7 +89,6 @@ SetupTests() NS_NewPermanentAtom(NS_LITERAL_STRING("en")), 0.0, false, false, false, - NS_LITERAL_STRING(""), NS_LITERAL_STRING("")); /* Test 0 */ diff --git a/gfx/tests/gfxTextRunPerfTest.cpp b/gfx/tests/gfxTextRunPerfTest.cpp index d41e6ccd83f6..b5cf5394e32b 100644 --- a/gfx/tests/gfxTextRunPerfTest.cpp +++ b/gfx/tests/gfxTextRunPerfTest.cpp @@ -66,7 +66,6 @@ RunTest (TestEntry *test, gfxContext *ctx) { NS_NewPermanentAtom(NS_LITERAL_STRING("en")), 0.0, false, false, false, - NS_LITERAL_STRING(""), NS_LITERAL_STRING("")); fontGroup = gfxPlatform::GetPlatform()->CreateFontGroup(NS_ConvertUTF8toUTF16(test->mFamilies), &style_western_normal_16, nullptr); diff --git a/gfx/tests/gfxWordCacheTest.cpp b/gfx/tests/gfxWordCacheTest.cpp index afe19b489212..1bb098504044 100644 --- a/gfx/tests/gfxWordCacheTest.cpp +++ b/gfx/tests/gfxWordCacheTest.cpp @@ -124,7 +124,6 @@ main (int argc, char **argv) { NS_NewPermanentAtom(NS_LITERAL_STRING("en")), 0.0, false, false, false, - NS_LITERAL_STRING(""), NS_LITERAL_STRING("")); nsRefPtr fontGroup = diff --git a/gfx/thebes/Makefile.in b/gfx/thebes/Makefile.in index 3fcae87f8aa2..12128905053c 100644 --- a/gfx/thebes/Makefile.in +++ b/gfx/thebes/Makefile.in @@ -34,6 +34,7 @@ CPPSRCS = \ gfxDrawable.cpp \ gfxImageSurface.cpp \ gfxFont.cpp \ + gfxFontFeatures.cpp \ gfxFontMissingGlyphs.cpp \ gfxFontTest.cpp \ gfxFontUtils.cpp \ diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index f11a9d258095..520f6984ffd9 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -1367,17 +1367,111 @@ gfxFontCache::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf, SizeOfExcludingThis(aMallocSizeOf, aSizes); } +#define MAX_SSXX_VALUE 99 +#define MAX_CVXX_VALUE 99 + +static void +LookupAlternateValues(gfxFontFeatureValueSet *featureLookup, + const nsAString& aFamily, + const nsTArray& altValue, + nsTArray& aFontFeatures) +{ + uint32_t numAlternates = altValue.Length(); + for (uint32_t i = 0; i < numAlternates; i++) { + const gfxAlternateValue& av = altValue.ElementAt(i); + nsAutoTArray values; + + // map ==> + bool found = + featureLookup->GetFontFeatureValuesFor(aFamily, av.alternate, + av.value, values); + uint32_t numValues = values.Length(); + + // nothing defined, skip + if (!found || numValues == 0) { + continue; + } + + gfxFontFeature feature; + if (av.alternate == NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT) { + NS_ASSERTION(numValues <= 2, + "too many values allowed for character-variant"); + // character-variant(12 3) ==> 'cv12' = 3 + uint32_t nn = values.ElementAt(0); + // ignore values greater than 99 + if (nn == 0 || nn > MAX_CVXX_VALUE) { + continue; + } + feature.mValue = 1; + if (numValues > 1) { + feature.mValue = values.ElementAt(1); + } + feature.mTag = HB_TAG('c','v',('0' + nn / 10), ('0' + nn % 10)); + aFontFeatures.AppendElement(feature); + + } else if (av.alternate == NS_FONT_VARIANT_ALTERNATES_STYLESET) { + // styleset(1 2 7) ==> 'ss01' = 1, 'ss02' = 1, 'ss07' = 1 + feature.mValue = 1; + for (uint32_t v = 0; v < numValues; v++) { + uint32_t nn = values.ElementAt(v); + if (nn == 0 || nn > MAX_SSXX_VALUE) { + continue; + } + feature.mTag = HB_TAG('s','s',('0' + nn / 10), ('0' + nn % 10)); + aFontFeatures.AppendElement(feature); + } + + } else { + NS_ASSERTION(numValues == 1, + "too many values for font-specific font-variant-alternates"); + feature.mValue = values.ElementAt(0); + + switch (av.alternate) { + case NS_FONT_VARIANT_ALTERNATES_STYLISTIC: // salt + feature.mTag = HB_TAG('s','a','l','t'); + break; + case NS_FONT_VARIANT_ALTERNATES_SWASH: // swsh, cswh + feature.mTag = HB_TAG('s','w','s','h'); + aFontFeatures.AppendElement(feature); + feature.mTag = HB_TAG('c','s','w','h'); + break; + case NS_FONT_VARIANT_ALTERNATES_ORNAMENTS: // ornm + feature.mTag = HB_TAG('o','r','n','m'); + break; + case NS_FONT_VARIANT_ALTERNATES_ANNOTATION: // nalt + feature.mTag = HB_TAG('n','a','l','t'); + break; + default: + feature.mTag = 0; + break; + } + + NS_ASSERTION(feature.mTag, "unsupported alternate type"); + if (!feature.mTag) { + continue; + } + aFontFeatures.AppendElement(feature); + } + } +} + /* static */ bool gfxFontShaper::MergeFontFeatures( - const nsTArray& aStyleRuleFeatures, + const gfxFontStyle *aStyle, const nsTArray& aFontFeatures, bool aDisableLigatures, + const nsAString& aFamilyName, nsDataHashtable& aMergedFeatures) { + uint32_t numAlts = aStyle->alternateValues.Length(); + const nsTArray& styleRuleFeatures = + aStyle->featureSettings; + // bail immediately if nothing to do - if (aStyleRuleFeatures.IsEmpty() && + if (styleRuleFeatures.IsEmpty() && aFontFeatures.IsEmpty() && - !aDisableLigatures) { + !aDisableLigatures && + numAlts == 0) { return false; } @@ -1399,10 +1493,25 @@ gfxFontShaper::MergeFontFeatures( aMergedFeatures.Put(feature.mTag, feature.mValue); } + // add font-specific feature values from style rules + if (aStyle->featureValueLookup && numAlts > 0) { + nsAutoTArray featureList; + + // insert list of alternate feature settings + LookupAlternateValues(aStyle->featureValueLookup, aFamilyName, + aStyle->alternateValues, featureList); + + count = featureList.Length(); + for (i = 0; i < count; i++) { + const gfxFontFeature& feature = featureList.ElementAt(i); + aMergedFeatures.Put(feature.mTag, feature.mValue); + } + } + // add feature values from style rules - count = aStyleRuleFeatures.Length(); + count = styleRuleFeatures.Length(); for (i = 0; i < count; i++) { - const gfxFontFeature& feature = aStyleRuleFeatures.ElementAt(i); + const gfxFontFeature& feature = styleRuleFeatures.ElementAt(i); aMergedFeatures.Put(feature.mTag, feature.mValue); } @@ -4791,6 +4900,7 @@ gfxFontStyle::gfxFontStyle(uint8_t aStyle, uint16_t aWeight, int16_t aStretch, gfxFontStyle::gfxFontStyle(const gfxFontStyle& aStyle) : language(aStyle.language), + featureValueLookup(aStyle.featureValueLookup), size(aStyle.size), sizeAdjust(aStyle.sizeAdjust), languageOverride(aStyle.languageOverride), weight(aStyle.weight), stretch(aStyle.stretch), @@ -4798,6 +4908,7 @@ gfxFontStyle::gfxFontStyle(const gfxFontStyle& aStyle) : style(aStyle.style) { featureSettings.AppendElements(aStyle.featureSettings); + alternateValues.AppendElements(aStyle.alternateValues); } int8_t diff --git a/gfx/thebes/gfxFont.h b/gfx/thebes/gfxFont.h index de3d55a42099..47d340bb36c8 100644 --- a/gfx/thebes/gfxFont.h +++ b/gfx/thebes/gfxFont.h @@ -73,9 +73,23 @@ struct THEBES_API gfxFontStyle { // or inferred from the charset nsRefPtr language; + // Features are composed of (1) features from style rules (2) features + // from feature setttings rules and (3) family-specific features. (1) and + // (3) are guaranteed to be mutually exclusive + // custom opentype feature settings nsTArray featureSettings; + // Some font-variant property values require font-specific settings + // defined via @font-feature-values rules. These are resolved after + // font matching occurs. + + // -- list of value tags for specific alternate features + nsTArray alternateValues; + + // -- object used to look these up once the font is matched + nsRefPtr featureValueLookup; + // The logical size of the font, in pixels gfxFloat size; @@ -144,7 +158,9 @@ struct THEBES_API gfxFontStyle { (*reinterpret_cast(&sizeAdjust) == *reinterpret_cast(&other.sizeAdjust)) && (featureSettings == other.featureSettings) && - (languageOverride == other.languageOverride); + (languageOverride == other.languageOverride) && + (alternateValues == other.alternateValues) && + (featureValueLookup == other.featureValueLookup); } static void ParseFontFeatureSettings(const nsString& aFeatureString, @@ -1147,9 +1163,10 @@ public: // returns true if features exist in output, false otherwise static bool - MergeFontFeatures(const nsTArray& aStyleRuleFeatures, + MergeFontFeatures(const gfxFontStyle *aStyle, const nsTArray& aFontFeatures, bool aDisableLigatures, + const nsAString& aFamilyName, nsDataHashtable& aMergedFeatures); protected: @@ -1580,6 +1597,10 @@ public: virtual mozilla::TemporaryRef GetScaledFont(mozilla::gfx::DrawTarget *aTarget) { return gfxPlatform::GetPlatform()->GetScaledFontForFont(aTarget, this); } + bool KerningDisabled() { + return mKerningSet && !mKerningEnabled; + } + protected: bool HasSubstitutionRulesWithSpaceLookups(int32_t aRunScript) { diff --git a/gfx/thebes/gfxFontConstants.h b/gfx/thebes/gfxFontConstants.h index 191201753d11..a7252a3a68f8 100644 --- a/gfx/thebes/gfxFontConstants.h +++ b/gfx/thebes/gfxFontConstants.h @@ -30,4 +30,165 @@ #define NS_FONT_STRETCH_EXTRA_EXPANDED 3 #define NS_FONT_STRETCH_ULTRA_EXPANDED 4 +#define NS_FONT_KERNING_AUTO 0 +#define NS_FONT_KERNING_NONE 1 +#define NS_FONT_KERNING_NORMAL 2 + +#define NS_FONT_SYNTHESIS_WEIGHT 0x1 +#define NS_FONT_SYNTHESIS_STYLE 0x2 + +enum { + eFeatureAlternates_historical, + eFeatureAlternates_stylistic, + eFeatureAlternates_styleset, + eFeatureAlternates_character_variant, + eFeatureAlternates_swash, + eFeatureAlternates_ornaments, + eFeatureAlternates_annotation, + + eFeatureAlternates_numFeatures +}; + +// alternates - simple enumerated values +#define NS_FONT_VARIANT_ALTERNATES_HISTORICAL (1 << eFeatureAlternates_historical) + +// alternates - values that use functional syntax +#define NS_FONT_VARIANT_ALTERNATES_STYLISTIC (1 << eFeatureAlternates_stylistic) +#define NS_FONT_VARIANT_ALTERNATES_STYLESET (1 << eFeatureAlternates_styleset) +#define NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT (1 << eFeatureAlternates_character_variant) +#define NS_FONT_VARIANT_ALTERNATES_SWASH (1 << eFeatureAlternates_swash) +#define NS_FONT_VARIANT_ALTERNATES_ORNAMENTS (1 << eFeatureAlternates_ornaments) +#define NS_FONT_VARIANT_ALTERNATES_ANNOTATION (1 << eFeatureAlternates_annotation) + +#define NS_FONT_VARIANT_ALTERNATES_ENUMERATED_MASK \ + NS_FONT_VARIANT_ALTERNATES_HISTORICAL + +#define NS_FONT_VARIANT_ALTERNATES_FUNCTIONAL_MASK ( \ + NS_FONT_VARIANT_ALTERNATES_STYLISTIC | \ + NS_FONT_VARIANT_ALTERNATES_STYLESET | \ + NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT | \ + NS_FONT_VARIANT_ALTERNATES_SWASH | \ + NS_FONT_VARIANT_ALTERNATES_ORNAMENTS | \ + NS_FONT_VARIANT_ALTERNATES_ANNOTATION ) + +#define NS_FONT_VARIANT_CAPS_NORMAL 0 +#define NS_FONT_VARIANT_CAPS_SMALLCAPS 1 +#define NS_FONT_VARIANT_CAPS_ALLSMALL 2 +#define NS_FONT_VARIANT_CAPS_PETITECAPS 3 +#define NS_FONT_VARIANT_CAPS_ALLPETITE 4 +#define NS_FONT_VARIANT_CAPS_TITLING 5 +#define NS_FONT_VARIANT_CAPS_UNICASE 6 + +enum { + eFeatureEastAsian_jis78, + eFeatureEastAsian_jis83, + eFeatureEastAsian_jis90, + eFeatureEastAsian_jis04, + eFeatureEastAsian_simplified, + eFeatureEastAsian_traditional, + eFeatureEastAsian_full_width, + eFeatureEastAsian_prop_width, + eFeatureEastAsian_ruby, + + eFeatureEastAsian_numFeatures +}; + +#define NS_FONT_VARIANT_EAST_ASIAN_JIS78 (1 << eFeatureEastAsian_jis78) +#define NS_FONT_VARIANT_EAST_ASIAN_JIS83 (1 << eFeatureEastAsian_jis83) +#define NS_FONT_VARIANT_EAST_ASIAN_JIS90 (1 << eFeatureEastAsian_jis90) +#define NS_FONT_VARIANT_EAST_ASIAN_JIS04 (1 << eFeatureEastAsian_jis04) +#define NS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED (1 << eFeatureEastAsian_simplified) +#define NS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL (1 << eFeatureEastAsian_traditional) +#define NS_FONT_VARIANT_EAST_ASIAN_FULL_WIDTH (1 << eFeatureEastAsian_full_width) +#define NS_FONT_VARIANT_EAST_ASIAN_PROP_WIDTH (1 << eFeatureEastAsian_prop_width) +#define NS_FONT_VARIANT_EAST_ASIAN_RUBY (1 << eFeatureEastAsian_ruby) + +#define NS_FONT_VARIANT_EAST_ASIAN_VARIANT_MASK ( \ + NS_FONT_VARIANT_EAST_ASIAN_JIS78 | \ + NS_FONT_VARIANT_EAST_ASIAN_JIS83 | \ + NS_FONT_VARIANT_EAST_ASIAN_JIS90 | \ + NS_FONT_VARIANT_EAST_ASIAN_JIS04 | \ + NS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED | \ + NS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL ) + +#define NS_FONT_VARIANT_EAST_ASIAN_WIDTH_MASK ( \ + NS_FONT_VARIANT_EAST_ASIAN_FULL_WIDTH | \ + NS_FONT_VARIANT_EAST_ASIAN_PROP_WIDTH ) + +enum { + eFeatureLigatures_common, + eFeatureLigatures_no_common, + eFeatureLigatures_discretionary, + eFeatureLigatures_no_discretionary, + eFeatureLigatures_historical, + eFeatureLigatures_no_historical, + eFeatureLigatures_contextual, + eFeatureLigatures_no_contextual, + + eFeatureLigatures_numFeatures +}; + +#define NS_FONT_VARIANT_LIGATURES_COMMON (1 << eFeatureLigatures_common) +#define NS_FONT_VARIANT_LIGATURES_NO_COMMON (1 << eFeatureLigatures_no_common) +#define NS_FONT_VARIANT_LIGATURES_DISCRETIONARY (1 << eFeatureLigatures_discretionary) +#define NS_FONT_VARIANT_LIGATURES_NO_DISCRETIONARY (1 << eFeatureLigatures_no_discretionary) +#define NS_FONT_VARIANT_LIGATURES_HISTORICAL (1 << eFeatureLigatures_historical) +#define NS_FONT_VARIANT_LIGATURES_NO_HISTORICAL (1 << eFeatureLigatures_no_historical) +#define NS_FONT_VARIANT_LIGATURES_CONTEXTUAL (1 << eFeatureLigatures_contextual) +#define NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL (1 << eFeatureLigatures_no_contextual) + +#define NS_FONT_VARIANT_LIGATURES_COMMON_MASK ( \ + NS_FONT_VARIANT_LIGATURES_COMMON | \ + NS_FONT_VARIANT_LIGATURES_NO_COMMON ) + +#define NS_FONT_VARIANT_LIGATURES_DISCRETIONARY_MASK ( \ + NS_FONT_VARIANT_LIGATURES_DISCRETIONARY | \ + NS_FONT_VARIANT_LIGATURES_NO_DISCRETIONARY ) + +#define NS_FONT_VARIANT_LIGATURES_HISTORICAL_MASK ( \ + NS_FONT_VARIANT_LIGATURES_HISTORICAL | \ + NS_FONT_VARIANT_LIGATURES_NO_HISTORICAL ) + +#define NS_FONT_VARIANT_LIGATURES_CONTEXTUAL_MASK \ + NS_FONT_VARIANT_LIGATURES_CONTEXTUAL | \ + NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL + +enum { + eFeatureNumeric_lining, + eFeatureNumeric_oldstyle, + eFeatureNumeric_proportional, + eFeatureNumeric_tabular, + eFeatureNumeric_diagonal_fractions, + eFeatureNumeric_stacked_fractions, + eFeatureNumeric_slashedzero, + eFeatureNumeric_ordinal, + + eFeatureNumeric_numFeatures +}; + +#define NS_FONT_VARIANT_NUMERIC_LINING (1 << eFeatureNumeric_lining) +#define NS_FONT_VARIANT_NUMERIC_OLDSTYLE (1 << eFeatureNumeric_oldstyle) +#define NS_FONT_VARIANT_NUMERIC_PROPORTIONAL (1 << eFeatureNumeric_proportional) +#define NS_FONT_VARIANT_NUMERIC_TABULAR (1 << eFeatureNumeric_tabular) +#define NS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS (1 << eFeatureNumeric_diagonal_fractions) +#define NS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS (1 << eFeatureNumeric_stacked_fractions) +#define NS_FONT_VARIANT_NUMERIC_SLASHZERO (1 << eFeatureNumeric_slashedzero) +#define NS_FONT_VARIANT_NUMERIC_ORDINAL (1 << eFeatureNumeric_ordinal) + +#define NS_FONT_VARIANT_NUMERIC_FIGURE_MASK \ + NS_FONT_VARIANT_NUMERIC_LINING | \ + NS_FONT_VARIANT_NUMERIC_OLDSTYLE + +#define NS_FONT_VARIANT_NUMERIC_SPACING_MASK \ + NS_FONT_VARIANT_NUMERIC_PROPORTIONAL | \ + NS_FONT_VARIANT_NUMERIC_TABULAR + +#define NS_FONT_VARIANT_NUMERIC_FRACTION_MASK \ + NS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS | \ + NS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS + +#define NS_FONT_VARIANT_POSITION_NORMAL 0 +#define NS_FONT_VARIANT_POSITION_SUPER 1 +#define NS_FONT_VARIANT_POSITION_SUB 2 + #endif diff --git a/gfx/thebes/gfxFontFeatures.h b/gfx/thebes/gfxFontFeatures.h index 86085c081f28..2072acee3fbe 100644 --- a/gfx/thebes/gfxFontFeatures.h +++ b/gfx/thebes/gfxFontFeatures.h @@ -7,6 +7,11 @@ #ifndef GFX_FONT_FEATURES_H #define GFX_FONT_FEATURES_H +#include "gfxTypes.h" +#include "nsTHashtable.h" +#include "nsTArray.h" +#include "nsString.h" + // An OpenType feature tag and value pair struct gfxFontFeature { uint32_t mTag; // see http://www.microsoft.com/typography/otspec/featuretags.htm @@ -26,4 +31,95 @@ operator==(const gfxFontFeature& a, const gfxFontFeature& b) return (a.mTag == b.mTag) && (a.mValue == b.mValue); } +struct gfxAlternateValue { + uint32_t alternate; // constants in gfxFontConstants.h + nsString value; // string value to be looked up +}; + +inline bool +operator<(const gfxAlternateValue& a, const gfxAlternateValue& b) +{ + return (a.alternate < b.alternate) || + ((a.alternate == b.alternate) && (a.value < b.value)); +} + +inline bool +operator==(const gfxAlternateValue& a, const gfxAlternateValue& b) +{ + return (a.alternate == b.alternate) && (a.value == b.value); +} + +class THEBES_API gfxFontFeatureValueSet { +public: + NS_INLINE_DECL_REFCOUNTING(gfxFontFeatureValueSet) + + gfxFontFeatureValueSet(); + virtual ~gfxFontFeatureValueSet() {} + + struct ValueList { + ValueList(const nsAString& aName, const nsTArray& aSelectors) + : name(aName), featureSelectors(aSelectors) + {} + nsString name; + nsTArray featureSelectors; + }; + + struct FeatureValues { + uint32_t alternate; + nsTArray valuelist; + }; + + // returns true if found, false otherwise + bool + GetFontFeatureValuesFor(const nsAString& aFamily, + uint32_t aVariantProperty, + const nsAString& aName, + nsTArray& aValues); + void + AddFontFeatureValues(const nsAString& aFamily, + const nsTArray& aValues); + +protected: + struct FeatureValueHashKey { + nsString mFamily; + uint32_t mPropVal; + nsString mName; + + FeatureValueHashKey() + : mPropVal(0) + { } + FeatureValueHashKey(const nsAString& aFamily, + uint32_t aPropVal, + const nsAString& aName) + : mFamily(aFamily), mPropVal(aPropVal), mName(aName) + { } + FeatureValueHashKey(const FeatureValueHashKey& aKey) + : mFamily(aKey.mFamily), mPropVal(aKey.mPropVal), mName(aKey.mName) + { } + }; + + class FeatureValueHashEntry : public PLDHashEntryHdr { + public: + typedef const FeatureValueHashKey &KeyType; + typedef const FeatureValueHashKey *KeyTypePointer; + + FeatureValueHashEntry(KeyTypePointer aKey) { } + FeatureValueHashEntry(const FeatureValueHashEntry& toCopy) + { + NS_ERROR("Should not be called"); + } + ~FeatureValueHashEntry() { } + + bool KeyEquals(const KeyTypePointer aKey) const; + static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } + static PLDHashNumber HashKey(const KeyTypePointer aKey); + enum { ALLOW_MEMMOVE = true }; + + FeatureValueHashKey mKey; + nsTArray mValues; + }; + + nsTHashtable mFontFeatureValues; + }; + #endif diff --git a/gfx/thebes/gfxGraphiteShaper.cpp b/gfx/thebes/gfxGraphiteShaper.cpp index ffe09efa6051..3e77677412bf 100644 --- a/gfx/thebes/gfxGraphiteShaper.cpp +++ b/gfx/thebes/gfxGraphiteShaper.cpp @@ -192,8 +192,13 @@ gfxGraphiteShaper::ShapeText(gfxContext *aContext, nsDataHashtable mergedFeatures; - if (MergeFontFeatures(style->featureSettings, entry->mFeatureSettings, - aShapedText->DisableLigatures(), mergedFeatures)) { + // if style contains font-specific features + if (MergeFontFeatures(style, + mFont->GetFontEntry()->mFeatureSettings, + aShapedText->DisableLigatures(), + mFont->GetFontEntry()->FamilyName(), + mergedFeatures)) + { // enumerate result and insert into Graphite feature list GrFontFeatures f = {mGrFace, grFeatures}; mergedFeatures.Enumerate(AddFeature, &f); diff --git a/gfx/thebes/gfxHarfBuzzShaper.cpp b/gfx/thebes/gfxHarfBuzzShaper.cpp index 8b0abea1b138..7d6065a92933 100644 --- a/gfx/thebes/gfxHarfBuzzShaper.cpp +++ b/gfx/thebes/gfxHarfBuzzShaper.cpp @@ -99,12 +99,14 @@ HBGetTable(hb_face_t *face, hb_tag_t aTag, void *aUserData) */ struct FontCallbackData { - FontCallbackData(gfxHarfBuzzShaper *aShaper, gfxContext *aContext) - : mShaper(aShaper), mContext(aContext) + FontCallbackData(gfxHarfBuzzShaper *aShaper, gfxContext *aContext, + bool aKerning) + : mShaper(aShaper), mContext(aContext), mKerning(aKerning) { } gfxHarfBuzzShaper *mShaper; gfxContext *mContext; + bool mKerning; }; #define UNICODE_BMP_LIMIT 0x10000 @@ -644,7 +646,13 @@ HBGetHKerning(hb_font_t *font, void *font_data, { const FontCallbackData *fcd = static_cast(font_data); - return fcd->mShaper->GetHKerning(first_glyph, second_glyph); + + // return 0 if kerning is explicitly disabled + if (fcd->mKerning) { + return fcd->mShaper->GetHKerning(first_glyph, second_glyph); + } else { + return 0.0; + } } /* @@ -956,7 +964,9 @@ gfxHarfBuzzShaper::ShapeText(gfxContext *aContext, return false; } - FontCallbackData fcd(this, aContext); + const gfxFontStyle *style = mFont->GetStyle(); + // kerning is enabled *except* when explicitly disabled (font-kerning: none) + FontCallbackData fcd(this, aContext, !mFont->KerningDisabled()); hb_font_t *font = hb_font_create(mHBFace); hb_font_set_funcs(font, sHBFontFuncs, &fcd, nullptr); hb_font_set_ppem(font, mFont->GetAdjustedSize(), mFont->GetAdjustedSize()); @@ -966,13 +976,15 @@ gfxHarfBuzzShaper::ShapeText(gfxContext *aContext, nsAutoTArray features; gfxFontEntry *entry = mFont->GetFontEntry(); - const gfxFontStyle *style = mFont->GetStyle(); nsDataHashtable mergedFeatures; - if (MergeFontFeatures(style->featureSettings, - mFont->GetFontEntry()->mFeatureSettings, - aShapedText->DisableLigatures(), mergedFeatures)) { + if (MergeFontFeatures(style, + mFont->GetFontEntry()->mFeatureSettings, + aShapedText->DisableLigatures(), + mFont->GetFontEntry()->FamilyName(), + mergedFeatures)) + { // enumerate result and insert into hb_feature array mergedFeatures.Enumerate(AddFeature, &features); } diff --git a/layout/reftests/font-features/reftest.list b/layout/reftests/font-features/reftest.list index 0f229ab64b13..7eebffa70f35 100644 --- a/layout/reftests/font-features/reftest.list +++ b/layout/reftests/font-features/reftest.list @@ -51,6 +51,38 @@ skip-if(B2G) HTTP(..) == font-features-turkish-override-5.html font-features-tur HTTP(..) == font-features-order-1.html font-features-ref.html HTTP(..) == font-features-order-2.html font-features-noliga.html +# check priority of feature settings vs. font-variant subproperty +HTTP(..) == font-features-order-3.html font-features-noliga.html +pref(layout.css.font-features.enabled,true) HTTP(..) == font-features-order-4.html font-features-noliga.html +pref(layout.css.font-features.enabled,true) HTTP(..) == font-features-order-5.html font-features-hlig.html + +# check priority involving feature settings and font-variant-alternates +pref(layout.css.font-features.enabled,true) HTTP(..) == alternates-order.html alternates-order-ref.html + +# check that font-specific values line up with @font-face feature settings +pref(layout.css.font-features.enabled,true) HTTP(..) == annotations.html annotations-ref.html + +# font-variant subproperties +# test for specific features being on and others off, based on prop values +# (debug problems with font-variant-debug.html which displays all props) +pref(layout.css.font-features.enabled,true) HTTP(..) == font-variant-alternates.html font-variant-alternates-ref.html +pref(layout.css.font-features.enabled,true) HTTP(..) == font-variant-caps.html font-variant-caps-ref.html +pref(layout.css.font-features.enabled,true) HTTP(..) == font-variant-east-asian.html font-variant-east-asian-ref.html +pref(layout.css.font-features.enabled,true) HTTP(..) == font-variant-ligatures.html font-variant-ligatures-ref.html +pref(layout.css.font-features.enabled,true) HTTP(..) == font-variant-numeric.html font-variant-numeric-ref.html +pref(layout.css.font-features.enabled,true) HTTP(..) == font-variant-position.html font-variant-position-ref.html + +# font-kerning +pref(layout.css.font-features.enabled,true) HTTP(..) != font-kerning-normal.html font-kerning-none.html +pref(layout.css.font-features.enabled,true) HTTP(..) != font-kerning-auto.html font-kerning-none.html +pref(layout.css.font-features.enabled,true) HTTP(..) == font-kerning-auto.html font-kerning-normal.html +pref(layout.css.font-features.enabled,true) HTTP(..) == font-kerning-normal.html font-kerning-kern.html +pref(layout.css.font-features.enabled,true) HTTP(..) == font-kerning-none.html font-kerning-nokern.html +pref(layout.css.font-features.enabled,true) HTTP(..) == font-kerning-1.html font-kerning-none.html +pref(layout.css.font-features.enabled,true) HTTP(..) == font-kerning-2.html font-kerning-normal.html +pref(layout.css.font-features.enabled,true) HTTP(..) == font-kerning-3.html font-kerning-none.html +pref(layout.css.font-features.enabled,true) HTTP(..) != font-kerning-table-none.html font-kerning-table-normal.html + # sanity check for kerning - with no spaces, kerning should occur HTTP(..) == kerning-sanity-check-kern.html kerning-sanity-check-default.html HTTP(..) != kerning-sanity-check-nokern.html kerning-sanity-check-default.html diff --git a/layout/style/Declaration.cpp b/layout/style/Declaration.cpp index a70daf0036e1..6ffa204016c4 100644 --- a/layout/style/Declaration.cpp +++ b/layout/style/Declaration.cpp @@ -537,6 +537,22 @@ Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue) const *data->ValueFor(eCSSProperty_font_feature_settings); const nsCSSValue &languageOverride = *data->ValueFor(eCSSProperty_font_language_override); + const nsCSSValue &fontKerning = + *data->ValueFor(eCSSProperty_font_kerning); + const nsCSSValue &fontSynthesis = + *data->ValueFor(eCSSProperty_font_synthesis); + const nsCSSValue &fontVariantAlternates = + *data->ValueFor(eCSSProperty_font_variant_alternates); + const nsCSSValue &fontVariantCaps = + *data->ValueFor(eCSSProperty_font_variant_caps); + const nsCSSValue &fontVariantEastAsian = + *data->ValueFor(eCSSProperty_font_variant_east_asian); + const nsCSSValue &fontVariantLigatures = + *data->ValueFor(eCSSProperty_font_variant_ligatures); + const nsCSSValue &fontVariantNumeric = + *data->ValueFor(eCSSProperty_font_variant_numeric); + const nsCSSValue &fontVariantPosition = + *data->ValueFor(eCSSProperty_font_variant_position); if (systemFont && systemFont->GetUnit() != eCSSUnit_None && @@ -550,21 +566,39 @@ Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue) const stretch.GetUnit() != eCSSUnit_System_Font || sizeAdjust.GetUnit() != eCSSUnit_System_Font || featureSettings.GetUnit() != eCSSUnit_System_Font || - languageOverride.GetUnit() != eCSSUnit_System_Font) { + languageOverride.GetUnit() != eCSSUnit_System_Font || + fontKerning.GetUnit() != eCSSUnit_System_Font || + fontSynthesis.GetUnit() != eCSSUnit_System_Font || + fontVariantAlternates.GetUnit() != eCSSUnit_System_Font || + fontVariantCaps.GetUnit() != eCSSUnit_System_Font || + fontVariantEastAsian.GetUnit() != eCSSUnit_System_Font || + fontVariantLigatures.GetUnit() != eCSSUnit_System_Font || + fontVariantNumeric.GetUnit() != eCSSUnit_System_Font || + fontVariantPosition.GetUnit() != eCSSUnit_System_Font) { // This can't be represented as a shorthand. return; } systemFont->AppendToString(eCSSProperty__x_system_font, aValue); } else { // The font-stretch, font-size-adjust, - // -moz-font-feature-settings, and -moz-font-language-override - // properties are reset by this shorthand property to their + // -moz-font-feature-settings, -moz-font-language-override + // along with kerning, synthesis and other font-variant + // subproperties are reset by this shorthand property to their // initial values, but can't be represented in its syntax. if (stretch.GetUnit() != eCSSUnit_Enumerated || stretch.GetIntValue() != NS_STYLE_FONT_STRETCH_NORMAL || sizeAdjust.GetUnit() != eCSSUnit_None || featureSettings.GetUnit() != eCSSUnit_Normal || - languageOverride.GetUnit() != eCSSUnit_Normal) { + languageOverride.GetUnit() != eCSSUnit_Normal || + fontKerning.GetIntValue() != NS_FONT_KERNING_AUTO || + fontSynthesis.GetIntValue() != + (NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE) || + fontVariantAlternates.GetUnit() != eCSSUnit_Normal || + fontVariantCaps.GetUnit() != eCSSUnit_Normal || + fontVariantEastAsian.GetUnit() != eCSSUnit_Normal || + fontVariantLigatures.GetUnit() != eCSSUnit_Normal || + fontVariantNumeric.GetUnit() != eCSSUnit_Normal || + fontVariantPosition.GetUnit() != eCSSUnit_Normal) { return; } diff --git a/layout/style/Rule.h b/layout/style/Rule.h index 3ba96accd4f2..900040533727 100644 --- a/layout/style/Rule.h +++ b/layout/style/Rule.h @@ -65,7 +65,8 @@ public: KEYFRAME_RULE, KEYFRAMES_RULE, DOCUMENT_RULE, - SUPPORTS_RULE + SUPPORTS_RULE, + FONT_FEATURE_VALUES_RULE }; virtual int32_t GetType() const = 0; diff --git a/layout/style/nsCSSFontDescList.h b/layout/style/nsCSSFontDescList.h index 041a1d54fba4..cbbaeab630ba 100644 --- a/layout/style/nsCSSFontDescList.h +++ b/layout/style/nsCSSFontDescList.h @@ -9,5 +9,8 @@ CSS_FONT_DESC(font-weight, Weight) CSS_FONT_DESC(font-stretch, Stretch) CSS_FONT_DESC(src, Src) CSS_FONT_DESC(unicode-range, UnicodeRange) + +/* Note: the parsing code explicitly also accepts font-feature-settings + and font-language-override. */ CSS_FONT_DESC(-moz-font-feature-settings, FontFeatureSettings) CSS_FONT_DESC(-moz-font-language-override, FontLanguageOverride) diff --git a/layout/style/nsCSSKeywordList.h b/layout/style/nsCSSKeywordList.h index e146d3437400..3c5deefedc81 100644 --- a/layout/style/nsCSSKeywordList.h +++ b/layout/style/nsCSSKeywordList.h @@ -163,11 +163,14 @@ CSS_KEY(activeborder, activeborder) CSS_KEY(activecaption, activecaption) CSS_KEY(alias, alias) CSS_KEY(all, all) +CSS_KEY(all-petite-caps, all_petite_caps) CSS_KEY(all-scroll, all_scroll) +CSS_KEY(all-small-caps, all_small_caps) CSS_KEY(alpha, alpha) CSS_KEY(alternate, alternate) CSS_KEY(alternate-reverse, alternate_reverse) CSS_KEY(always, always) +CSS_KEY(annotation, annotation) CSS_KEY(appworkspace, appworkspace) CSS_KEY(armenian, armenian) CSS_KEY(auto, auto) @@ -200,6 +203,7 @@ CSS_KEY(captiontext, captiontext) CSS_KEY(cell, cell) CSS_KEY(center, center) CSS_KEY(ch, ch) +CSS_KEY(character-variant, character_variant) CSS_KEY(circle, circle) CSS_KEY(cjk-ideographic, cjk_ideographic) CSS_KEY(clip, clip) @@ -209,6 +213,7 @@ CSS_KEY(closest-side, closest_side) CSS_KEY(cm, cm) CSS_KEY(col-resize, col_resize) CSS_KEY(collapse, collapse) +CSS_KEY(common-ligatures, common_ligatures) CSS_KEY(column, column) CSS_KEY(column-reverse, column_reverse) CSS_KEY(condensed, condensed) @@ -217,6 +222,7 @@ CSS_KEY(content-box, content_box) CSS_KEY(context-menu, context_menu) CSS_KEY(continuous, continuous) CSS_KEY(copy, copy) +CSS_KEY(contextual, contextual) CSS_KEY(cover, cover) CSS_KEY(crop, crop) CSS_KEY(cross, cross) @@ -227,9 +233,11 @@ CSS_KEY(decimal, decimal) CSS_KEY(decimal-leading-zero, decimal_leading_zero) CSS_KEY(default, default) CSS_KEY(deg, deg) +CSS_KEY(diagonal-fractions, diagonal_fractions) CSS_KEY(dialog, dialog) CSS_KEY(disabled, disabled) CSS_KEY(disc, disc) +CSS_KEY(discretionary-ligatures, discretionary_ligatures) CSS_KEY(dotted, dotted) CSS_KEY(double, double) CSS_KEY(e-resize, e_resize) @@ -273,6 +281,8 @@ CSS_KEY(highlight, highlight) CSS_KEY(highlighttext, highlighttext) CSS_KEY(hiragana, hiragana) CSS_KEY(hiragana-iroha, hiragana_iroha) +CSS_KEY(historical-forms, historical_forms) +CSS_KEY(historical-ligatures, historical_ligatures) CSS_KEY(horizontal, horizontal) CSS_KEY(hz, hz) CSS_KEY(icon, icon) @@ -297,6 +307,10 @@ CSS_KEY(inset, inset) CSS_KEY(inside, inside) CSS_KEY(interpolatematrix, interpolatematrix) CSS_KEY(italic, italic) +CSS_KEY(jis78, jis78) +CSS_KEY(jis83, jis83) +CSS_KEY(jis90, jis90) +CSS_KEY(jis04, jis04) CSS_KEY(justify, justify) CSS_KEY(katakana, katakana) CSS_KEY(katakana-iroha, katakana_iroha) @@ -309,6 +323,7 @@ CSS_KEY(left, left) CSS_KEY(lighter, lighter) CSS_KEY(line-through, line_through) CSS_KEY(linear, linear) +CSS_KEY(lining-nums, lining_nums) CSS_KEY(list-item, list_item) CSS_KEY(logical, logical) CSS_KEY(lower-alpha, lower_alpha) @@ -337,7 +352,11 @@ CSS_KEY(narrower, narrower) CSS_KEY(ne-resize, ne_resize) CSS_KEY(nesw-resize, nesw_resize) CSS_KEY(no-close-quote, no_close_quote) +CSS_KEY(no-common-ligatures, no_common_ligatures) +CSS_KEY(no-contextual, no_contextual) +CSS_KEY(no-discretionary-ligatures, no_discretionary_ligatures) CSS_KEY(no-drop, no_drop) +CSS_KEY(no-historical-ligatures, no_historical_ligatures) CSS_KEY(no-open-quote, no_open_quote) CSS_KEY(no-repeat, no_repeat) CSS_KEY(none, none) @@ -348,7 +367,10 @@ CSS_KEY(ns-resize, ns_resize) CSS_KEY(nw-resize, nw_resize) CSS_KEY(nwse-resize, nwse_resize) CSS_KEY(oblique, oblique) +CSS_KEY(oldstyle-nums, oldstyle_nums) CSS_KEY(open-quote, open_quote) +CSS_KEY(ordinal, ordinal) +CSS_KEY(ornaments, ornaments) CSS_KEY(outset, outset) CSS_KEY(outside, outside) CSS_KEY(overline, overline) @@ -357,6 +379,7 @@ CSS_KEY(painted, painted) CSS_KEY(paused, paused) CSS_KEY(pc, pc) CSS_KEY(perspective, perspective) +CSS_KEY(petite-caps, petite_caps) CSS_KEY(physical, physical) CSS_KEY(pointer, pointer) CSS_KEY(portrait, portrait) @@ -366,6 +389,8 @@ CSS_KEY(pre-line, pre_line) CSS_KEY(preserve-3d, preserve_3d) CSS_KEY(progress, progress) CSS_KEY(progressive, progressive) +CSS_KEY(proportional-nums, proportional_nums) +CSS_KEY(proportional-width, proportional_width) CSS_KEY(pt, pt) CSS_KEY(px, px) CSS_KEY(rad, rad) @@ -388,6 +413,7 @@ CSS_KEY(row, row) CSS_KEY(row-resize, row_resize) CSS_KEY(row-reverse, row_reverse) CSS_KEY(rtl, rtl) +CSS_KEY(ruby, ruby) CSS_KEY(running, running) CSS_KEY(s, s) CSS_KEY(s-resize, s_resize) @@ -409,9 +435,11 @@ CSS_KEY(semi-condensed, semi_condensed) CSS_KEY(semi-expanded, semi_expanded) CSS_KEY(separate, separate) CSS_KEY(show, show) +CSS_KEY(simplified, simplified) CSS_KEY(skew, skew) CSS_KEY(skewx, skewx) CSS_KEY(skewy, skewy) +CSS_KEY(slashed-zero, slashed_zero) CSS_KEY(small, small) CSS_KEY(small-caps, small_caps) CSS_KEY(small-caption, small_caption) @@ -421,6 +449,7 @@ CSS_KEY(solid, solid) CSS_KEY(space-around, space_around) CSS_KEY(space-between, space_between) CSS_KEY(square, square) +CSS_KEY(stacked-fractions, stacked_fractions) CSS_KEY(start, start) CSS_KEY(static, static) CSS_KEY(status-bar, status_bar) @@ -429,9 +458,13 @@ CSS_KEY(step-start, step_start) CSS_KEY(stretch, stretch) CSS_KEY(stretch-to-fit, stretch_to_fit) CSS_KEY(stroke, stroke) +CSS_KEY(style, style) +CSS_KEY(styleset, styleset) +CSS_KEY(stylistic, stylistic) CSS_KEY(sub, sub) CSS_KEY(super, super) CSS_KEY(sw-resize, sw_resize) +CSS_KEY(swash, swash) CSS_KEY(table, table) CSS_KEY(table-caption, table_caption) CSS_KEY(table-cell, table_cell) @@ -441,6 +474,7 @@ CSS_KEY(table-footer-group, table_footer_group) CSS_KEY(table-header-group, table_header_group) CSS_KEY(table-row, table_row) CSS_KEY(table-row-group, table_row_group) +CSS_KEY(tabular-nums, tabular_nums) CSS_KEY(text, text) CSS_KEY(text-bottom, text_bottom) CSS_KEY(text-top, text_top) @@ -451,9 +485,11 @@ CSS_KEY(threedface, threedface) CSS_KEY(threedhighlight, threedhighlight) CSS_KEY(threedlightshadow, threedlightshadow) CSS_KEY(threedshadow, threedshadow) +CSS_KEY(titling-caps, titling_caps) CSS_KEY(toggle, toggle) CSS_KEY(top, top) CSS_KEY(top-outside, top_outside) +CSS_KEY(traditional, traditional) CSS_KEY(translate, translate) CSS_KEY(translate3d, translate3d) CSS_KEY(translatex, translatex) @@ -464,6 +500,7 @@ CSS_KEY(tri-state, tri_state) CSS_KEY(ultra-condensed, ultra_condensed) CSS_KEY(ultra-expanded, ultra_expanded) CSS_KEY(underline, underline) +CSS_KEY(unicase, unicase) CSS_KEY(upper-alpha, upper_alpha) CSS_KEY(upper-latin, upper_latin) CSS_KEY(upper-roman, upper_roman) @@ -477,6 +514,7 @@ CSS_KEY(visiblestroke, visiblestroke) CSS_KEY(w-resize, w_resize) CSS_KEY(wait, wait) CSS_KEY(wavy, wavy) +CSS_KEY(weight, weight) CSS_KEY(wider, wider) CSS_KEY(window, window) CSS_KEY(windowframe, windowframe) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 068c3a1e9e23..01dbbd463494 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -375,6 +375,9 @@ protected: void* aProcessData); bool ParseFontFaceRule(RuleAppendFunc aAppendFunc, void* aProcessData); + bool ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc, + void* aProcessData); + bool ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule *aRule); bool ParseFontDescriptor(nsCSSFontFaceRule* aRule); bool ParseFontDescriptorValue(nsCSSFontDesc aDescID, nsCSSValue& aValue); @@ -562,6 +565,14 @@ protected: bool ParseCounterData(nsCSSProperty aPropID); bool ParseCursor(); bool ParseFont(); + bool ParseFontSynthesis(nsCSSValue& aValue); + bool ParseSingleAlternate(int32_t& aWhichFeature, nsCSSValue& aValue); + bool ParseFontVariantAlternates(nsCSSValue& aValue); + bool ParseBitmaskValues(nsCSSValue& aValue, const int32_t aKeywordTable[], + const int32_t aMasks[]); + bool ParseFontVariantEastAsian(nsCSSValue& aValue); + bool ParseFontVariantLigatures(nsCSSValue& aValue); + bool ParseFontVariantNumeric(nsCSSValue& aValue); bool ParseFontWeight(nsCSSValue& aValue); bool ParseOneFamily(nsAString& aFamily, bool& aOneKeyword); bool ParseFamily(nsCSSValue& aValue); @@ -665,12 +676,13 @@ protected: /* Functions for transform Parsing */ bool ParseSingleTransform(bool aIsPrefixed, nsCSSValue& aValue, bool& aIs3D); bool ParseFunction(const nsString &aFunction, const int32_t aAllowedTypes[], - uint16_t aMinElems, uint16_t aMaxElems, - nsCSSValue &aValue); + int32_t aVariantMaskAll, uint16_t aMinElems, + uint16_t aMaxElems, nsCSSValue &aValue); bool ParseFunctionInternals(const int32_t aVariantMask[], - uint16_t aMinElems, - uint16_t aMaxElems, - InfallibleTArray& aOutput); + int32_t aVariantMaskAll, + uint16_t aMinElems, + uint16_t aMaxElems, + InfallibleTArray& aOutput); /* Functions for transform-origin/perspective-origin Parsing */ bool ParseTransformOrigin(bool aPerspective); @@ -1626,6 +1638,11 @@ CSSParserImpl::ParseAtRule(RuleAppendFunc aAppendFunc, parseFunc = &CSSParserImpl::ParseFontFaceRule; newSection = eCSSSection_General; + } else if (mToken.mIdent.LowerCaseEqualsLiteral("font-feature-values") && + nsCSSFontFeatureValuesRule::PrefEnabled()) { + parseFunc = &CSSParserImpl::ParseFontFeatureValuesRule; + newSection = eCSSSection_General; + } else if (mToken.mIdent.LowerCaseEqualsLiteral("page")) { parseFunc = &CSSParserImpl::ParsePageRule; newSection = eCSSSection_General; @@ -2351,6 +2368,233 @@ CSSParserImpl::ParseFontDescriptor(nsCSSFontFaceRule* aRule) return true; } +// @font-feature-values # { +// @ { +// : +; +// : +; +// ... +// } +// ... +// } + +bool +CSSParserImpl::ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc, + void* aData) +{ + nsRefPtr + valuesRule(new nsCSSFontFeatureValuesRule()); + + // parse family list + nsCSSValue familyValue; + + if (!ParseFamily(familyValue) || + familyValue.GetUnit() != eCSSUnit_Families) + { + REPORT_UNEXPECTED_TOKEN(PEFFVNoFamily); + return false; + } + + // add family to rule + nsAutoString familyList; + bool hasGeneric; + familyValue.GetStringValue(familyList); + valuesRule->SetFamilyList(familyList, hasGeneric); + + // family list has generic ==> parse error + if (hasGeneric) { + REPORT_UNEXPECTED_TOKEN(PEFFVGenericInFamilyList); + return false; + } + + // open brace + if (!ExpectSymbol('{', true)) { + REPORT_UNEXPECTED_TOKEN(PEFFVBlockStart); + return false; + } + + // list of sets of feature values, each set bound to a specific + // feature-type (e.g. swash, annotation) + for (;;) { + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF); + break; + } + if (mToken.IsSymbol('}')) { // done! + UngetToken(); + break; + } + + if (!ParseFontFeatureValueSet(valuesRule)) { + if (!SkipAtRule(false)) { + break; + } + } + } + if (!ExpectSymbol('}', true)) { + REPORT_UNEXPECTED_TOKEN(PEFFVUnexpectedBlockEnd); + SkipUntil('}'); + return false; + } + + (*aAppendFunc)(valuesRule, aData); + return true; +} + +#define NUMVALUES_NO_LIMIT 0xFFFF + +// parse a single value set containing name-value pairs for a single feature type +// @ { [ : + ; ]* } +// Ex: @swash { flowing: 1; delicate: 2; } +bool +CSSParserImpl::ParseFontFeatureValueSet(nsCSSFontFeatureValuesRule + *aFeatureValuesRule) +{ + // -- @keyword (e.g. swash, styleset) + if (eCSSToken_AtKeyword != mToken.mType) { + REPORT_UNEXPECTED_TOKEN(PEFontFeatureValuesNoAt); + OUTPUT_ERROR(); + UngetToken(); + return false; + } + + // which font-specific variant of font-variant-alternates + int32_t whichVariant; + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent); + if (keyword == eCSSKeyword_UNKNOWN || + !nsCSSProps::FindKeyword(keyword, + nsCSSProps::kFontVariantAlternatesFuncsKTable, + whichVariant)) + { + if (!NonMozillaVendorIdentifier(mToken.mIdent)) { + REPORT_UNEXPECTED_TOKEN(PEFFVUnknownFontVariantPropValue); + OUTPUT_ERROR(); + } + UngetToken(); + return false; + } + + nsAutoString featureType(mToken.mIdent); + + // open brace + if (!ExpectSymbol('{', true)) { + REPORT_UNEXPECTED_TOKEN(PEFFVValueSetStart); + return false; + } + + // styleset and character-variant can be multi-valued, otherwise single value + int32_t limitNumValues; + + switch (keyword) { + case eCSSKeyword_styleset: + limitNumValues = NUMVALUES_NO_LIMIT; + break; + case eCSSKeyword_character_variant: + limitNumValues = 2; + break; + default: + limitNumValues = 1; + break; + } + + // -- ident integer+ [, ident integer+] + nsAutoTArray values; + + // list of font-feature-values-declaration's + for (;;) { + nsAutoString valueId; + + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF); + break; + } + + // ignore extra semicolons + if (mToken.IsSymbol(';')) { + continue; + } + + // close brace ==> done + if (mToken.IsSymbol('}')) { + break; + } + + // ident + if (eCSSToken_Ident != mToken.mType) { + REPORT_UNEXPECTED_TOKEN(PEFFVExpectedIdent); + if (!SkipDeclaration(true)) { + break; + } + continue; + } + + valueId.Assign(mToken.mIdent); + + // colon + if (!ExpectSymbol(':', true)) { + REPORT_UNEXPECTED_TOKEN(PEParseDeclarationNoColon); + OUTPUT_ERROR(); + if (!SkipDeclaration(true)) { + break; + } + continue; + } + + // value list + nsAutoTArray featureSelectors; + + nsCSSValue intValue; + while (ParseNonNegativeVariant(intValue, VARIANT_INTEGER, nullptr)) { + featureSelectors.AppendElement(uint32_t(intValue.GetIntValue())); + } + + int32_t numValues = featureSelectors.Length(); + + if (numValues == 0) { + REPORT_UNEXPECTED_TOKEN(PEFFVExpectedValue); + OUTPUT_ERROR(); + if (!SkipDeclaration(true)) { + break; + } + continue; + } + + if (numValues > limitNumValues) { + REPORT_UNEXPECTED_P(PEFFVTooManyValues, featureType); + OUTPUT_ERROR(); + if (!SkipDeclaration(true)) { + break; + } + continue; + } + + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEFFVUnexpectedEOF); + gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors); + values.AppendElement(v); + break; + } + + // ';' or '}' to end definition + if (!mToken.IsSymbol(';') && !mToken.IsSymbol('}')) { + REPORT_UNEXPECTED_TOKEN(PEFFVValueDefinitionTrailing); + OUTPUT_ERROR(); + if (!SkipDeclaration(true)) { + break; + } + continue; + } + + gfxFontFeatureValueSet::ValueList v(valueId, featureSelectors); + values.AppendElement(v); + + if (mToken.IsSymbol('}')) { + break; + } + } + + aFeatureValuesRule->AddValueList(whichVariant, values); + return true; +} bool CSSParserImpl::ParseKeyframesRule(RuleAppendFunc aAppendFunc, void* aData) @@ -6370,6 +6614,16 @@ CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue, switch (aPropID) { case eCSSProperty_font_family: return ParseFamily(aValue); + case eCSSProperty_font_synthesis: + return ParseFontSynthesis(aValue); + case eCSSProperty_font_variant_alternates: + return ParseFontVariantAlternates(aValue); + case eCSSProperty_font_variant_east_asian: + return ParseFontVariantEastAsian(aValue); + case eCSSProperty_font_variant_ligatures: + return ParseFontVariantLigatures(aValue); + case eCSSProperty_font_variant_numeric: + return ParseFontVariantNumeric(aValue); case eCSSProperty_font_feature_settings: return ParseFontFeatureSettings(aValue); case eCSSProperty_font_weight: @@ -7993,7 +8247,7 @@ CSSParserImpl::ParseCalcTerm(nsCSSValue& aValue, int32_t& aVariantMask) // ... or just a value UngetToken(); // Always pass VARIANT_NUMBER to ParseVariant so that unitless zero - // always gets picked up + // always gets picked up if (!ParseVariant(aValue, aVariantMask | VARIANT_NUMBER, nullptr)) { return false; } @@ -8303,6 +8557,14 @@ CSSParserImpl::ParseFont() AppendValue(eCSSProperty_font_size_adjust, family); AppendValue(eCSSProperty_font_feature_settings, family); AppendValue(eCSSProperty_font_language_override, family); + AppendValue(eCSSProperty_font_kerning, family); + AppendValue(eCSSProperty_font_synthesis, family); + AppendValue(eCSSProperty_font_variant_alternates, family); + AppendValue(eCSSProperty_font_variant_caps, family); + AppendValue(eCSSProperty_font_variant_east_asian, family); + AppendValue(eCSSProperty_font_variant_ligatures, family); + AppendValue(eCSSProperty_font_variant_numeric, family); + AppendValue(eCSSProperty_font_variant_position, family); } else { AppendValue(eCSSProperty__x_system_font, family); @@ -8317,6 +8579,14 @@ CSSParserImpl::ParseFont() AppendValue(eCSSProperty_font_size_adjust, systemFont); AppendValue(eCSSProperty_font_feature_settings, systemFont); AppendValue(eCSSProperty_font_language_override, systemFont); + AppendValue(eCSSProperty_font_kerning, systemFont); + AppendValue(eCSSProperty_font_synthesis, systemFont); + AppendValue(eCSSProperty_font_variant_alternates, systemFont); + AppendValue(eCSSProperty_font_variant_caps, systemFont); + AppendValue(eCSSProperty_font_variant_east_asian, systemFont); + AppendValue(eCSSProperty_font_variant_ligatures, systemFont); + AppendValue(eCSSProperty_font_variant_numeric, systemFont); + AppendValue(eCSSProperty_font_variant_position, systemFont); } return true; } @@ -8380,12 +8650,287 @@ CSSParserImpl::ParseFont() AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None)); AppendValue(eCSSProperty_font_feature_settings, nsCSSValue(eCSSUnit_Normal)); AppendValue(eCSSProperty_font_language_override, nsCSSValue(eCSSUnit_Normal)); + AppendValue(eCSSProperty_font_kerning, + nsCSSValue(NS_FONT_KERNING_AUTO, eCSSUnit_Enumerated)); + AppendValue(eCSSProperty_font_synthesis, + nsCSSValue(NS_FONT_SYNTHESIS_WEIGHT | NS_FONT_SYNTHESIS_STYLE, + eCSSUnit_Enumerated)); + AppendValue(eCSSProperty_font_variant_alternates, + nsCSSValue(eCSSUnit_Normal)); + AppendValue(eCSSProperty_font_variant_caps, nsCSSValue(eCSSUnit_Normal)); + AppendValue(eCSSProperty_font_variant_east_asian, + nsCSSValue(eCSSUnit_Normal)); + AppendValue(eCSSProperty_font_variant_ligatures, + nsCSSValue(eCSSUnit_Normal)); + AppendValue(eCSSProperty_font_variant_numeric, + nsCSSValue(eCSSUnit_Normal)); + AppendValue(eCSSProperty_font_variant_position, + nsCSSValue(eCSSUnit_Normal)); return true; } } return false; } +bool +CSSParserImpl::ParseFontSynthesis(nsCSSValue& aValue) +{ + if (!ParseVariant(aValue, VARIANT_HK | VARIANT_NONE, + nsCSSProps::kFontSynthesisKTable)) { + return false; + } + + // first value 'none' ==> done + if (eCSSUnit_None == aValue.GetUnit() || + eCSSUnit_Initial == aValue.GetUnit() || + eCSSUnit_Inherit == aValue.GetUnit()) + { + return true; + } + + // look for a second value + int32_t intValue = aValue.GetIntValue(); + nsCSSValue nextValue; + + if (ParseEnum(nextValue, nsCSSProps::kFontSynthesisKTable)) { + int32_t nextIntValue = nextValue.GetIntValue(); + if (nextIntValue & intValue) { + return false; + } + aValue.SetIntValue(nextIntValue | intValue, eCSSUnit_Enumerated); + } + + return true; +} + +// font-variant-alternates allows for a combination of multiple +// simple enumerated values and functional values. Functional values have +// parameter lists with one or more idents which are later resolved +// based on values defined in @font-feature-value rules. +// +// font-variant-alternates: swash(flowing), historical-forms, styleset(alt-g, alt-m); +// +// So for this the nsCSSValue is set to a pair value, with one +// value for a bitmask of both simple and functional property values +// and another value containing a ValuePairList with lists of idents +// for each functional property value. +// +// pairValue +// o intValue +// NS_FONT_VARIANT_ALTERNATES_SWASH | +// NS_FONT_VARIANT_ALTERNATES_STYLESET +// o valuePairList, each element with +// - intValue - indicates which alternate +// - string or valueList of strings +// +// Note: when only 'historical-forms' is specified, there are no +// functional values to store, in which case the valuePairList is a +// single element dummy list. In all other cases, the length of the +// list will match the number of functional values. + +#define MAX_ALLOWED_FEATURES 512 + +bool +CSSParserImpl::ParseSingleAlternate(int32_t& aWhichFeature, + nsCSSValue& aValue) +{ + if (!GetToken(true)) { + return false; + } + + bool isIdent = (mToken.mType == eCSSToken_Ident); + if (mToken.mType != eCSSToken_Function && !isIdent) { + UngetToken(); + return false; + } + + // ident ==> simple enumerated prop val (e.g. historical-forms) + // function ==> e.g. swash(flowing) styleset(alt-g, alt-m) + + nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent); + if (!(eCSSKeyword_UNKNOWN < keyword && + nsCSSProps::FindKeyword(keyword, + (isIdent ? + nsCSSProps::kFontVariantAlternatesKTable : + nsCSSProps::kFontVariantAlternatesFuncsKTable), + aWhichFeature))) + { + // failed, pop token + UngetToken(); + return false; + } + + if (isIdent) { + aValue.SetIntValue(aWhichFeature, eCSSUnit_Enumerated); + return true; + } + + uint16_t maxElems = 1; + if (keyword == eCSSKeyword_styleset || + keyword == eCSSKeyword_character_variant) { + maxElems = MAX_ALLOWED_FEATURES; + } + return ParseFunction(mToken.mIdent, nullptr, VARIANT_IDENTIFIER, + 1, maxElems, aValue); +} + +bool +CSSParserImpl::ParseFontVariantAlternates(nsCSSValue& aValue) +{ + if (ParseVariant(aValue, VARIANT_INHERIT | VARIANT_NORMAL, nullptr)) { + return true; + } + + // iterate through parameters + nsCSSValue listValue; + int32_t feature, featureFlags = 0; + + // if no functional values, this may be a list with a single, unused element + listValue.SetListValue(); + + nsCSSValueList* list = nullptr; + nsCSSValue value; + while (ParseSingleAlternate(feature, value)) { + + // check to make sure value not already set + if (feature == 0 || + feature & featureFlags) { + return false; + } + + featureFlags |= feature; + + // if function, need to add to the list of functions + if (value.GetUnit() == eCSSUnit_Function) { + if (!list) { + list = listValue.GetListValue(); + } else { + list->mNext = new nsCSSValueList; + list = list->mNext; + } + list->mValue = value; + } + } + + nsCSSValue featureValue; + featureValue.SetIntValue(featureFlags, eCSSUnit_Enumerated); + aValue.SetPairValue(featureValue, listValue); + + return true; +} + +#define MASK_END_VALUE -1 + +// aMasks - array of masks for mutually-exclusive property values, +// e.g. proportial-nums, tabular-nums + +bool +CSSParserImpl::ParseBitmaskValues(nsCSSValue& aValue, + const int32_t aKeywordTable[], + const int32_t aMasks[]) +{ + if (!ParseVariant(aValue, VARIANT_HMK, aKeywordTable)) { + return false; + } + + // first value 'normal', 'inherit', 'initial' ==> done + if (eCSSUnit_Normal == aValue.GetUnit() || + eCSSUnit_Initial == aValue.GetUnit() || + eCSSUnit_Inherit == aValue.GetUnit()) + { + return true; + } + + // look for more values + nsCSSValue nextValue; + int32_t mergedValue = aValue.GetIntValue(); + + while (ParseEnum(nextValue, aKeywordTable)) + { + int32_t nextIntValue = nextValue.GetIntValue(); + + // check to make sure value not already set + if (nextIntValue & mergedValue) { + return false; + } + + const int32_t *m = aMasks; + int32_t c = 0; + + while (*m != MASK_END_VALUE) { + if (*m & nextIntValue) { + c = mergedValue & *m; + break; + } + m++; + } + + if (c) { + return false; + } + + mergedValue |= nextIntValue; + } + + aValue.SetIntValue(mergedValue, eCSSUnit_Enumerated); + + return true; +} + +static const int32_t maskEastAsian[] = { + NS_FONT_VARIANT_EAST_ASIAN_VARIANT_MASK, + NS_FONT_VARIANT_EAST_ASIAN_WIDTH_MASK, + MASK_END_VALUE +}; + +bool +CSSParserImpl::ParseFontVariantEastAsian(nsCSSValue& aValue) +{ + NS_ASSERTION(maskEastAsian[NS_ARRAY_LENGTH(maskEastAsian) - 1] == + MASK_END_VALUE, + "incorrectly terminated array"); + + return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantEastAsianKTable, + maskEastAsian); +} + +static const int32_t maskLigatures[] = { + NS_FONT_VARIANT_LIGATURES_COMMON_MASK, + NS_FONT_VARIANT_LIGATURES_DISCRETIONARY_MASK, + NS_FONT_VARIANT_LIGATURES_HISTORICAL_MASK, + NS_FONT_VARIANT_LIGATURES_CONTEXTUAL_MASK, + MASK_END_VALUE +}; + +bool +CSSParserImpl::ParseFontVariantLigatures(nsCSSValue& aValue) +{ + NS_ASSERTION(maskLigatures[NS_ARRAY_LENGTH(maskLigatures) - 1] == + MASK_END_VALUE, + "incorrectly terminated array"); + + return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantLigaturesKTable, + maskLigatures); +} + +static const int32_t maskNumeric[] = { + NS_FONT_VARIANT_NUMERIC_FIGURE_MASK, + NS_FONT_VARIANT_NUMERIC_SPACING_MASK, + NS_FONT_VARIANT_NUMERIC_FRACTION_MASK, + MASK_END_VALUE +}; + +bool +CSSParserImpl::ParseFontVariantNumeric(nsCSSValue& aValue) +{ + NS_ASSERTION(maskNumeric[NS_ARRAY_LENGTH(maskNumeric) - 1] == + MASK_END_VALUE, + "incorrectly terminated array"); + + return ParseBitmaskValues(aValue, nsCSSProps::kFontVariantNumericKTable, + maskNumeric); +} + bool CSSParserImpl::ParseFontWeight(nsCSSValue& aValue) { @@ -9157,14 +9702,21 @@ CSSParserImpl::ParseTextOverflow(nsCSSValue& aValue) */ bool CSSParserImpl::ParseFunctionInternals(const int32_t aVariantMask[], + int32_t aVariantMaskAll, uint16_t aMinElems, uint16_t aMaxElems, InfallibleTArray &aOutput) { + NS_ASSERTION((aVariantMask && !aVariantMaskAll) || + (!aVariantMask && aVariantMaskAll), + "only one of the two variant mask parameters can be set"); + for (uint16_t index = 0; index < aMaxElems; ++index) { nsCSSValue newValue; - if (!ParseVariant(newValue, aVariantMask[index], nullptr)) + int32_t m = aVariantMaskAll ? aVariantMaskAll : aVariantMask[index]; + if (!ParseVariant(newValue, m, nullptr)) { return false; + } aOutput.AppendElement(newValue); @@ -9192,7 +9744,8 @@ CSSParserImpl::ParseFunctionInternals(const int32_t aVariantMask[], * types for each element in the function. The zeroth element in the * array corresponds to the first function parameter, etc. The length * of this array _must_ be greater than or equal to aMaxElems or the - * behavior is undefined. + * behavior is undefined. If not null, aAllowTypesAll must be 0. + * @param aAllowedTypesAll If set, every element tested for these types * @param aMinElems Minimum number of elements to read. Reading fewer than * this many elements will result in the function failing. * @param aMaxElems Maximum number of elements to read. Reading more than @@ -9202,9 +9755,13 @@ CSSParserImpl::ParseFunctionInternals(const int32_t aVariantMask[], bool CSSParserImpl::ParseFunction(const nsString &aFunction, const int32_t aAllowedTypes[], + int32_t aAllowedTypesAll, uint16_t aMinElems, uint16_t aMaxElems, nsCSSValue &aValue) { + NS_ASSERTION((aAllowedTypes && !aAllowedTypesAll) || + (!aAllowedTypes && aAllowedTypesAll), + "only one of the two allowed type parameter can be set"); typedef InfallibleTArray::size_type arrlen_t; /* 2^16 - 2, so that if we have 2^16 - 2 transforms, we have 2^16 - 1 @@ -9222,9 +9779,10 @@ CSSParserImpl::ParseFunction(const nsString &aFunction, * it's out of bounds. */ InfallibleTArray foundValues; - if (!ParseFunctionInternals(aAllowedTypes, aMinElems, aMaxElems, - foundValues)) + if (!ParseFunctionInternals(aAllowedTypes, aAllowedTypesAll, aMinElems, + aMaxElems, foundValues)) { return false; + } /* Now, convert this array into an nsCSSValue::Array object. * We'll need N + 1 spots, one for the function name and the rest for the @@ -9499,7 +10057,8 @@ CSSParserImpl::ParseSingleTransform(bool aIsPrefixed, break; } - return ParseFunction(mToken.mIdent, variantMask, minElems, maxElems, aValue); + return ParseFunction(mToken.mIdent, variantMask, 0, minElems, + maxElems, aValue); } /* Parses a transform property list by continuously reading in properties diff --git a/layout/style/nsCSSPropAliasList.h b/layout/style/nsCSSPropAliasList.h index 2a7d91aaa68c..110456c42752 100644 --- a/layout/style/nsCSSPropAliasList.h +++ b/layout/style/nsCSSPropAliasList.h @@ -55,3 +55,5 @@ CSS_PROP_ALIAS(-moz-animation-iteration-count, animation_iteration_count, MozAni CSS_PROP_ALIAS(-moz-animation-name, animation_name, MozAnimationName, "layout.css.prefixes.animations") CSS_PROP_ALIAS(-moz-animation-play-state, animation_play_state, MozAnimationPlayState, "layout.css.prefixes.animations") CSS_PROP_ALIAS(-moz-animation-timing-function, animation_timing_function, MozAnimationTimingFunction, "layout.css.prefixes.animations") +CSS_PROP_ALIAS(font-feature-settings, font_feature_settings, FontFeatureSettings, "layout.css.font-features.enabled") +CSS_PROP_ALIAS(font-language-override, font_language_override, FontLanguageOverride, "layout.css.font-features.enabled") diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index 401bd7e59b6e..fc459326e4a0 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -1709,6 +1709,18 @@ CSS_PROP_FONT( nullptr, CSS_PROP_NO_OFFSET, eStyleAnimType_None) +CSS_PROP_FONT( + font-kerning, + font_kerning, + FontKerning, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "layout.css.font-features.enabled", + VARIANT_HK, + kFontKerningKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) CSS_PROP_FONT( -moz-font-language-override, font_language_override, @@ -1774,6 +1786,19 @@ CSS_PROP_FONT( kFontStyleKTable, offsetof(nsStyleFont, mFont.style), eStyleAnimType_EnumU8) + CSS_PROP_FONT( + font-synthesis, + font_synthesis, + FontSynthesis, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "layout.css.font-features.enabled", + 0, + kFontSynthesisKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) CSS_PROP_FONT( font-variant, font_variant, @@ -1786,6 +1811,82 @@ CSS_PROP_FONT( kFontVariantKTable, offsetof(nsStyleFont, mFont.variant), eStyleAnimType_EnumU8) +CSS_PROP_FONT( + font-variant-alternates, + font_variant_alternates, + FontVariantAlternates, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "layout.css.font-features.enabled", + VARIANT_HK, + kFontVariantAlternatesKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_FONT( + font-variant-caps, + font_variant_caps, + FontVariantCaps, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "layout.css.font-features.enabled", + VARIANT_HMK, + kFontVariantCapsKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_FONT( + font-variant-east-asian, + font_variant_east_asian, + FontVariantEastAsian, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "layout.css.font-features.enabled", + 0, + kFontVariantEastAsianKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_FONT( + font-variant-ligatures, + font_variant_ligatures, + FontVariantLigatures, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "layout.css.font-features.enabled", + 0, + kFontVariantLigaturesKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_FONT( + font-variant-numeric, + font_variant_numeric, + FontVariantNumeric, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "layout.css.font-features.enabled", + 0, + kFontVariantNumericKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) +CSS_PROP_FONT( + font-variant-position, + font_variant_position, + FontVariantPosition, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE | + CSS_PROPERTY_APPLIES_TO_PLACEHOLDER, + "layout.css.font-features.enabled", + VARIANT_HMK, + kFontVariantPositionKTable, + CSS_PROP_NO_OFFSET, + eStyleAnimType_None) CSS_PROP_FONT( font-weight, font_weight, diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index dfba342d2be0..0586c32ac95a 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -410,7 +410,17 @@ nsCSSFontDesc nsCSSProps::LookupFontDesc(const nsAString& aFontDesc) { NS_ABORT_IF_FALSE(gFontDescTable, "no lookup table, needs addref"); - return nsCSSFontDesc(gFontDescTable->Lookup(aFontDesc)); + nsCSSFontDesc which = nsCSSFontDesc(gFontDescTable->Lookup(aFontDesc)); + + // check for unprefixed font-feature-settings/font-language-override + if (which == eCSSFontDesc_UNKNOWN && + mozilla::Preferences::GetBool("layout.css.font-features.enabled")) { + nsAutoString prefixedProp; + prefixedProp.AppendLiteral("-moz-"); + prefixedProp.Append(aFontDesc); + which = nsCSSFontDesc(gFontDescTable->Lookup(prefixedProp)); + } + return which; } const nsAFlatCString& @@ -1013,6 +1023,13 @@ const int32_t nsCSSProps::kFontKTable[] = { eCSSKeyword_UNKNOWN,-1 }; +const int32_t nsCSSProps::kFontKerningKTable[] = { + eCSSKeyword_auto, NS_FONT_KERNING_AUTO, + eCSSKeyword_none, NS_FONT_KERNING_NONE, + eCSSKeyword_normal, NS_FONT_KERNING_NORMAL, + eCSSKeyword_UNKNOWN,-1 +}; + const int32_t nsCSSProps::kFontSizeKTable[] = { eCSSKeyword_xx_small, NS_STYLE_FONT_SIZE_XXSMALL, eCSSKeyword_x_small, NS_STYLE_FONT_SIZE_XSMALL, @@ -1046,12 +1063,87 @@ const int32_t nsCSSProps::kFontStyleKTable[] = { eCSSKeyword_UNKNOWN,-1 }; +const int32_t nsCSSProps::kFontSynthesisKTable[] = { + eCSSKeyword_weight, NS_FONT_SYNTHESIS_WEIGHT, + eCSSKeyword_style, NS_FONT_SYNTHESIS_STYLE, + eCSSKeyword_UNKNOWN,-1 +}; + + const int32_t nsCSSProps::kFontVariantKTable[] = { eCSSKeyword_normal, NS_STYLE_FONT_VARIANT_NORMAL, eCSSKeyword_small_caps, NS_STYLE_FONT_VARIANT_SMALL_CAPS, eCSSKeyword_UNKNOWN,-1 }; +const int32_t nsCSSProps::kFontVariantAlternatesKTable[] = { + eCSSKeyword_historical_forms, NS_FONT_VARIANT_ALTERNATES_HISTORICAL, + eCSSKeyword_UNKNOWN,-1 +}; + +const int32_t nsCSSProps::kFontVariantAlternatesFuncsKTable[] = { + eCSSKeyword_stylistic, NS_FONT_VARIANT_ALTERNATES_STYLISTIC, + eCSSKeyword_styleset, NS_FONT_VARIANT_ALTERNATES_STYLESET, + eCSSKeyword_character_variant, NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT, + eCSSKeyword_swash, NS_FONT_VARIANT_ALTERNATES_SWASH, + eCSSKeyword_ornaments, NS_FONT_VARIANT_ALTERNATES_ORNAMENTS, + eCSSKeyword_annotation, NS_FONT_VARIANT_ALTERNATES_ANNOTATION, + eCSSKeyword_UNKNOWN,-1 +}; + +const int32_t nsCSSProps::kFontVariantCapsKTable[] = { + eCSSKeyword_small_caps, NS_FONT_VARIANT_CAPS_SMALLCAPS, + eCSSKeyword_all_small_caps, NS_FONT_VARIANT_CAPS_ALLSMALL, + eCSSKeyword_petite_caps, NS_FONT_VARIANT_CAPS_PETITECAPS, + eCSSKeyword_all_petite_caps, NS_FONT_VARIANT_CAPS_ALLPETITE, + eCSSKeyword_titling_caps, NS_FONT_VARIANT_CAPS_TITLING, + eCSSKeyword_unicase, NS_FONT_VARIANT_CAPS_UNICASE, + eCSSKeyword_UNKNOWN,-1 +}; + +const int32_t nsCSSProps::kFontVariantEastAsianKTable[] = { + eCSSKeyword_jis78, NS_FONT_VARIANT_EAST_ASIAN_JIS78, + eCSSKeyword_jis83, NS_FONT_VARIANT_EAST_ASIAN_JIS83, + eCSSKeyword_jis90, NS_FONT_VARIANT_EAST_ASIAN_JIS90, + eCSSKeyword_jis04, NS_FONT_VARIANT_EAST_ASIAN_JIS04, + eCSSKeyword_simplified, NS_FONT_VARIANT_EAST_ASIAN_SIMPLIFIED, + eCSSKeyword_traditional, NS_FONT_VARIANT_EAST_ASIAN_TRADITIONAL, + eCSSKeyword_full_width, NS_FONT_VARIANT_EAST_ASIAN_FULL_WIDTH, + eCSSKeyword_proportional_width, NS_FONT_VARIANT_EAST_ASIAN_PROP_WIDTH, + eCSSKeyword_ruby, NS_FONT_VARIANT_EAST_ASIAN_RUBY, + eCSSKeyword_UNKNOWN,-1 +}; + +const int32_t nsCSSProps::kFontVariantLigaturesKTable[] = { + eCSSKeyword_common_ligatures, NS_FONT_VARIANT_LIGATURES_COMMON, + eCSSKeyword_no_common_ligatures, NS_FONT_VARIANT_LIGATURES_NO_COMMON, + eCSSKeyword_discretionary_ligatures, NS_FONT_VARIANT_LIGATURES_DISCRETIONARY, + eCSSKeyword_no_discretionary_ligatures, NS_FONT_VARIANT_LIGATURES_NO_DISCRETIONARY, + eCSSKeyword_historical_ligatures, NS_FONT_VARIANT_LIGATURES_HISTORICAL, + eCSSKeyword_no_historical_ligatures, NS_FONT_VARIANT_LIGATURES_NO_HISTORICAL, + eCSSKeyword_contextual, NS_FONT_VARIANT_LIGATURES_CONTEXTUAL, + eCSSKeyword_no_contextual, NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL, + eCSSKeyword_UNKNOWN,-1 +}; + +const int32_t nsCSSProps::kFontVariantNumericKTable[] = { + eCSSKeyword_lining_nums, NS_FONT_VARIANT_NUMERIC_LINING, + eCSSKeyword_oldstyle_nums, NS_FONT_VARIANT_NUMERIC_OLDSTYLE, + eCSSKeyword_proportional_nums, NS_FONT_VARIANT_NUMERIC_PROPORTIONAL, + eCSSKeyword_tabular_nums, NS_FONT_VARIANT_NUMERIC_TABULAR, + eCSSKeyword_diagonal_fractions, NS_FONT_VARIANT_NUMERIC_DIAGONAL_FRACTIONS, + eCSSKeyword_stacked_fractions, NS_FONT_VARIANT_NUMERIC_STACKED_FRACTIONS, + eCSSKeyword_slashed_zero, NS_FONT_VARIANT_NUMERIC_SLASHZERO, + eCSSKeyword_ordinal, NS_FONT_VARIANT_NUMERIC_ORDINAL, + eCSSKeyword_UNKNOWN,-1 +}; + +const int32_t nsCSSProps::kFontVariantPositionKTable[] = { + eCSSKeyword_super, NS_FONT_VARIANT_POSITION_SUPER, + eCSSKeyword_sub, NS_FONT_VARIANT_POSITION_SUB, + eCSSKeyword_UNKNOWN,-1 +}; + const int32_t nsCSSProps::kFontWeightKTable[] = { eCSSKeyword_normal, NS_STYLE_FONT_WEIGHT_NORMAL, eCSSKeyword_bold, NS_STYLE_FONT_WEIGHT_BOLD, @@ -2072,11 +2164,19 @@ static const nsCSSProperty gFontSubpropTable[] = { eCSSProperty_font_weight, eCSSProperty_font_size, eCSSProperty_line_height, - eCSSProperty_font_size_adjust, // XXX Added LDB. - eCSSProperty_font_stretch, // XXX Added LDB. + eCSSProperty_font_size_adjust, + eCSSProperty_font_stretch, eCSSProperty__x_system_font, eCSSProperty_font_feature_settings, eCSSProperty_font_language_override, + eCSSProperty_font_kerning, + eCSSProperty_font_synthesis, + eCSSProperty_font_variant_alternates, + eCSSProperty_font_variant_caps, + eCSSProperty_font_variant_east_asian, + eCSSProperty_font_variant_ligatures, + eCSSProperty_font_variant_numeric, + eCSSProperty_font_variant_position, eCSSProperty_UNKNOWN }; diff --git a/layout/style/nsCSSProps.h b/layout/style/nsCSSProps.h index c9840af09fc4..c3a7064b3d42 100644 --- a/layout/style/nsCSSProps.h +++ b/layout/style/nsCSSProps.h @@ -192,7 +192,7 @@ public: static nsCSSProperty OtherNameFor(nsCSSProperty aProperty); // Given a CSS Property and a Property Enum Value - // Return back a const nsString& representation of the + // Return back a const nsString& representation of the // value. Return back nullstr if no value is found static const nsAFlatCString& LookupPropertyValue(nsCSSProperty aProperty, int32_t aValue); @@ -402,10 +402,19 @@ public: static const int32_t kFloatKTable[]; static const int32_t kFloatEdgeKTable[]; static const int32_t kFontKTable[]; + static const int32_t kFontKerningKTable[]; static const int32_t kFontSizeKTable[]; static const int32_t kFontStretchKTable[]; static const int32_t kFontStyleKTable[]; + static const int32_t kFontSynthesisKTable[]; static const int32_t kFontVariantKTable[]; + static const int32_t kFontVariantAlternatesKTable[]; + static const int32_t kFontVariantAlternatesFuncsKTable[]; + static const int32_t kFontVariantCapsKTable[]; + static const int32_t kFontVariantEastAsianKTable[]; + static const int32_t kFontVariantLigaturesKTable[]; + static const int32_t kFontVariantNumericKTable[]; + static const int32_t kFontVariantPositionKTable[]; static const int32_t kFontWeightKTable[]; static const int32_t kIMEModeKTable[]; static const int32_t kLineHeightKTable[]; diff --git a/layout/style/nsCSSRuleProcessor.cpp b/layout/style/nsCSSRuleProcessor.cpp index df4e0d1b0e96..6836bb845669 100644 --- a/layout/style/nsCSSRuleProcessor.cpp +++ b/layout/style/nsCSSRuleProcessor.cpp @@ -649,7 +649,7 @@ void RuleHash::AppendRule(const RuleSelectorPair& aRuleInfo) } AppendRuleToTagTable(&mTagTable, selector->mLowercaseTag, ruleValue); RULE_HASH_STAT_INCREMENT(mTagSelectors); - if (selector->mCasedTag && + if (selector->mCasedTag && selector->mCasedTag != selector->mLowercaseTag) { AppendRuleToTagTable(&mTagTable, selector->mCasedTag, ruleValue); RULE_HASH_STAT_INCREMENT(mTagSelectors); @@ -978,6 +978,7 @@ struct RuleCascadeData { nsTArray mFontFaceRules; nsTArray mKeyframesRules; + nsTArray mFontFeatureValuesRules; nsTArray mPageRules; // Looks up or creates the appropriate list in |mAttributeSelectors|. @@ -1029,6 +1030,7 @@ RuleCascadeData::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const n += mFontFaceRules.SizeOfExcludingThis(aMallocSizeOf); n += mKeyframesRules.SizeOfExcludingThis(aMallocSizeOf); + n += mFontFeatureValuesRules.SizeOfExcludingThis(aMallocSizeOf); n += mPageRules.SizeOfExcludingThis(aMallocSizeOf); return n; @@ -1437,11 +1439,11 @@ static bool AttrMatchesValue(const nsAttrSelector* aAttrSelector, : static_cast(ciComparator); switch (aAttrSelector->mFunction) { - case NS_ATTR_FUNC_EQUALS: + case NS_ATTR_FUNC_EQUALS: return aValue.Equals(aAttrSelector->mValue, comparator); - case NS_ATTR_FUNC_INCLUDES: + case NS_ATTR_FUNC_INCLUDES: return ValueIncludes(aValue, aAttrSelector->mValue, comparator); - case NS_ATTR_FUNC_DASHMATCH: + case NS_ATTR_FUNC_DASHMATCH: return nsStyleUtil::DashMatchCompare(aValue, aAttrSelector->mValue, comparator); case NS_ATTR_FUNC_ENDSMATCH: return StringEndsWith(aValue, aAttrSelector->mValue, comparator); @@ -2749,6 +2751,21 @@ nsCSSRuleProcessor::AppendPageRules( return true; } +bool +nsCSSRuleProcessor::AppendFontFeatureValuesRules( + nsPresContext *aPresContext, + nsTArray& aArray) +{ + RuleCascadeData* cascade = GetRuleCascade(aPresContext); + + if (cascade) { + if (!aArray.AppendElements(cascade->mFontFeatureValuesRules)) + return false; + } + + return true; +} + nsresult nsCSSRuleProcessor::ClearRuleCascades() { @@ -3047,12 +3064,14 @@ struct CascadeEnumData { CascadeEnumData(nsPresContext* aPresContext, nsTArray& aFontFaceRules, nsTArray& aKeyframesRules, + nsTArray& aFontFeatureValuesRules, nsTArray& aPageRules, nsMediaQueryResultCacheKey& aKey, uint8_t aSheetType) : mPresContext(aPresContext), mFontFaceRules(aFontFaceRules), mKeyframesRules(aKeyframesRules), + mFontFeatureValuesRules(aFontFeatureValuesRules), mPageRules(aPageRules), mCacheKey(aKey), mSheetType(aSheetType) @@ -3076,6 +3095,7 @@ struct CascadeEnumData { nsPresContext* mPresContext; nsTArray& mFontFaceRules; nsTArray& mKeyframesRules; + nsTArray& mFontFeatureValuesRules; nsTArray& mPageRules; nsMediaQueryResultCacheKey& mCacheKey; PLArenaPool mArena; @@ -3093,7 +3113,9 @@ struct CascadeEnumData { * but kept in order per-weight, and * (2) add any @font-face rules, in order, into data->mFontFaceRules. * (3) add any @keyframes rules, in order, into data->mKeyframesRules. - * (4) add any @page rules, in order, into data->mPageRules. + * (4) add any @font-feature-value rules, in order, + * into data->mFontFeatureValuesRules. + * (5) add any @page rules, in order, into data->mPageRules. */ static bool CascadeRuleEnumFunc(css::Rule* aRule, void* aData) @@ -3146,6 +3168,13 @@ CascadeRuleEnumFunc(css::Rule* aRule, void* aData) return false; } } + else if (css::Rule::FONT_FEATURE_VALUES_RULE == type) { + nsCSSFontFeatureValuesRule *fontFeatureValuesRule = + static_cast(aRule); + if (!data->mFontFeatureValuesRules.AppendElement(fontFeatureValuesRule)) { + return false; + } + } else if (css::Rule::PAGE_RULE == type) { nsCSSPageRule* pageRule = static_cast(aRule); if (!data->mPageRules.AppendElement(pageRule)) { @@ -3252,6 +3281,7 @@ nsCSSRuleProcessor::RefreshRuleCascade(nsPresContext* aPresContext) if (newCascade) { CascadeEnumData data(aPresContext, newCascade->mFontFaceRules, newCascade->mKeyframesRules, + newCascade->mFontFeatureValuesRules, newCascade->mPageRules, newCascade->mCacheKey, mSheetType); diff --git a/layout/style/nsCSSRuleProcessor.h b/layout/style/nsCSSRuleProcessor.h index 94809df3a193..11f00abf8704 100644 --- a/layout/style/nsCSSRuleProcessor.h +++ b/layout/style/nsCSSRuleProcessor.h @@ -128,6 +128,9 @@ public: bool AppendPageRules(nsPresContext* aPresContext, nsTArray& aArray); + bool AppendFontFeatureValuesRules(nsPresContext* aPresContext, + nsTArray& aArray); + /** * Returns the scope element for the scoped style sheets this rule * processor is for. If this is not a rule processor for scoped style diff --git a/layout/style/nsCSSRules.cpp b/layout/style/nsCSSRules.cpp index a9d4d7094d37..c5efbc0af698 100644 --- a/layout/style/nsCSSRules.cpp +++ b/layout/style/nsCSSRules.cpp @@ -34,6 +34,7 @@ #include "nsDOMClassInfoID.h" #include "mozilla/dom/CSSStyleDeclarationBinding.h" #include "StyleRule.h" +#include "nsFont.h" using namespace mozilla; @@ -718,7 +719,7 @@ GroupRule::AppendRulesToCssText(nsAString& aCssText) } aCssText.AppendLiteral("}"); - + return NS_OK; } @@ -1910,6 +1911,255 @@ nsCSSFontFaceRule::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const } +// ----------------------------------- +// nsCSSFontFeatureValuesRule +// + +/* virtual */ already_AddRefed +nsCSSFontFeatureValuesRule::Clone() const +{ + nsRefPtr clone = new nsCSSFontFeatureValuesRule(*this); + return clone.forget(); +} + +NS_IMPL_ADDREF(nsCSSFontFeatureValuesRule) +NS_IMPL_RELEASE(nsCSSFontFeatureValuesRule) + +DOMCI_DATA(CSSFontFeatureValuesRule, nsCSSFontFeatureValuesRule) + +// QueryInterface implementation for nsCSSFontFeatureValuesRule +NS_INTERFACE_MAP_BEGIN(nsCSSFontFeatureValuesRule) + NS_INTERFACE_MAP_ENTRY(nsIStyleRule) + NS_INTERFACE_MAP_ENTRY(nsIDOMCSSFontFeatureValuesRule) + NS_INTERFACE_MAP_ENTRY(nsIDOMCSSRule) + NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStyleRule) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CSSFontFeatureValuesRule) +NS_INTERFACE_MAP_END + +IMPL_STYLE_RULE_INHERIT(nsCSSFontFeatureValuesRule, Rule) + +static void +FamilyListToString(const nsTArray& aFamilyList, nsAString& aOutStr) +{ + uint32_t i, n = aFamilyList.Length(); + + for (i = 0; i < n; i++) { + nsStyleUtil::AppendEscapedCSSString(aFamilyList[i], aOutStr); + if (i != n - 1) { + aOutStr.AppendLiteral(", "); + } + } +} + +static void +FeatureValuesToString( + const nsTArray& aFeatureValues, + nsAString& aOutStr) +{ + uint32_t i, n; + + // append values + n = aFeatureValues.Length(); + for (i = 0; i < n; i++) { + const gfxFontFeatureValueSet::FeatureValues& fv = aFeatureValues[i]; + + // @alternate + aOutStr.AppendLiteral(" @"); + nsAutoString functAlt; + nsStyleUtil::GetFunctionalAlternatesName(fv.alternate, functAlt); + aOutStr.Append(functAlt); + aOutStr.AppendLiteral(" {"); + + // for each ident-values tuple + uint32_t j, numValues = fv.valuelist.Length(); + for (j = 0; j < numValues; j++) { + aOutStr.AppendLiteral(" "); + const gfxFontFeatureValueSet::ValueList& vlist = fv.valuelist[j]; + nsStyleUtil::AppendEscapedCSSIdent(vlist.name, aOutStr); + aOutStr.AppendLiteral(":"); + + uint32_t k, numSelectors = vlist.featureSelectors.Length(); + for (k = 0; k < numSelectors; k++) { + aOutStr.AppendLiteral(" "); + aOutStr.AppendInt(vlist.featureSelectors[k]); + } + + aOutStr.AppendLiteral(";"); + } + aOutStr.AppendLiteral(" }\n"); + } +} + +static void +FontFeatureValuesRuleToString( + const nsTArray& aFamilyList, + const nsTArray& aFeatureValues, + nsAString& aOutStr) +{ + aOutStr.AssignLiteral("@font-feature-values "); + nsAutoString familyListStr, valueTextStr; + FamilyListToString(aFamilyList, familyListStr); + aOutStr.Append(familyListStr); + aOutStr.AppendLiteral(" {\n"); + FeatureValuesToString(aFeatureValues, valueTextStr); + aOutStr.Append(valueTextStr); + aOutStr.AppendLiteral("}"); +} + +#ifdef DEBUG +void +nsCSSFontFeatureValuesRule::List(FILE* out, int32_t aIndent) const +{ + nsAutoString text; + FontFeatureValuesRuleToString(mFamilyList, mFeatureValues, text); + NS_ConvertUTF16toUTF8 utf8(text); + + // replace newlines with newlines plus indent spaces + char* indent = new char[(aIndent + 1) * 2]; + int32_t i; + for (i = 1; i < (aIndent + 1) * 2 - 1; i++) { + indent[i] = 0x20; + } + indent[0] = 0xa; + indent[aIndent * 2 + 1] = 0; + utf8.ReplaceSubstring("\n", indent); + delete [] indent; + + for (i = aIndent; --i >= 0; ) fputs(" ", out); + fprintf(out, "%s\n", utf8.get()); +} +#endif + +/* virtual */ int32_t +nsCSSFontFeatureValuesRule::GetType() const +{ + return Rule::FONT_FEATURE_VALUES_RULE; +} + +NS_IMETHODIMP +nsCSSFontFeatureValuesRule::GetType(uint16_t* aType) +{ + *aType = nsIDOMCSSRule::FONT_FEATURE_VALUES_RULE; + return NS_OK; +} + +NS_IMETHODIMP +nsCSSFontFeatureValuesRule::GetCssText(nsAString& aCssText) +{ + FontFeatureValuesRuleToString(mFamilyList, mFeatureValues, aCssText); + return NS_OK; +} + +NS_IMETHODIMP +nsCSSFontFeatureValuesRule::SetCssText(const nsAString& aCssText) +{ + // FIXME: implement??? + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsCSSFontFeatureValuesRule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet) +{ + return Rule::GetParentStyleSheet(aSheet); +} + +NS_IMETHODIMP +nsCSSFontFeatureValuesRule::GetParentRule(nsIDOMCSSRule** aParentRule) +{ + return Rule::GetParentRule(aParentRule); +} + +NS_IMETHODIMP +nsCSSFontFeatureValuesRule::GetFontFamily(nsAString& aFontFamily) +{ + FamilyListToString(mFamilyList, aFontFamily); + return NS_OK; +} + +NS_IMETHODIMP +nsCSSFontFeatureValuesRule::SetFontFamily(const nsAString& aFontFamily) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP +nsCSSFontFeatureValuesRule::GetValueText(nsAString& aValueText) +{ + FeatureValuesToString(mFeatureValues, aValueText); + return NS_OK; +} + +NS_IMETHODIMP +nsCSSFontFeatureValuesRule::SetValueText(const nsAString& aValueText) +{ + return NS_ERROR_NOT_IMPLEMENTED; +} + +struct MakeFamilyArray { + MakeFamilyArray(nsTArray& aFamilyArray) + : familyArray(aFamilyArray), hasGeneric(false) + {} + + static bool + AddFamily(const nsString& aFamily, bool aGeneric, void* aData) + { + MakeFamilyArray *familyArr = reinterpret_cast (aData); + if (!aGeneric && !aFamily.IsEmpty()) { + familyArr->familyArray.AppendElement(aFamily); + } + if (aGeneric) { + familyArr->hasGeneric = true; + } + return true; + } + + nsTArray& familyArray; + bool hasGeneric; +}; + +void +nsCSSFontFeatureValuesRule::SetFamilyList(const nsAString& aFamilyList, + bool& aContainsGeneric) +{ + nsFont font(aFamilyList, 0, 0, 0, 0, 0, 0); + MakeFamilyArray families(mFamilyList); + font.EnumerateFamilies(MakeFamilyArray::AddFamily, (void*) &families); + aContainsGeneric = families.hasGeneric; +} + +void +nsCSSFontFeatureValuesRule::AddValueList(int32_t aVariantAlternate, + nsTArray& aValueList) +{ + uint32_t i, len = mFeatureValues.Length(); + bool foundAlternate = false; + + // add to an existing list for a given property value + for (i = 0; i < len; i++) { + gfxFontFeatureValueSet::FeatureValues& f = mFeatureValues.ElementAt(i); + + if (f.alternate == uint32_t(aVariantAlternate)) { + f.valuelist.AppendElements(aValueList); + foundAlternate = true; + break; + } + } + + // create a new list for a given property value + if (!foundAlternate) { + gfxFontFeatureValueSet::FeatureValues &f = *mFeatureValues.AppendElement(); + f.alternate = aVariantAlternate; + f.valuelist.AppendElements(aValueList); + } +} + +size_t +nsCSSFontFeatureValuesRule::SizeOfIncludingThis( + nsMallocSizeOfFun aMallocSizeOf) const +{ + return aMallocSizeOf(this); +} + // ------------------------------------------- // nsCSSKeyframeStyleDeclaration // diff --git a/layout/style/nsCSSRules.h b/layout/style/nsCSSRules.h index 79a08621db3d..b3e87294a085 100644 --- a/layout/style/nsCSSRules.h +++ b/layout/style/nsCSSRules.h @@ -15,6 +15,7 @@ #include "mozilla/Preferences.h" #include "nsIDOMCSSConditionRule.h" #include "nsIDOMCSSFontFaceRule.h" +#include "nsIDOMCSSFontFeatureValuesRule.h" #include "nsIDOMCSSGroupingRule.h" #include "nsIDOMCSSMediaRule.h" #include "nsIDOMCSSMozDocumentRule.h" @@ -31,6 +32,7 @@ #include "Declaration.h" #include "nsIDOMCSSPageRule.h" #include "StyleRule.h" +#include "gfxFontFeatures.h" class nsMediaList; @@ -212,7 +214,7 @@ protected: #include "nsCSSFontDescList.h" #undef CSS_FONT_DESC - static nsCSSValue nsCSSFontFaceStyleDecl::* const Fields[]; + static nsCSSValue nsCSSFontFaceStyleDecl::* const Fields[]; inline nsCSSFontFaceRule* ContainingRule(); inline const nsCSSFontFaceRule* ContainingRule() const; @@ -264,7 +266,7 @@ protected: nsCSSFontFaceStyleDecl mDecl; }; -// nsFontFaceRuleContainer - used for associating sheet type with +// nsFontFaceRuleContainer - used for associating sheet type with // specific @font-face rules struct nsFontFaceRuleContainer { nsRefPtr mRule; @@ -285,6 +287,61 @@ nsCSSFontFaceStyleDecl::ContainingRule() const (reinterpret_cast(this) - offsetof(nsCSSFontFaceRule, mDecl)); } +class nsCSSFontFeatureValuesRule MOZ_FINAL : + public mozilla::css::Rule, + public nsIDOMCSSFontFeatureValuesRule +{ +public: + nsCSSFontFeatureValuesRule() {} + + nsCSSFontFeatureValuesRule(const nsCSSFontFeatureValuesRule& aCopy) + // copy everything except our reference count + : mozilla::css::Rule(aCopy), + mFamilyList(aCopy.mFamilyList), + mFeatureValues(aCopy.mFeatureValues) {} + + NS_DECL_ISUPPORTS + + // nsIStyleRule methods +#ifdef DEBUG + virtual void List(FILE* out = stdout, int32_t aIndent = 0) const MOZ_OVERRIDE; +#endif + + // Rule methods + DECL_STYLE_RULE_INHERIT + + virtual int32_t GetType() const MOZ_OVERRIDE; + virtual already_AddRefed Clone() const MOZ_OVERRIDE; + + // nsIDOMCSSRule interface + NS_DECL_NSIDOMCSSRULE + + // nsIDOMCSSFontFaceRule interface + NS_DECL_NSIDOMCSSFONTFEATUREVALUESRULE + + const nsTArray& GetFamilyList() { return mFamilyList; } + void SetFamilyList(const nsAString& aFamilyList, bool& aContainsGeneric); + + void AddValueList(int32_t aVariantAlternate, + nsTArray& aValueList); + + const nsTArray& GetFeatureValues() + { + return mFeatureValues; + } + + virtual size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + + static bool PrefEnabled() + { + return mozilla::Preferences::GetBool("layout.css.font-features.enabled"); + } + +protected: + nsTArray mFamilyList; + nsTArray mFeatureValues; +}; + namespace mozilla { namespace css { diff --git a/layout/style/nsCSSValue.cpp b/layout/style/nsCSSValue.cpp index 5ed96f9b12ae..289549575183 100644 --- a/layout/style/nsCSSValue.cpp +++ b/layout/style/nsCSSValue.cpp @@ -806,8 +806,10 @@ nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult) const aResult.AppendInt(GetIntValue(), 10); } else if (eCSSUnit_Enumerated == unit) { - if (eCSSProperty_text_decoration_line == aProperty) { - int32_t intValue = GetIntValue(); + int32_t intValue = GetIntValue(); + switch(aProperty) { + + case eCSSProperty_text_decoration_line: if (NS_STYLE_TEXT_DECORATION_LINE_NONE == intValue) { AppendASCIItoUTF16(nsCSSProps::LookupPropertyValue(aProperty, intValue), aResult); @@ -821,9 +823,9 @@ nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult) const NS_STYLE_TEXT_DECORATION_LINE_PREF_ANCHORS, aResult); } - } - else if (eCSSProperty_marks == aProperty) { - int32_t intValue = GetIntValue(); + break; + + case eCSSProperty_marks: if (intValue == NS_STYLE_PAGE_MARKS_NONE) { AppendASCIItoUTF16(nsCSSProps::LookupPropertyValue(aProperty, intValue), aResult); @@ -833,17 +835,48 @@ nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult) const NS_STYLE_PAGE_MARKS_REGISTER, aResult); } - } - else if (eCSSProperty_paint_order == aProperty) { + break; + + case eCSSProperty_paint_order: MOZ_STATIC_ASSERT (NS_STYLE_PAINT_ORDER_BITWIDTH * NS_STYLE_PAINT_ORDER_LAST_VALUE <= 8, "SVGStyleStruct::mPaintOrder and the following cast not big enough"); nsStyleUtil::AppendPaintOrderValue(static_cast(GetIntValue()), aResult); - } - else { - const nsAFlatCString& name = nsCSSProps::LookupPropertyValue(aProperty, GetIntValue()); + break; + + case eCSSProperty_font_synthesis: + nsStyleUtil::AppendBitmaskCSSValue(aProperty, intValue, + NS_FONT_SYNTHESIS_WEIGHT, + NS_FONT_SYNTHESIS_STYLE, + aResult); + break; + + case eCSSProperty_font_variant_east_asian: + nsStyleUtil::AppendBitmaskCSSValue(aProperty, intValue, + NS_FONT_VARIANT_EAST_ASIAN_JIS78, + NS_FONT_VARIANT_EAST_ASIAN_RUBY, + aResult); + break; + + case eCSSProperty_font_variant_ligatures: + nsStyleUtil::AppendBitmaskCSSValue(aProperty, intValue, + NS_FONT_VARIANT_LIGATURES_COMMON, + NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL, + aResult); + break; + + case eCSSProperty_font_variant_numeric: + nsStyleUtil::AppendBitmaskCSSValue(aProperty, intValue, + NS_FONT_VARIANT_NUMERIC_LINING, + NS_FONT_VARIANT_NUMERIC_ORDINAL, + aResult); + break; + + default: + const nsAFlatCString& name = nsCSSProps::LookupPropertyValue(aProperty, intValue); AppendASCIItoUTF16(name, aResult); + break; } } else if (eCSSUnit_EnumColor == unit) { @@ -1057,7 +1090,27 @@ nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult) const aResult.AppendLiteral(")"); } else if (eCSSUnit_Pair == unit) { - GetPairValue().AppendToString(aProperty, aResult); + if (eCSSProperty_font_variant_alternates == aProperty) { + int32_t intValue = GetPairValue().mXValue.GetIntValue(); + nsAutoString out; + + // simple, enumerated values + nsStyleUtil::AppendBitmaskCSSValue(aProperty, + intValue & NS_FONT_VARIANT_ALTERNATES_ENUMERATED_MASK, + NS_FONT_VARIANT_ALTERNATES_HISTORICAL, + NS_FONT_VARIANT_ALTERNATES_HISTORICAL, + out); + + // functional values + const nsCSSValueList *list = GetPairValue().mYValue.GetListValue(); + nsAutoTArray altValues; + + nsStyleUtil::ComputeFunctionalAlternates(list, altValues); + nsStyleUtil::SerializeFunctionalAlternates(altValues, out); + aResult.Append(out); + } else { + GetPairValue().AppendToString(aProperty, aResult); + } } else if (eCSSUnit_Triplet == unit) { GetTripletValue().AppendToString(aProperty, aResult); } else if (eCSSUnit_Rect == unit) { diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index 743fe87a059a..3b9a615b6188 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -1332,6 +1332,16 @@ nsComputedDOMStyle::DoGetFontFeatureSettings() return val; } +CSSValue* +nsComputedDOMStyle::DoGetFontKerning() +{ + nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue(); + val->SetIdent( + nsCSSProps::ValueToKeywordEnum(StyleFont()->mFont.kerning, + nsCSSProps::kFontKerningKTable)); + return val; +} + CSSValue* nsComputedDOMStyle::DoGetFontLanguageOverride() { @@ -1348,6 +1358,157 @@ nsComputedDOMStyle::DoGetFontLanguageOverride() return val; } +CSSValue* +nsComputedDOMStyle::DoGetFontSynthesis() +{ + nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue(); + + int32_t intValue = StyleFont()->mFont.synthesis; + + if (0 == intValue) { + val->SetIdent(eCSSKeyword_none); + } else { + nsAutoString valueStr; + + nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_font_synthesis, + intValue, NS_FONT_SYNTHESIS_WEIGHT, + NS_FONT_SYNTHESIS_STYLE, valueStr); + val->SetString(valueStr); + } + + return val; +} + +CSSValue* +nsComputedDOMStyle::DoGetFontVariantAlternates() +{ + nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue(); + + int32_t intValue = StyleFont()->mFont.variantAlternates; + + if (0 == intValue) { + val->SetIdent(eCSSKeyword_normal); + return val; + } + + // first, include enumerated values + nsAutoString valueStr; + + nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_font_variant_alternates, + intValue & NS_FONT_VARIANT_ALTERNATES_ENUMERATED_MASK, + NS_FONT_VARIANT_ALTERNATES_HISTORICAL, + NS_FONT_VARIANT_ALTERNATES_HISTORICAL, valueStr); + + // next, include functional values if present + if (intValue & NS_FONT_VARIANT_ALTERNATES_FUNCTIONAL_MASK) { + nsStyleUtil::SerializeFunctionalAlternates(StyleFont()->mFont.alternateValues, + valueStr); + } + + val->SetString(valueStr); + return val; +} + + +CSSValue* +nsComputedDOMStyle::DoGetFontVariantCaps() +{ + nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue(); + + int32_t intValue = StyleFont()->mFont.variantCaps; + + if (0 == intValue) { + val->SetIdent(eCSSKeyword_normal); + } else { + val->SetIdent( + nsCSSProps::ValueToKeywordEnum(intValue, + nsCSSProps::kFontVariantCapsKTable)); + } + + return val; +} + +CSSValue* +nsComputedDOMStyle::DoGetFontVariantEastAsian() +{ + nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue(); + + int32_t intValue = StyleFont()->mFont.variantEastAsian; + + if (0 == intValue) { + val->SetIdent(eCSSKeyword_normal); + } else { + nsAutoString valueStr; + + nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_font_variant_east_asian, + intValue, NS_FONT_VARIANT_EAST_ASIAN_JIS78, + NS_FONT_VARIANT_EAST_ASIAN_RUBY, valueStr); + val->SetString(valueStr); + } + + return val; +} + +CSSValue* +nsComputedDOMStyle::DoGetFontVariantLigatures() +{ + nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue(); + + int32_t intValue = StyleFont()->mFont.variantLigatures; + + if (0 == intValue) { + val->SetIdent(eCSSKeyword_normal); + } else { + nsAutoString valueStr; + + nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_font_variant_ligatures, + intValue, NS_FONT_VARIANT_LIGATURES_COMMON, + NS_FONT_VARIANT_LIGATURES_NO_CONTEXTUAL, valueStr); + val->SetString(valueStr); + } + + return val; +} + +CSSValue* +nsComputedDOMStyle::DoGetFontVariantNumeric() +{ + nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue(); + + int32_t intValue = StyleFont()->mFont.variantNumeric; + + if (0 == intValue) { + val->SetIdent(eCSSKeyword_normal); + } else { + nsAutoString valueStr; + + nsStyleUtil::AppendBitmaskCSSValue(eCSSProperty_font_variant_numeric, + intValue, NS_FONT_VARIANT_NUMERIC_LINING, + NS_FONT_VARIANT_NUMERIC_ORDINAL, valueStr); + val->SetString(valueStr); + } + + return val; +} + +CSSValue* +nsComputedDOMStyle::DoGetFontVariantPosition() +{ + nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue(); + + int32_t intValue = StyleFont()->mFont.variantPosition; + + if (0 == intValue) { + val->SetIdent(eCSSKeyword_normal); + } else { + val->SetIdent( + nsCSSProps::ValueToKeywordEnum(intValue, + nsCSSProps::kFontVariantPositionKTable)); + } + + return val; +} + CSSValue* nsComputedDOMStyle::GetBackgroundList(uint8_t nsStyleBackground::Layer::* aMember, uint32_t nsStyleBackground::* aCount, @@ -4761,11 +4922,19 @@ nsComputedDOMStyle::GetQueryablePropertyMap(uint32_t* aLength) COMPUTED_STYLE_MAP_ENTRY(float, Float), //// COMPUTED_STYLE_MAP_ENTRY(font, Font), COMPUTED_STYLE_MAP_ENTRY(font_family, FontFamily), + COMPUTED_STYLE_MAP_ENTRY(font_kerning, FontKerning), COMPUTED_STYLE_MAP_ENTRY(font_size, FontSize), COMPUTED_STYLE_MAP_ENTRY(font_size_adjust, FontSizeAdjust), COMPUTED_STYLE_MAP_ENTRY(font_stretch, FontStretch), COMPUTED_STYLE_MAP_ENTRY(font_style, FontStyle), + COMPUTED_STYLE_MAP_ENTRY(font_synthesis, FontSynthesis), COMPUTED_STYLE_MAP_ENTRY(font_variant, FontVariant), + COMPUTED_STYLE_MAP_ENTRY(font_variant_alternates, FontVariantAlternates), + COMPUTED_STYLE_MAP_ENTRY(font_variant_caps, FontVariantCaps), + COMPUTED_STYLE_MAP_ENTRY(font_variant_east_asian, FontVariantEastAsian), + COMPUTED_STYLE_MAP_ENTRY(font_variant_ligatures, FontVariantLigatures), + COMPUTED_STYLE_MAP_ENTRY(font_variant_numeric, FontVariantNumeric), + COMPUTED_STYLE_MAP_ENTRY(font_variant_position, FontVariantPosition), COMPUTED_STYLE_MAP_ENTRY(font_weight, FontWeight), COMPUTED_STYLE_MAP_ENTRY_LAYOUT(height, Height), COMPUTED_STYLE_MAP_ENTRY(ime_mode, IMEMode), diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index 67e76ac37f4a..dbe40fb3765a 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -188,13 +188,21 @@ private: mozilla::dom::CSSValue* DoGetColor(); mozilla::dom::CSSValue* DoGetFontFamily(); mozilla::dom::CSSValue* DoGetFontFeatureSettings(); + mozilla::dom::CSSValue* DoGetFontKerning(); mozilla::dom::CSSValue* DoGetFontLanguageOverride(); mozilla::dom::CSSValue* DoGetFontSize(); mozilla::dom::CSSValue* DoGetFontSizeAdjust(); mozilla::dom::CSSValue* DoGetFontStretch(); mozilla::dom::CSSValue* DoGetFontStyle(); - mozilla::dom::CSSValue* DoGetFontWeight(); + mozilla::dom::CSSValue* DoGetFontSynthesis(); mozilla::dom::CSSValue* DoGetFontVariant(); + mozilla::dom::CSSValue* DoGetFontVariantAlternates(); + mozilla::dom::CSSValue* DoGetFontVariantCaps(); + mozilla::dom::CSSValue* DoGetFontVariantEastAsian(); + mozilla::dom::CSSValue* DoGetFontVariantLigatures(); + mozilla::dom::CSSValue* DoGetFontVariantNumeric(); + mozilla::dom::CSSValue* DoGetFontVariantPosition(); + mozilla::dom::CSSValue* DoGetFontWeight(); /* Background properties */ mozilla::dom::CSSValue* DoGetBackgroundAttachment(); diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index 49e5ba220cdf..ffd43eed298d 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -39,6 +39,7 @@ #include "CSSCalc.h" #include "nsPrintfCString.h" #include "nsRenderingContext.h" +#include "nsStyleUtil.h" #include "mozilla/LookAndFeel.h" @@ -3273,6 +3274,109 @@ nsRuleNode::SetFont(nsPresContext* aPresContext, nsStyleContext* aContext, aFont->mScriptLevel = 0; } + // font-kerning: none, enum, inherit, initial, -moz-system-font + SetDiscrete(*aRuleData->ValueForFontKerning(), + aFont->mFont.kerning, aCanStoreInRuleTree, + SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT, + aParentFont->mFont.kerning, + defaultVariableFont->kerning, + 0, 0, 0, systemFont.kerning); + + // font-synthesis: none, enum (bit field), inherit, initial, -moz-system-font + SetDiscrete(*aRuleData->ValueForFontSynthesis(), + aFont->mFont.synthesis, aCanStoreInRuleTree, + SETDSC_NONE | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT, + aParentFont->mFont.synthesis, + defaultVariableFont->synthesis, + 0, 0, 0, systemFont.synthesis); + + // font-variant-alternates: normal, enum (bit field) + functions, inherit, + // initial, -moz-system-font + const nsCSSValue* variantAlternatesValue = + aRuleData->ValueForFontVariantAlternates(); + int32_t variantAlternates = 0; + + switch (variantAlternatesValue->GetUnit()) { + case eCSSUnit_Inherit: + aFont->mFont.CopyAlternates(aParentFont->mFont); + aCanStoreInRuleTree = false; + break; + + case eCSSUnit_Initial: + case eCSSUnit_Normal: + aFont->mFont.variantAlternates = 0; + aFont->mFont.alternateValues.Clear(); + aFont->mFont.featureValueLookup = nullptr; + break; + + case eCSSUnit_Pair: + NS_ASSERTION(variantAlternatesValue->GetPairValue().mXValue.GetUnit() == + eCSSUnit_Enumerated, "strange unit for variantAlternates"); + variantAlternates = + variantAlternatesValue->GetPairValue().mXValue.GetIntValue(); + aFont->mFont.variantAlternates = variantAlternates; + + if (variantAlternates & NS_FONT_VARIANT_ALTERNATES_FUNCTIONAL_MASK) { + // fetch the feature lookup object from the styleset + aFont->mFont.featureValueLookup = + aPresContext->StyleSet()->GetFontFeatureValuesLookup(); + + NS_ASSERTION(variantAlternatesValue->GetPairValue().mYValue.GetUnit() == + eCSSUnit_List, "function list not a list value"); + nsStyleUtil::ComputeFunctionalAlternates( + variantAlternatesValue->GetPairValue().mYValue.GetListValue(), + aFont->mFont.alternateValues); + } + break; + + default: + break; + } + + // font-variant-caps: normal, enum, inherit, initial, -moz-system-font + SetDiscrete(*aRuleData->ValueForFontVariantCaps(), + aFont->mFont.variantCaps, aCanStoreInRuleTree, + SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT, + aParentFont->mFont.variantCaps, + defaultVariableFont->variantCaps, + 0, 0, 0, systemFont.variantCaps); + + // font-variant-east-asian: normal, enum (bit field), inherit, initial, + // -moz-system-font + SetDiscrete(*aRuleData->ValueForFontVariantEastAsian(), + aFont->mFont.variantEastAsian, aCanStoreInRuleTree, + SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT, + aParentFont->mFont.variantEastAsian, + defaultVariableFont->variantEastAsian, + 0, 0, 0, systemFont.variantEastAsian); + + // font-variant-ligatures: normal, enum (bit field), inherit, initial, + // -moz-system-font + SetDiscrete(*aRuleData->ValueForFontVariantLigatures(), + aFont->mFont.variantLigatures, aCanStoreInRuleTree, + SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT, + aParentFont->mFont.variantLigatures, + defaultVariableFont->variantLigatures, + 0, 0, 0, systemFont.variantLigatures); + + // font-variant-numeric: normal, enum (bit field), inherit, initial, + // -moz-system-font + SetDiscrete(*aRuleData->ValueForFontVariantNumeric(), + aFont->mFont.variantNumeric, aCanStoreInRuleTree, + SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT, + aParentFont->mFont.variantNumeric, + defaultVariableFont->variantNumeric, + 0, 0, 0, systemFont.variantNumeric); + + // font-variant-position: normal, enum, inherit, initial, + // -moz-system-font + SetDiscrete(*aRuleData->ValueForFontVariantPosition(), + aFont->mFont.variantPosition, aCanStoreInRuleTree, + SETDSC_NORMAL | SETDSC_ENUMERATED | SETDSC_SYSTEM_FONT, + aParentFont->mFont.variantPosition, + defaultVariableFont->variantPosition, + 0, 0, 0, systemFont.variantPosition); + // font-feature-settings const nsCSSValue* featureSettingsValue = aRuleData->ValueForFontFeatureSettings(); diff --git a/layout/style/nsStyleSet.cpp b/layout/style/nsStyleSet.cpp index 089fcabbea50..7105db27e632 100644 --- a/layout/style/nsStyleSet.cpp +++ b/layout/style/nsStyleSet.cpp @@ -110,6 +110,7 @@ nsStyleSet::nsStyleSet() mInShutdown(false), mAuthorStyleDisabled(false), mInReconstruct(false), + mInitFontFeatureValuesLookup(true), mDirty(0), mUnusedRuleNodeCount(0) { @@ -1104,7 +1105,7 @@ nsStyleSet::WalkRuleProcessors(nsIStyleRuleProcessor::EnumFunc aFunc, if (mRuleProcessors[ePresHintSheet]) (*aFunc)(mRuleProcessors[ePresHintSheet], aData); - + bool cutOffInheritance = false; if (mBindingManager) { // We can supply additional document-level sheets that should be walked. @@ -1540,6 +1541,59 @@ nsStyleSet::AppendKeyframesRules(nsPresContext* aPresContext, return true; } +bool +nsStyleSet::AppendFontFeatureValuesRules(nsPresContext* aPresContext, + nsTArray& aArray) +{ + NS_ENSURE_FALSE(mInShutdown, false); + + for (uint32_t i = 0; i < NS_ARRAY_LENGTH(gCSSSheetTypes); ++i) { + nsCSSRuleProcessor *ruleProc = static_cast + (mRuleProcessors[gCSSSheetTypes[i]].get()); + if (ruleProc && + !ruleProc->AppendFontFeatureValuesRules(aPresContext, aArray)) + { + return false; + } + } + return true; +} + +already_AddRefed +nsStyleSet::GetFontFeatureValuesLookup() +{ + if (mInitFontFeatureValuesLookup) { + mInitFontFeatureValuesLookup = false; + + nsTArray rules; + AppendFontFeatureValuesRules(PresContext(), rules); + + mFontFeatureValuesLookup = new gfxFontFeatureValueSet(); + + uint32_t i, numRules = rules.Length(); + for (i = 0; i < numRules; i++) { + nsCSSFontFeatureValuesRule *rule = rules[i]; + + const nsTArray& familyList = rule->GetFamilyList(); + const nsTArray& + featureValues = rule->GetFeatureValues(); + + // for each family + uint32_t f, numFam; + + numFam = familyList.Length(); + for (f = 0; f < numFam; f++) { + const nsString& family = familyList.ElementAt(f); + nsAutoString silly(family); + mFontFeatureValuesLookup->AddFontFeatureValues(silly, featureValues); + } + } + } + + nsRefPtr lookup = mFontFeatureValuesLookup; + return lookup.forget(); +} + bool nsStyleSet::AppendPageRules(nsPresContext* aPresContext, nsTArray& aArray) @@ -1812,7 +1866,7 @@ struct MOZ_STACK_CLASS AttributeData : public AttributeRuleProcessorData { mHint(nsRestyleHint(0)) {} nsRestyleHint mHint; -}; +}; static bool SheetHasAttributeStyle(nsIStyleRuleProcessor* aProcessor, void *aData) diff --git a/layout/style/nsStyleSet.h b/layout/style/nsStyleSet.h index 8644289a48a3..1a0ae8f082ce 100644 --- a/layout/style/nsStyleSet.h +++ b/layout/style/nsStyleSet.h @@ -23,11 +23,12 @@ #include "nsAutoPtr.h" #include "nsIStyleRule.h" #include "nsCSSPseudoElements.h" -#include "mozilla/Attributes.h" +#include "gfxFontFeatures.h" class nsIURI; class nsCSSFontFaceRule; class nsCSSKeyframesRule; +class nsCSSFontFeatureValuesRule; class nsCSSPageRule; class nsRuleWalker; struct ElementDependentRuleProcessorData; @@ -137,7 +138,7 @@ class nsStyleSet nsCSSPseudoElements::Type aType, nsStyleContext* aParentContext, TreeMatchContext& aTreeMatchContext); - + // Get a style context for an anonymous box. aPseudoTag is the // pseudo-tag to use and must be non-null. already_AddRefed @@ -164,6 +165,14 @@ class nsStyleSet bool AppendKeyframesRules(nsPresContext* aPresContext, nsTArray& aArray); + // Fetch object for looking up font feature values + already_AddRefed GetFontFeatureValuesLookup(); + + // Append all the currently-active font feature values rules to aArray. + // Return true for success and false for failure. + bool AppendFontFeatureValuesRules(nsPresContext* aPresContext, + nsTArray& aArray); + // Append all the currently-active page rules to aArray. Return // true for success and false for failure. bool AppendPageRules(nsPresContext* aPresContext, @@ -406,6 +415,7 @@ class nsStyleSet unsigned mInShutdown : 1; unsigned mAuthorStyleDisabled: 1; unsigned mInReconstruct : 1; + unsigned mInitFontFeatureValuesLookup : 1; unsigned mDirty : 9; // one dirty bit is used per sheet type uint32_t mUnusedRuleNodeCount; // used to batch rule node GC @@ -423,6 +433,9 @@ class nsStyleSet // BeginReconstruct and EndReconstruct, but in case of bugs that cause // style contexts to exist too long, may last longer. nsTArray mOldRuleTrees; + + // whether font feature values lookup object needs initialization + nsRefPtr mFontFeatureValuesLookup; }; #ifdef _IMPL_NS_LAYOUT diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 047d57aef423..e4b50db6b6e9 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -192,6 +192,16 @@ nsChangeHint nsStyleFont::CalcFontDifference(const nsFont& aFont1, const nsFont& (aFont1.weight == aFont2.weight) && (aFont1.stretch == aFont2.stretch) && (aFont1.name == aFont2.name) && + (aFont1.kerning == aFont2.kerning) && + (aFont1.synthesis == aFont2.synthesis) && + (aFont1.variantAlternates == aFont2.variantAlternates) && + (aFont1.alternateValues == aFont2.alternateValues) && + (aFont1.featureValueLookup == aFont2.featureValueLookup) && + (aFont1.variantCaps == aFont2.variantCaps) && + (aFont1.variantEastAsian == aFont2.variantEastAsian) && + (aFont1.variantLigatures == aFont2.variantLigatures) && + (aFont1.variantNumeric == aFont2.variantNumeric) && + (aFont1.variantPosition == aFont2.variantPosition) && (aFont1.fontFeatureSettings == aFont2.fontFeatureSettings) && (aFont1.languageOverride == aFont2.languageOverride)) { if ((aFont1.decorations == aFont2.decorations)) { diff --git a/layout/style/nsStyleUtil.cpp b/layout/style/nsStyleUtil.cpp index 44223181de43..b20148e377f4 100644 --- a/layout/style/nsStyleUtil.cpp +++ b/layout/style/nsStyleUtil.cpp @@ -270,6 +270,118 @@ nsStyleUtil::AppendFontFeatureSettings(const nsCSSValue& aSrc, AppendFontFeatureSettings(featureSettings, aResult); } +/* static */ void +nsStyleUtil::GetFunctionalAlternatesName(int32_t aFeature, + nsAString& aFeatureName) +{ + aFeatureName.Truncate(); + nsCSSKeyword key = + nsCSSProps::ValueToKeywordEnum(aFeature, + nsCSSProps::kFontVariantAlternatesFuncsKTable); + + NS_ASSERTION(key != eCSSKeyword_UNKNOWN, "bad alternate feature type"); + AppendUTF8toUTF16(nsCSSKeywords::GetStringValue(key), aFeatureName); +} + +/* static */ void +nsStyleUtil::SerializeFunctionalAlternates( + const nsTArray& aAlternates, + nsAString& aResult) +{ + nsAutoString funcName, funcParams; + uint32_t numValues = aAlternates.Length(); + + uint32_t feature = 0; + for (uint32_t i = 0; i < numValues; i++) { + const gfxAlternateValue& v = aAlternates.ElementAt(i); + if (feature != v.alternate) { + feature = v.alternate; + if (!funcName.IsEmpty() && !funcParams.IsEmpty()) { + if (!aResult.IsEmpty()) { + aResult.Append(PRUnichar(' ')); + } + + // append the previous functional value + aResult.Append(funcName); + aResult.Append(PRUnichar('(')); + aResult.Append(funcParams); + aResult.Append(PRUnichar(')')); + } + + // function name + GetFunctionalAlternatesName(v.alternate, funcName); + NS_ASSERTION(!funcName.IsEmpty(), "unknown property value name"); + + // function params + AppendEscapedCSSIdent(v.value, funcParams); + } else { + if (!funcParams.IsEmpty()) { + funcParams.Append(NS_LITERAL_STRING(", ")); + } + AppendEscapedCSSIdent(v.value, funcParams); + } + } + + // append the previous functional value + if (!funcName.IsEmpty() && !funcParams.IsEmpty()) { + if (!aResult.IsEmpty()) { + aResult.Append(PRUnichar(' ')); + } + + aResult.Append(funcName); + aResult.Append(PRUnichar('(')); + aResult.Append(funcParams); + aResult.Append(PRUnichar(')')); + } +} + +/* static */ void +nsStyleUtil::ComputeFunctionalAlternates(const nsCSSValueList* aList, + nsTArray& aAlternateValues) +{ + gfxAlternateValue v; + + aAlternateValues.Clear(); + for (const nsCSSValueList* curr = aList; curr != nullptr; curr = curr->mNext) { + // list contains function units + if (curr->mValue.GetUnit() != eCSSUnit_Function) { + continue; + } + + // element 0 is the propval in ident form + const nsCSSValue::Array *func = curr->mValue.GetArrayValue(); + + // lookup propval + nsAutoString keywordStr; + func->Item(0).GetStringValue(keywordStr); + nsCSSKeyword key = nsCSSKeywords::LookupKeyword(keywordStr); + NS_ASSERTION(key != eCSSKeyword_UNKNOWN, "unknown alternate property value"); + + int32_t alternate; + if (key == eCSSKeyword_UNKNOWN || + !nsCSSProps::FindKeyword(key, + nsCSSProps::kFontVariantAlternatesFuncsKTable, + alternate)) { + NS_NOTREACHED("keyword not a font-variant-alternates value"); + } + v.alternate = alternate; + + // other elements are the idents associated with the propval + // append one alternate value for each one + uint32_t numElems = func->Count(); + for (uint32_t i = 1; i < numElems; i++) { + const nsCSSValue& value = func->Item(i); + NS_ASSERTION(value.GetUnit() == eCSSUnit_Ident, + "weird unit found in variant alternate"); + if (value.GetUnit() != eCSSUnit_Ident) { + continue; + } + value.GetStringValue(v.value); + aAlternateValues.AppendElement(v); + } + } +} + /* static */ float nsStyleUtil::ColorComponentToFloat(uint8_t aAlpha) { diff --git a/layout/style/nsStyleUtil.h b/layout/style/nsStyleUtil.h index c44628cb2ee0..99bad2def69c 100644 --- a/layout/style/nsStyleUtil.h +++ b/layout/style/nsStyleUtil.h @@ -7,6 +7,7 @@ #include "nsCoord.h" #include "nsCSSProperty.h" +#include "gfxFontFeatures.h" #include "nsIPrincipal.h" #include "nsSubstring.h" @@ -14,6 +15,7 @@ class nsCSSValue; class nsStringComparator; class nsIContent; struct gfxFontFeature; +class nsCSSValueList; template class nsTArray; // Style utility functions @@ -51,6 +53,20 @@ public: static void AppendFontFeatureSettings(const nsCSSValue& src, nsAString& aResult); + // convert bitmask value to keyword name for a functional alternate + static void GetFunctionalAlternatesName(int32_t aFeature, + nsAString& aFeatureName); + + // Append functional font-variant-alternates values to string + static void + SerializeFunctionalAlternates(const nsTArray& aAlternates, + nsAString& aResult); + + // List of functional font-variant-alternates values to feature/value pairs + static void + ComputeFunctionalAlternates(const nsCSSValueList* aList, + nsTArray& aAlternateValues); + /* * Convert an author-provided floating point number to an integer (0 * ... 255) appropriate for use in the alpha component of a color. diff --git a/layout/style/test/Makefile.in b/layout/style/test/Makefile.in index b5f76ee5712f..e760cab04745 100644 --- a/layout/style/test/Makefile.in +++ b/layout/style/test/Makefile.in @@ -101,6 +101,7 @@ MOCHITEST_FILES = test_acid3_test46.html \ test_dont_use_document_colors.html \ test_font_face_parser.html \ test_font_family_parsing.html \ + test_font_feature_values_parsing.html \ test_garbage_at_end_of_declarations.html \ test_group_insertRule.html \ test_html_attribute_computed_values.html \ diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 94bcd1c4ca39..684cf284801a 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -2491,7 +2491,7 @@ var gCSSProperties = { type: CSS_TYPE_LONGHAND, initial_values: [ "normal" ], other_values: [ "small-caps" ], - invalid_values: [] + invalid_values: [ "small-caps normal" ] }, "font-weight": { domProp: "fontWeight", @@ -4257,6 +4257,129 @@ if (SpecialPowers.getBoolPref("layout.css.flexbox.enabled")) { gCSSProperties["display"].other_values.push("inline-flex"); } +if (SpecialPowers.getBoolPref("layout.css.font-features.enabled")) { + var fontFeatureProperties = { + "font-kerning": { + domProp: "fontKerning", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "auto" ], + other_values: [ "normal", "none" ], + invalid_values: [ "on" ] + }, + "font-variant-alternates": { + domProp: "fontVariantAlternates", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "historical-forms", + "styleset(alt-a, alt-b)", "character-variant(a, b, c)", "annotation(circled)", + "swash(squishy)", "styleset(complex\\ blob, a)", "annotation(\\62 lah)" ], + invalid_values: [ "historical-forms normal", "historical-forms historical-forms", + "swash", "swash(3)", "annotation(a, b)", "ornaments(a,b)", + "styleset(1234blah)", "annotation(a), annotation(b)", "annotation(a) normal" ] + }, + "font-variant-caps": { + domProp: "fontVariantCaps", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "small-caps", "all-small-caps", "petite-caps", "all-petite-caps", "titling-caps", "unicase" ], + invalid_values: [ "normal small-caps", "petite-caps normal", "unicase unicase" ] + }, + "font-variant-east-asian": { + domProp: "fontVariantEastAsian", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "jis78", "jis83", "jis90", "jis04", "simplified", "traditional", "full-width", "proportional-width", "ruby", + "jis78 full-width", "jis78 full-width ruby", "simplified proportional-width", "ruby simplified" ], + invalid_values: [ "jis78 normal", "jis90 jis04", "simplified traditional", "full-width proportional-width", + "ruby simplified ruby", "jis78 ruby simplified" ] + }, + "font-variant-ligatures": { + domProp: "fontVariantLigatures", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "common-ligatures", "no-common-ligatures", "discretionary-ligatures", "no-discretionary-ligatures", + "historical-ligatures", "no-historical-ligatures", "contextual", "no-contextual", + "common-ligatures no-discretionary-ligatures", "contextual no-discretionary-ligatures", + "historical-ligatures no-common-ligatures", "no-historical-ligatures discretionary-ligatures", + "common-ligatures no-discretionary-ligatures historical-ligatures no-contextual" ], + invalid_values: [ "common-ligatures normal", "common-ligatures no-common-ligatures", "common-ligatures common-ligatures", + "no-historical-ligatures historical-ligatures", "no-discretionary-ligatures discretionary-ligatures", + "no-contextual contextual", "common-ligatures no-discretionary-ligatures no-common-ligatures" ] + }, + "font-variant-numeric": { + domProp: "fontVariantNumeric", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "lining-nums", "oldstyle-nums", "proportional-nums", "tabular-nums", "diagonal-fractions", + "stacked-fractions", "slashed-zero", "ordinal", "lining-nums diagonal-fractions", + "tabular-nums stacked-fractions", "tabular-nums slashed-zero stacked-fractions", + "proportional-nums slashed-zero diagonal-fractions oldstyle-nums ordinal" ], + invalid_values: [ "lining-nums normal", "lining-nums oldstyle-nums", "lining-nums normal slashed-zero ordinal", + "proportional-nums tabular-nums", "diagonal-fractions stacked-fractions", "slashed-zero diagonal-fractions slashed-zero", + "lining-nums slashed-zero diagonal-fractions oldstyle-nums", "diagonal-fractions diagonal-fractions" ] + }, + "font-variant-position": { + domProp: "fontVariantPosition", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "normal" ], + other_values: [ "super", "sub" ], + invalid_values: [ "normal sub", "super sub" ] + }, + "font-synthesis": { + domProp: "fontSynthesis", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ "weight style" ], + other_values: [ "none", "weight", "style" ], + invalid_values: [ "weight none", "style none", "none style", "weight 10px", "weight weight", "style style" ] + }, + // aliases for prefixed properties + "font-feature-settings": { + domProp: "fontFeatureSettings", + inherited: true, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "-moz-font-feature-settings", + subproperties: [ "-moz-font-feature-settings" ], + initial_values: [ "normal" ], + other_values: [ + "'liga' on", "'liga'", "\"liga\" 1", "'liga', 'clig' 1", + "\"liga\" off", "\"liga\" 0", '"cv01" 3, "cv02" 4', + '"cswh", "smcp" off, "salt" 4', '"cswh" 1, "smcp" off, "salt" 4', + '"cswh" 0, \'blah\', "liga", "smcp" off, "salt" 4', + '"liga" ,"smcp" 0 , "blah"' + ], + invalid_values: [ + 'liga', 'liga 1', 'liga normal', '"liga" normal', 'normal liga', + 'normal "liga"', 'normal, "liga"', '"liga=1"', "'foobar' on", + '"blahblah" 0', '"liga" 3.14', '"liga" 1 3.14', '"liga" 1 normal', + '"liga" 1 off', '"liga" on off', '"liga" , 0 "smcp"', '"liga" "smcp"' + ] + }, + "font-language-override": { + domProp: "fontLanguageOverride", + inherited: true, + type: CSS_TYPE_SHORTHAND_AND_LONGHAND, + alias_for: "-moz-font-language-override", + subproperties: [ "-moz-font-language-override" ], + initial_values: [ "normal" ], + other_values: [ "'ENG'", "'TRK'", "\"TRK\"", "'N\\'Ko'" ], + invalid_values: [ "TRK", "ja" ] + } + }; + for (var prop in fontFeatureProperties) { + gCSSProperties[prop] = fontFeatureProperties[prop]; + } + var fontAdditions = [ "font-kerning", "font-synthesis", "font-variant-alternates", "font-variant-caps", "font-variant-east-asian", "font-variant-ligatures", "font-variant-numeric", "font-variant-position" ]; + gCSSProperties["font"].subproperties = gCSSProperties["font"].subproperties.concat(fontAdditions); +} + if (SpecialPowers.getBoolPref("layout.css.masking.enabled")) { gCSSProperties["mask-type"] = { domProp: "maskType", diff --git a/layout/style/test/test_bug377947.html b/layout/style/test/test_bug377947.html index a1d81e501129..2bcea156fa69 100644 --- a/layout/style/test/test_bug377947.html +++ b/layout/style/test/test_bug377947.html @@ -61,6 +61,21 @@ var all_but_one = { "-moz-font-feature-settings": "normal", // has to be default value "-moz-font-language-override": "normal" // has to be default value }; +if (SpecialPowers.getBoolPref("layout.css.font-features.enabled")) { + var featureDefs = { + "font-kerning": "auto", // has to be default value + "font-synthesis": "weight style", // has to be default value + "font-variant-alternates": "normal", // has to be default value + "font-variant-caps": "normal", // has to be default value + "font-variant-east-asian": "normal", // has to be default value + "font-variant-ligatures": "normal", // has to be default value + "font-variant-numeric": "normal", // has to be default value + "font-variant-position": "normal" // has to be default value + }; + for (var prop in featureDefs) { + all_but_one[prop] = featureDefs[prop]; + } +} for (var prop in all_but_one) { s.setProperty(prop, all_but_one[prop], ""); } diff --git a/layout/style/test/test_system_font_serialization.html b/layout/style/test/test_system_font_serialization.html index 2f8f87e4fc24..cd29b9a80f66 100644 --- a/layout/style/test/test_system_font_serialization.html +++ b/layout/style/test/test_system_font_serialization.html @@ -47,7 +47,13 @@ is(e.style.cssText, "font: menu; font-weight: -moz-use-system-font ! important;" is(e.style.font, "", "font getter returns nothing"); e.setAttribute("style", "font: inherit; font-family: Helvetica;"); -is(e.style.cssText, "font-style: inherit; font-variant: inherit; font-weight: inherit; font-size: inherit; line-height: inherit; font-size-adjust: inherit; font-stretch: inherit; -moz-font-feature-settings: inherit; -moz-font-language-override: inherit; font-family: Helvetica;", "don't serialize system font for font:inherit"); + +var cssTextStr = "font-style: inherit; font-variant: inherit; font-weight: inherit; font-size: inherit; line-height: inherit; font-size-adjust: inherit; font-stretch: inherit; -moz-font-feature-settings: inherit; -moz-font-language-override: inherit;"; +if (SpecialPowers.getBoolPref("layout.css.font-features.enabled")) { + cssTextStr += " font-kerning: inherit; font-synthesis: inherit; font-variant-alternates: inherit; font-variant-caps: inherit; font-variant-east-asian: inherit; font-variant-ligatures: inherit; font-variant-numeric: inherit; font-variant-position: inherit;" +} + +is(e.style.cssText, cssTextStr + " font-family: Helvetica;", "don't serialize system font for font:inherit"); is(e.style.font, "", "font getter returns nothing"); diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index a3304da39b92..cb53989f3ce0 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -1739,6 +1739,19 @@ pref("layout.css.supports-rule.enabled", true); // Is support for CSS Flexbox enabled? pref("layout.css.flexbox.enabled", true); +// Is support for CSS3 Fonts features enabled? +// (includes font-variant-*, font-kerning, font-synthesis +// and the @font-feature-values rule) +// Note: with this enabled, font-feature-settings is aliased +// to -moz-font-feature-settings. When unprefixing, this should +// be reversed, -moz-font-feature-settings should alias to +// font-feature-settings. +#ifdef RELEASE_BUILD +pref("layout.css.font-features.enabled", false); +#else +pref("layout.css.font-features.enabled", true); +#endif + // Are sets of prefixed properties supported? pref("layout.css.prefixes.border-image", true); pref("layout.css.prefixes.transforms", true);