Bug 549861. Implement support for @font-feature-values rule. r=dbaron

This commit is contained in:
John Daggett 2013-05-13 18:45:37 +09:00
Родитель 6f990b3fc6
Коммит 5b570d8745
20 изменённых файлов: 1085 добавлений и 20 удалений

Просмотреть файл

@ -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)
@ -8012,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;
}

Просмотреть файл

@ -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<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;
@ -1437,11 +1439,11 @@ static bool AttrMatchesValue(const nsAttrSelector* aAttrSelector,
: static_cast<const nsStringComparator&>(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<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;
@ -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<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;
@ -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<nsCSSFontFaceRule> mRule;
@ -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)
{
@ -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<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)
@ -1812,7 +1866,7 @@ struct MOZ_STACK_CLASS AttributeData : public AttributeRuleProcessorData {
mHint(nsRestyleHint(0))
{}
nsRestyleHint mHint;
};
};
static bool
SheetHasAttributeStyle(nsIStyleRuleProcessor* aProcessor, void *aData)

Просмотреть файл

@ -28,6 +28,7 @@
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<nsStyleContext>
@ -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>