зеркало из https://github.com/mozilla/gecko-dev.git
Bug 549861. Implement support for @font-feature-values rule. r=dbaron
This commit is contained in:
Родитель
6f990b3fc6
Коммит
5b570d8745
|
@ -913,6 +913,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,
|
||||
|
@ -2304,6 +2306,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)
|
||||
|
|
|
@ -237,6 +237,8 @@ DOMCI_CLASS(AsyncScrollEventDetail)
|
|||
|
||||
DOMCI_CLASS(LockedFile)
|
||||
|
||||
DOMCI_CLASS(CSSFontFeatureValuesRule)
|
||||
|
||||
#ifdef MOZ_TIME_MANAGER
|
||||
DOMCI_CLASS(MozTimeManager)
|
||||
#endif
|
||||
|
|
|
@ -69,6 +69,7 @@ interface nsIDOMCSSPrimitiveValue;
|
|||
interface nsIDOMCSSRule;
|
||||
interface nsIDOMCSSRuleList;
|
||||
interface nsIDOMMozCSSKeyframeRule;
|
||||
interface nsIDOMCSSFontFeatureValuesRule;
|
||||
interface nsIDOMCSSStyleSheet;
|
||||
interface nsIDOMCSSStyleDeclaration;
|
||||
interface nsIDOMCounter;
|
||||
|
|
|
@ -8,6 +8,7 @@ XPIDL_SOURCES += [
|
|||
'nsIDOMCSSCharsetRule.idl',
|
||||
'nsIDOMCSSConditionRule.idl',
|
||||
'nsIDOMCSSFontFaceRule.idl',
|
||||
'nsIDOMCSSFontFeatureValuesRule.idl',
|
||||
'nsIDOMCSSGroupingRule.idl',
|
||||
'nsIDOMCSSImportRule.idl',
|
||||
'nsIDOMCSSMediaRule.idl',
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is mozilla.org code.
|
||||
*
|
||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2004
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s):
|
||||
* John Daggett <jdaggett@mozilla.com> (original author)
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
#include "nsIDOMCSSRule.idl"
|
||||
|
||||
[scriptable, uuid(f4cb1776-389d-4f52-a4d8-68bea5bd00c1)]
|
||||
interface nsIDOMCSSFontFeatureValuesRule : nsIDOMCSSRule
|
||||
{
|
||||
attribute DOMString fontFamily;
|
||||
// raises(DOMException) on setting
|
||||
|
||||
attribute DOMString valueText;
|
||||
// raises(DOMException) on setting
|
||||
};
|
|
@ -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;
|
||||
|
|
|
@ -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=')'
|
||||
|
|
|
@ -492,6 +492,7 @@ var interfaceNamesInGlobalScope =
|
|||
"CloseEvent",
|
||||
"IDBCursorWithValue",
|
||||
"CSSFontFaceRule",
|
||||
"CSSFontFeatureValuesRule",
|
||||
"XMLHttpRequestEventTarget",
|
||||
"CompositionEvent",
|
||||
"HTMLOutputElement",
|
||||
|
|
|
@ -35,7 +35,7 @@ nsFont::nsFont(const char* aName, uint8_t aStyle, uint8_t aVariant,
|
|||
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)
|
||||
: name(aName)
|
||||
|
|
|
@ -113,7 +113,7 @@ struct NS_GFX nsFont {
|
|||
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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
@ -1635,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;
|
||||
|
@ -2360,6 +2368,233 @@ CSSParserImpl::ParseFontDescriptor(nsCSSFontFaceRule* aRule)
|
|||
return true;
|
||||
}
|
||||
|
||||
// @font-feature-values <font-family># {
|
||||
// @<feature-type> {
|
||||
// <feature-ident> : <feature-index>+;
|
||||
// <feature-ident> : <feature-index>+;
|
||||
// ...
|
||||
// }
|
||||
// ...
|
||||
// }
|
||||
|
||||
bool
|
||||
CSSParserImpl::ParseFontFeatureValuesRule(RuleAppendFunc aAppendFunc,
|
||||
void* aData)
|
||||
{
|
||||
nsRefPtr<nsCSSFontFeatureValuesRule>
|
||||
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
|
||||
// @<feature-type> { [ <feature-ident> : <feature-index>+ ; ]* }
|
||||
// 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<gfxFontFeatureValueSet::ValueList, 5> 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<uint32_t,4> 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)
|
||||
|
|
|
@ -978,6 +978,7 @@ struct RuleCascadeData {
|
|||
|
||||
nsTArray<nsFontFaceRuleContainer> mFontFaceRules;
|
||||
nsTArray<nsCSSKeyframesRule*> mKeyframesRules;
|
||||
nsTArray<nsCSSFontFeatureValuesRule*> mFontFeatureValuesRules;
|
||||
nsTArray<nsCSSPageRule*> 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;
|
||||
|
@ -2749,6 +2751,21 @@ nsCSSRuleProcessor::AppendPageRules(
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsCSSRuleProcessor::AppendFontFeatureValuesRules(
|
||||
nsPresContext *aPresContext,
|
||||
nsTArray<nsCSSFontFeatureValuesRule*>& 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<nsFontFaceRuleContainer>& aFontFaceRules,
|
||||
nsTArray<nsCSSKeyframesRule*>& aKeyframesRules,
|
||||
nsTArray<nsCSSFontFeatureValuesRule*>& aFontFeatureValuesRules,
|
||||
nsTArray<nsCSSPageRule*>& 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<nsFontFaceRuleContainer>& mFontFaceRules;
|
||||
nsTArray<nsCSSKeyframesRule*>& mKeyframesRules;
|
||||
nsTArray<nsCSSFontFeatureValuesRule*>& mFontFeatureValuesRules;
|
||||
nsTArray<nsCSSPageRule*>& 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<nsCSSFontFeatureValuesRule*>(aRule);
|
||||
if (!data->mFontFeatureValuesRules.AppendElement(fontFeatureValuesRule)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (css::Rule::PAGE_RULE == type) {
|
||||
nsCSSPageRule* pageRule = static_cast<nsCSSPageRule*>(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);
|
||||
|
|
|
@ -128,6 +128,9 @@ public:
|
|||
bool AppendPageRules(nsPresContext* aPresContext,
|
||||
nsTArray<nsCSSPageRule*>& aArray);
|
||||
|
||||
bool AppendFontFeatureValuesRules(nsPresContext* aPresContext,
|
||||
nsTArray<nsCSSFontFeatureValuesRule*>& 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
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "nsDOMClassInfoID.h"
|
||||
#include "mozilla/dom/CSSStyleDeclarationBinding.h"
|
||||
#include "StyleRule.h"
|
||||
#include "nsFont.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
|
@ -1910,6 +1911,255 @@ nsCSSFontFaceRule::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const
|
|||
}
|
||||
|
||||
|
||||
// -----------------------------------
|
||||
// nsCSSFontFeatureValuesRule
|
||||
//
|
||||
|
||||
/* virtual */ already_AddRefed<css::Rule>
|
||||
nsCSSFontFeatureValuesRule::Clone() const
|
||||
{
|
||||
nsRefPtr<css::Rule> 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<nsString>& 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<gfxFontFeatureValueSet::FeatureValues>& 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<nsString>& aFamilyList,
|
||||
const nsTArray<gfxFontFeatureValueSet::FeatureValues>& 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<nsString>& aFamilyArray)
|
||||
: familyArray(aFamilyArray), hasGeneric(false)
|
||||
{}
|
||||
|
||||
static bool
|
||||
AddFamily(const nsString& aFamily, bool aGeneric, void* aData)
|
||||
{
|
||||
MakeFamilyArray *familyArr = reinterpret_cast<MakeFamilyArray*> (aData);
|
||||
if (!aGeneric && !aFamily.IsEmpty()) {
|
||||
familyArr->familyArray.AppendElement(aFamily);
|
||||
}
|
||||
if (aGeneric) {
|
||||
familyArr->hasGeneric = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
nsTArray<nsString>& 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<gfxFontFeatureValueSet::ValueList>& 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
|
||||
//
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
@ -285,6 +287,61 @@ nsCSSFontFaceStyleDecl::ContainingRule() const
|
|||
(reinterpret_cast<const char*>(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<mozilla::css::Rule> Clone() const MOZ_OVERRIDE;
|
||||
|
||||
// nsIDOMCSSRule interface
|
||||
NS_DECL_NSIDOMCSSRULE
|
||||
|
||||
// nsIDOMCSSFontFaceRule interface
|
||||
NS_DECL_NSIDOMCSSFONTFEATUREVALUESRULE
|
||||
|
||||
const nsTArray<nsString>& GetFamilyList() { return mFamilyList; }
|
||||
void SetFamilyList(const nsAString& aFamilyList, bool& aContainsGeneric);
|
||||
|
||||
void AddValueList(int32_t aVariantAlternate,
|
||||
nsTArray<gfxFontFeatureValueSet::ValueList>& aValueList);
|
||||
|
||||
const nsTArray<gfxFontFeatureValueSet::FeatureValues>& GetFeatureValues()
|
||||
{
|
||||
return mFeatureValues;
|
||||
}
|
||||
|
||||
virtual size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
|
||||
|
||||
static bool PrefEnabled()
|
||||
{
|
||||
return mozilla::Preferences::GetBool("layout.css.font-features.enabled");
|
||||
}
|
||||
|
||||
protected:
|
||||
nsTArray<nsString> mFamilyList;
|
||||
nsTArray<gfxFontFeatureValueSet::FeatureValues> mFeatureValues;
|
||||
};
|
||||
|
||||
namespace mozilla {
|
||||
namespace css {
|
||||
|
||||
|
|
|
@ -110,6 +110,7 @@ nsStyleSet::nsStyleSet()
|
|||
mInShutdown(false),
|
||||
mAuthorStyleDisabled(false),
|
||||
mInReconstruct(false),
|
||||
mInitFontFeatureValuesLookup(true),
|
||||
mDirty(0),
|
||||
mUnusedRuleNodeCount(0)
|
||||
{
|
||||
|
@ -1540,6 +1541,59 @@ nsStyleSet::AppendKeyframesRules(nsPresContext* aPresContext,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsStyleSet::AppendFontFeatureValuesRules(nsPresContext* aPresContext,
|
||||
nsTArray<nsCSSFontFeatureValuesRule*>& aArray)
|
||||
{
|
||||
NS_ENSURE_FALSE(mInShutdown, false);
|
||||
|
||||
for (uint32_t i = 0; i < NS_ARRAY_LENGTH(gCSSSheetTypes); ++i) {
|
||||
nsCSSRuleProcessor *ruleProc = static_cast<nsCSSRuleProcessor*>
|
||||
(mRuleProcessors[gCSSSheetTypes[i]].get());
|
||||
if (ruleProc &&
|
||||
!ruleProc->AppendFontFeatureValuesRules(aPresContext, aArray))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
already_AddRefed<gfxFontFeatureValueSet>
|
||||
nsStyleSet::GetFontFeatureValuesLookup()
|
||||
{
|
||||
if (mInitFontFeatureValuesLookup) {
|
||||
mInitFontFeatureValuesLookup = false;
|
||||
|
||||
nsTArray<nsCSSFontFeatureValuesRule*> 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<nsString>& familyList = rule->GetFamilyList();
|
||||
const nsTArray<gfxFontFeatureValueSet::FeatureValues>&
|
||||
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<gfxFontFeatureValueSet> lookup = mFontFeatureValuesLookup;
|
||||
return lookup.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
nsStyleSet::AppendPageRules(nsPresContext* aPresContext,
|
||||
nsTArray<nsCSSPageRule*>& aArray)
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
class nsIURI;
|
||||
class nsCSSFontFaceRule;
|
||||
class nsCSSKeyframesRule;
|
||||
class nsCSSFontFeatureValuesRule;
|
||||
class nsCSSPageRule;
|
||||
class nsRuleWalker;
|
||||
struct ElementDependentRuleProcessorData;
|
||||
|
@ -164,11 +165,13 @@ class nsStyleSet
|
|||
bool AppendKeyframesRules(nsPresContext* aPresContext,
|
||||
nsTArray<nsCSSKeyframesRule*>& aArray);
|
||||
|
||||
already_AddRefed<gfxFontFeatureValueSet>
|
||||
GetFontFeatureValuesLookup()
|
||||
{
|
||||
return nullptr; // not implemented yet
|
||||
}
|
||||
// Fetch object for looking up font feature values
|
||||
already_AddRefed<gfxFontFeatureValueSet> 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<nsCSSFontFeatureValuesRule*>& aArray);
|
||||
|
||||
// Append all the currently-active page rules to aArray. Return
|
||||
// true for success and false for failure.
|
||||
|
@ -412,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
|
||||
|
@ -429,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<nsRuleNode*> mOldRuleTrees;
|
||||
|
||||
// whether font feature values lookup object needs initialization
|
||||
nsRefPtr<gfxFontFeatureValueSet> mFontFeatureValuesLookup;
|
||||
};
|
||||
|
||||
#ifdef _IMPL_NS_LAYOUT
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -0,0 +1,356 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<title>@font-feature-values rule parsing tests</title>
|
||||
<link rel="author" title="John Daggett" href="mailto:jdaggett@mozilla.com">
|
||||
<link rel="help" href="http://www.w3.org/TR/css3-fonts/#font-feature-values" />
|
||||
<meta name="assert" content="tests that valid @font-feature-values rules parse and invalid ones don't" />
|
||||
<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=549861 -->
|
||||
<script type="text/javascript" src="/resources/testharness.js"></script>
|
||||
<script type="text/javascript" src="/resources/testharnessreport.js"></script>
|
||||
<style type="text/css">
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="log"></div>
|
||||
<pre id="display"></pre>
|
||||
<style type="text/css" id="testbox"></style>
|
||||
|
||||
<script type="text/javascript">
|
||||
var gPrefix = "";
|
||||
var kFontFeatureValuesRuleType = 14;
|
||||
|
||||
function ruleName() { return "@" + gPrefix + "font-feature-values"; }
|
||||
function makeRule(f, v) {
|
||||
return ruleName() + " " + f + " { " + v + " }";
|
||||
}
|
||||
|
||||
function _()
|
||||
{
|
||||
var i, decl = [];
|
||||
for (i = 0; i < arguments.length; i++) {
|
||||
decl.push(arguments[i]);
|
||||
}
|
||||
return makeRule("bongo", decl.join(" "));
|
||||
}
|
||||
|
||||
// note: because of bugs in the way family names are serialized,
|
||||
// 'serializationSame' only implies that the value definition block
|
||||
// is the same (i.e. not including the family name list)
|
||||
|
||||
var testrules = [
|
||||
|
||||
/* basic syntax */
|
||||
{ rule: ruleName() + ";", invalid: true },
|
||||
{ rule: ruleName() + " bongo;", invalid: true },
|
||||
{ rule: ruleName().replace("values", "value") + " {;}", invalid: true },
|
||||
{ rule: ruleName().replace("feature", "features") + " {;}", invalid: true },
|
||||
{ rule: makeRule("bongo", ""), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", ";"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", ",;"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", ";,"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", ",;,"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset;"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset,;"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset abc;"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset { abc }"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset { ;;abc }"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset { abc;; }"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset { abc: }"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset { abc,: }"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset { abc:, }"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset { abc:,; }"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset { a,b }"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset { a;b }"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset { a:;b: }"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset { a:,;b: }"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset { a:1,;b: }"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset { abc 1 2 3 }"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset { abc:, 1 2 3 }"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset { abc:; 1 2 3 }"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset { abc:; 1 2 3 }"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset { abc: 1 2 3a }"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@styleset { abc: 1 2 3, def: 1; }"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@blah @styleset { abc: 1 2 3; }"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@blah } @styleset { abc: 1 2 3; }"), serializationNoValueDefn: true },
|
||||
{ rule: makeRule("bongo", "@blah , @styleset { abc: 1 2 3; }"), serializationNoValueDefn: true },
|
||||
{ rule: ruleName() + " bongo { @styleset { abc: 1 2 3; }", serialization: _("@styleset { abc: 1 2 3; }") },
|
||||
{ rule: ruleName() + " bongo { @styleset { abc: 1 2 3 }", serialization: _("@styleset { abc: 1 2 3; }") },
|
||||
{ rule: ruleName() + " bongo { @styleset { abc: 1 2 3;", serialization: _("@styleset { abc: 1 2 3; }") },
|
||||
{ rule: ruleName() + " bongo { @styleset { abc: 1 2 3", serialization: _("@styleset { abc: 1 2 3; }") },
|
||||
{ rule: _("@styleset { ok-1: 1; }"), serializationSame: true },
|
||||
{ rule: _("@annotation { ok-1: 3; }"), serializationSame: true },
|
||||
{ rule: _("@stylistic { blah: 3; }"), serializationSame: true },
|
||||
{ rule: makeRule("bongo", "\n@styleset\n { blah: 3; super-blah: 4 5;\n more-blah: 5 6 7;\n }"), serializationSame: true },
|
||||
{ rule: makeRule("bongo", "\n@styleset\n {\n blah:\n 3\n;\n super-blah:\n 4\n 5\n;\n more-blah:\n 5 6\n 7;\n }"), serializationSame: true },
|
||||
|
||||
/* limits on number of values */
|
||||
{ rule: _("@stylistic { blah: 1; }"), serializationSame: true },
|
||||
{ rule: _("@styleset { blah: 1 2 3 4; }"), serializationSame: true },
|
||||
{ rule: _("@character-variant { blah: 1 2; }"), serializationSame: true },
|
||||
{ rule: _("@swash { blah: 1; }"), serializationSame: true },
|
||||
{ rule: _("@ornaments { blah: 1; }"), serializationSame: true },
|
||||
{ rule: _("@annotation { blah: 1; }"), serializationSame: true },
|
||||
|
||||
/* values ignored when used */
|
||||
{ rule: _("@styleset { blah: 0; }"), serializationSame: true },
|
||||
{ rule: _("@styleset { blah: 120 124; }"), serializationSame: true },
|
||||
{ rule: _("@character-variant { blah: 0; }"), serializationSame: true },
|
||||
{ rule: _("@character-variant { blah: 111; }"), serializationSame: true },
|
||||
{ rule: _("@character-variant { blah: 111 13; }"), serializationSame: true },
|
||||
|
||||
/* invalid value name */
|
||||
{ rulesrc: ["styleset { blah: 1 }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["stylistic { blah: 1 }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["character-variant { blah: 1 }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["swash { blah: 1 }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["ornaments { blah: 1 }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["annotation { blah: 1 }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@bongo { blah: 1 }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@bongo { blah: 1 2 3 }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@bongo { blah: 1 2 3; burp: 1;;; }"], serializationNoValueDefn: true },
|
||||
|
||||
/* values */
|
||||
{ rulesrc: ["@styleset { blah: -1 }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@styleset { blah: 1 -1 }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@styleset { blah: 1.5 }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@styleset { blah: 15px }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@styleset { blah: red }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@styleset { blah: (1) }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@styleset { blah:(1) }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@styleset { blah:, 1 }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@styleset { blah: <1> }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@styleset { blah: 1! }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@styleset { blah: 1,, }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@styleset { blah: 1 1 1 1; }"], serializationSame: true },
|
||||
|
||||
/* limits on number of values */
|
||||
{ rulesrc: ["@stylistic { blah: 1 2 }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@character-variant { blah: 1 2 3 }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@swash { blah: 1 2 }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@ornaments { blah: 1 2 }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@annotation { blah: 1 2 }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@styleset { blah: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19; }"], serializationSame: true },
|
||||
|
||||
/* family names */
|
||||
{ rule: makeRule("bongo", "@styleset { blah: 1; }"), serializationSame: true },
|
||||
{ rule: makeRule("\"bongo\"", "@styleset { blah: 1; }"), serializationSame: true },
|
||||
{ rule: makeRule("'bongo'", "@styleset { blah: 1; }"), serializationSame: true },
|
||||
{ rule: makeRule("\\62 ongo", "@styleset { blah: 1; }"), serializationSame: true },
|
||||
{ rule: makeRule("bongo, super bongo, bongo the supreme", "@styleset { blah: 1; }"), serializationSame: true },
|
||||
{ rule: makeRule("bongo,, super bongo", "@styleset { blah: 1; }"), invalid: true },
|
||||
{ rule: makeRule("bongo,*", "@styleset { blah: 1; }"), invalid: true },
|
||||
{ rule: makeRule("bongo, sans-serif", "@styleset { blah: 1; }"), invalid: true },
|
||||
{ rule: makeRule("serif, sans-serif", "@styleset { blah: 1; }"), invalid: true },
|
||||
{ rule: makeRule("'serif', 'sans-serif'", "@styleset { blah: 1; }"), serializationSame: true },
|
||||
{ rule: makeRule("bongo, \"super bongo\", 'bongo the supreme'", "@styleset { blah: 1; }"), serializationSame: true },
|
||||
{ rule: makeRule("毎日カレーを食べたい!", "@styleset { blah: 1; }"), serializationSame: true },
|
||||
{ rule: makeRule("毎日カレーを食べたい!, 納豆嫌い", "@styleset { blah: 1; }"), serializationSame: true },
|
||||
|
||||
{ rule: makeRule("bongo, \"super\" bongo, bongo the supreme", "@styleset { blah: 1; }"), invalid: true },
|
||||
{ rule: makeRule("--bongo", "@styleset { blah: 1; }"), invalid: true },
|
||||
|
||||
/* ident tests */
|
||||
{ rule: _("@styleset { blah: 1; blah: 1; }"), serializationSame: true },
|
||||
{ rule: _("@styleset { blah: 1; de-blah: 1; blah: 2; }"), serializationSame: true },
|
||||
{ rule: _("@styleset { \\tra-la: 1; }"), serialization: _("@styleset { tra-la: 1; }") },
|
||||
{ rule: _("@styleset { b\\lah: 1; }"), serialization: _("@styleset { blah: 1; }") },
|
||||
{ rule: _("@styleset { \\62 lah: 1; }"), serialization: _("@styleset { blah: 1; }") },
|
||||
{ rule: _("@styleset { \\:blah: 1; }"), serialization: _("@styleset { \\:blah: 1; }") },
|
||||
{ rule: _("@styleset { \\;blah: 1; }"), serialization: _("@styleset { \\;blah: 1; }") },
|
||||
{ rule: _("@styleset { complex\\20 blah: 1; }"), serialization: _("@styleset { complex\\ blah: 1; }") },
|
||||
{ rule: _("@styleset { complex\\ blah: 1; }"), serializationSame: true },
|
||||
{ rule: _("@styleset { Håkon: 1; }"), serializationSame: true },
|
||||
{ rule: _("@styleset { Åквариум: 1; }"), serializationSame: true },
|
||||
{ rule: _("@styleset { \\1f449\\1f4a9\\1f448: 1; }"), serialization: _("@styleset { 👉💩👈: 1; }") },
|
||||
{ rule: _("@styleset { 魅力: 1; }"), serializationSame: true },
|
||||
{ rule: _("@styleset { 毎日カレーを食べたい!: 1; }"), serializationSame: true },
|
||||
/* from http://en.wikipedia.org/wiki/Metal_umlaut */
|
||||
{ rule: _("@styleset { TECHNICIÄNS\\ ÖF\\ SPÅCE\\ SHIP\\ EÅRTH\\ THIS\\ IS\\ YÖÜR\\ CÄPTÅIN\\ SPEÄKING\\ YÖÜR\\ ØÅPTÅIN\\ IS\\ DEA̋D: 1; }"), serializationSame: true },
|
||||
|
||||
{ rulesrc: ["@styleset { 123blah: 1; }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@styleset { :123blah 1; }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@styleset { :123blah: 1; }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@styleset { ?123blah: 1; }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@styleset { \"blah\": 1; }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@styleset { complex blah: 1; }"], serializationNoValueDefn: true },
|
||||
{ rulesrc: ["@styleset { complex\\ blah: 1; }"], serializationNoValueDefn: true }
|
||||
|
||||
];
|
||||
|
||||
// test that invalid value declarations don't affect the parsing of surrounding
|
||||
// declarations. So before + invalid + after should match the serialization
|
||||
// given in s.
|
||||
|
||||
var gSurroundingTests = [
|
||||
// -- invalid, valid ==> valid
|
||||
{ before: "", after: "@ornaments { whatchamacallit-1: 23; thingy-dingy: 3; }", s: _("@ornaments { whatchamacallit-1: 23; thingy-dingy: 3; }") },
|
||||
|
||||
// -- valid, invalid ==> valid
|
||||
{ before: "@ornaments { whatchamacallit-1: 23; thingy-dingy: 7; }", after: "", s: _("@ornaments { whatchamacallit-1: 23; thingy-dingy: 7; }") },
|
||||
|
||||
// -- valid, invalid, valid ==> valid, valid
|
||||
{ before: "@ornaments { whatchamacallit-1: 23; thingy-dingy: 3; }", after: "@character-variant { whatchamacallit-2: 23 4; }", s: _("@ornaments { whatchamacallit-1: 23; thingy-dingy: 3; } @character-variant { whatchamacallit-2: 23 4; }") },
|
||||
|
||||
// -- invalid, valid, invalid ==> valid
|
||||
{ between: "@ornaments { whatchamacallit-1: 23; thingy-dingy: 4; }", s: _("@ornaments { whatchamacallit-1: 23; thingy-dingy: 4; }") }
|
||||
];
|
||||
|
||||
/* strip out just values, along with empty value blocks (e.g. @swash { })*/
|
||||
function valuesText(ruletext)
|
||||
{
|
||||
var t = ruletext.replace(/@[a-zA-Z0-9\-]+[ \n]*{[ \n]*}/g, "");
|
||||
t = t.replace(/[ \n]+/g, " ");
|
||||
t = t.replace(/^[^{]+{[ \n]*/, "");
|
||||
t = t.replace(/[ \n]*}[^}]*$/, "");
|
||||
t = t.replace(/[ \n]*;/g, ";");
|
||||
return t;
|
||||
}
|
||||
|
||||
function testParse(rulesrc)
|
||||
{
|
||||
var sheet = document.styleSheets[1];
|
||||
var rule = _.apply(this, rulesrc);
|
||||
|
||||
while(sheet.cssRules.length > 0)
|
||||
sheet.deleteRule(0);
|
||||
try {
|
||||
sheet.insertRule(rule, 0);
|
||||
} catch (e) {
|
||||
return e.toString();
|
||||
}
|
||||
|
||||
if (sheet.cssRules.length == 1 && sheet.cssRules[0].type == kFontFeatureValuesRuleType) {
|
||||
return sheet.cssRules[0].cssText.replace(/[ \n]+/g, " ");
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
function testOneRule(testrule) {
|
||||
var sheet = document.styleSheets[1];
|
||||
var rule;
|
||||
|
||||
if ("rulesrc" in testrule) {
|
||||
rule = _.apply(this, testrule.rulesrc);
|
||||
} else {
|
||||
rule = testrule.rule;
|
||||
}
|
||||
|
||||
var parseErr = false;
|
||||
var expectedErr = false;
|
||||
var invalid = false;
|
||||
if ("invalid" in testrule && testrule.invalid) invalid = true;
|
||||
|
||||
while(sheet.cssRules.length > 0)
|
||||
sheet.deleteRule(0);
|
||||
try {
|
||||
sheet.insertRule(rule, 0);
|
||||
} catch (e) {
|
||||
expectedErr = (e.name == "SyntaxError"
|
||||
&& e instanceof DOMException
|
||||
&& e.code == DOMException.SYNTAX_ERR
|
||||
&& invalid);
|
||||
parseErr = true;
|
||||
}
|
||||
|
||||
test(function() {
|
||||
assert_true(!parseErr || expectedErr, "unexpected syntax error");
|
||||
if (!parseErr) {
|
||||
assert_equals(sheet.cssRules.length, 1, "bad rule count");
|
||||
assert_equals(sheet.cssRules[0].type, kFontFeatureValuesRuleType, "bad rule type");
|
||||
}
|
||||
}, "basic parse tests - " + rule);
|
||||
|
||||
var sanitizedRule = rule.replace(/[ \n]+/g, " ");
|
||||
if (parseErr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// should result in one @font-feature-values rule constructed
|
||||
|
||||
// serialization matches expectation
|
||||
// -- note: due to inconsistent font family serialization problems,
|
||||
// only the serialization of the values is tested currently
|
||||
|
||||
var ruleValues = valuesText(rule);
|
||||
var serialized = sheet.cssRules[0].cssText;
|
||||
var serializedValues = valuesText(serialized);
|
||||
var haveSerialization = true;
|
||||
|
||||
if (testrule.serializationSame) {
|
||||
test(function() {
|
||||
assert_equals(serializedValues, ruleValues, "canonical cssText serialization doesn't match");
|
||||
}, "serialization check - " + rule);
|
||||
} else if ("serialization" in testrule) {
|
||||
var s = valuesText(testrule.serialization);
|
||||
test(function() {
|
||||
assert_equals(serializedValues, s, "non-canonical cssText serialization doesn't match - ");
|
||||
}, "serialization check - " + rule);
|
||||
} else if (testrule.serializationNoValueDefn) {
|
||||
test(function() {
|
||||
assert_equals(serializedValues, "", "cssText serialization should have no value defintions - ");
|
||||
}, "no value definitions in serialization - " + rule);
|
||||
|
||||
haveSerialization = false;
|
||||
|
||||
if ("rulesrc" in testrule) {
|
||||
test(function() {
|
||||
var j, rulesrc = testrule.rulesrc;
|
||||
|
||||
// invalid value definitions shouldn't affect the parsing of valid
|
||||
// definitions before or after an invalid one
|
||||
for (var j = 0; j < gSurroundingTests.length; j++) {
|
||||
var t = gSurroundingTests[j];
|
||||
var srulesrc = [];
|
||||
|
||||
if ("between" in t) {
|
||||
srulesrc = srulesrc.concat(rulesrc);
|
||||
srulesrc = srulesrc.concat(t.between);
|
||||
srulesrc = srulesrc.concat(rulesrc);
|
||||
} else {
|
||||
if (t.before != "")
|
||||
srulesrc = srulesrc.concat(t.before);
|
||||
srulesrc = srulesrc.concat(rulesrc);
|
||||
if (t.after != "")
|
||||
srulesrc = srulesrc.concat(t.after);
|
||||
}
|
||||
|
||||
var result = testParse(srulesrc);
|
||||
assert_equals(valuesText(result), valuesText(t.s), "invalid declarations should not affect valid ones - ");
|
||||
}
|
||||
}, "invalid declarations don't affect valid ones - " + rule);
|
||||
}
|
||||
}
|
||||
|
||||
// if serialization non-empty, serialization should round-trip to itself
|
||||
if (haveSerialization) {
|
||||
var roundTripText = testParse([serializedValues]);
|
||||
test(function() {
|
||||
assert_equals(valuesText(roundTripText), serializedValues,
|
||||
"serialization should round-trip to itself - ");
|
||||
}, "serialization round-trip - " + rule);
|
||||
}
|
||||
}
|
||||
|
||||
function testFontFeatureValuesRuleParsing() {
|
||||
var i;
|
||||
for (i = 0; i < testrules.length; i++) {
|
||||
var testrule = testrules[i];
|
||||
var rule;
|
||||
|
||||
if ("rulesrc" in testrule) {
|
||||
rule = _.apply(this, testrule.rulesrc);
|
||||
} else {
|
||||
rule = testrule.rule;
|
||||
}
|
||||
|
||||
testOneRule(testrule);
|
||||
//test(function() { testOneRule(testrule); }, "parsing " + rule);
|
||||
}
|
||||
}
|
||||
|
||||
testFontFeatureValuesRuleParsing();
|
||||
</script>
|
||||
</body></html>
|
Загрузка…
Ссылка в новой задаче