mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-31 22:25:30 +00:00
4112 lines
128 KiB
C++
4112 lines
128 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public License
|
|
* Version 1.0 (the "NPL"); you may not use this file except in
|
|
* compliance with the NPL. You may obtain a copy of the NPL at
|
|
* http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the NPL is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
|
|
* for the specific language governing rights and limitations under the
|
|
* NPL.
|
|
*
|
|
* The Initial Developer of this code under the NPL is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
|
|
* Reserved.
|
|
*/
|
|
#include "nsICSSParser.h"
|
|
#include "nsCSSProps.h"
|
|
#include "nsCSSKeywords.h"
|
|
#include "nsCSSScanner.h"
|
|
#include "nsICSSLoader.h"
|
|
#include "nsICSSStyleRule.h"
|
|
#include "nsICSSImportRule.h"
|
|
#include "nsICSSMediaRule.h"
|
|
#include "nsICSSCharsetRule.h"
|
|
#include "nsICSSNameSpaceRule.h"
|
|
#include "nsIUnicharInputStream.h"
|
|
#include "nsIStyleSet.h"
|
|
#include "nsICSSStyleSheet.h"
|
|
#include "nsICSSDeclaration.h"
|
|
#include "nsStyleConsts.h"
|
|
#include "nsIURL.h"
|
|
#ifdef NECKO
|
|
#include "nsNeckoUtil.h"
|
|
#include "nsCOMPtr.h"
|
|
#else
|
|
#include "nsIURLGroup.h"
|
|
#endif // NECKO
|
|
#include "nsString.h"
|
|
#include "nsIAtom.h"
|
|
#include "nsVoidArray.h"
|
|
#include "nsISupportsArray.h"
|
|
#include "nsColor.h"
|
|
#include "nsStyleConsts.h"
|
|
#include "nsCSSAtoms.h"
|
|
#include "nsINameSpaceManager.h"
|
|
#include "nsINameSpace.h"
|
|
|
|
// XXX TODO:
|
|
// - rework aErrorCode stuff: switch over to nsresult
|
|
// verify ! is followed by important and nothing else
|
|
|
|
static NS_DEFINE_IID(kICSSParserIID, NS_ICSS_PARSER_IID);
|
|
static NS_DEFINE_IID(kICSSStyleSheetIID, NS_ICSS_STYLE_SHEET_IID);
|
|
static NS_DEFINE_IID(kIStyleSheetIID, NS_ISTYLE_SHEET_IID);
|
|
|
|
// e.g. "P B, H1 B { ... }" has a selector list with two elements,
|
|
// each of which has two selectors.
|
|
struct SelectorList {
|
|
SelectorList(void);
|
|
~SelectorList(void);
|
|
|
|
void AddSelector(const nsCSSSelector& aSelector);
|
|
|
|
#ifdef NS_DEBUG
|
|
void Dump(void);
|
|
#endif
|
|
|
|
nsCSSSelector* mSelectors;
|
|
nsAutoString mSourceString;
|
|
PRInt32 mWeight;
|
|
SelectorList* mNext;
|
|
};
|
|
|
|
SelectorList::SelectorList(void)
|
|
: mSelectors(nsnull),
|
|
mNext(nsnull)
|
|
{
|
|
}
|
|
|
|
SelectorList::~SelectorList()
|
|
{
|
|
nsCSSSelector* sel = mSelectors;
|
|
while (nsnull != sel) {
|
|
nsCSSSelector* dead = sel;
|
|
sel = sel->mNext;
|
|
delete dead;
|
|
}
|
|
if (nsnull != mNext) {
|
|
delete mNext;
|
|
}
|
|
}
|
|
|
|
void SelectorList::AddSelector(const nsCSSSelector& aSelector)
|
|
{ // prepend to list
|
|
nsCSSSelector* newSel = new nsCSSSelector(aSelector);
|
|
if (nsnull != newSel) {
|
|
newSel->mNext = mSelectors;
|
|
mSelectors = newSel;
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef NS_DEBUG
|
|
void SelectorList::Dump()
|
|
{
|
|
}
|
|
#endif
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
// Your basic top-down recursive descent style parser
|
|
class CSSParserImpl : public nsICSSParser {
|
|
public:
|
|
CSSParserImpl();
|
|
virtual ~CSSParserImpl();
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
NS_IMETHOD Init(nsICSSStyleSheet* aSheet);
|
|
|
|
NS_IMETHOD GetInfoMask(PRUint32& aResult);
|
|
|
|
NS_IMETHOD SetStyleSheet(nsICSSStyleSheet* aSheet);
|
|
|
|
NS_IMETHOD SetCaseSensitive(PRBool aCaseSensitive);
|
|
|
|
NS_IMETHOD SetQuirkMode(PRBool aQuirkMode);
|
|
|
|
NS_IMETHOD SetChildLoader(nsICSSLoader* aChildLoader);
|
|
|
|
NS_IMETHOD Parse(nsIUnicharInputStream* aInput,
|
|
nsIURI* aInputURL,
|
|
nsICSSStyleSheet*& aResult);
|
|
|
|
NS_IMETHOD ParseDeclarations(const nsString& aDeclaration,
|
|
nsIURI* aBaseURL,
|
|
nsIStyleRule*& aResult);
|
|
|
|
NS_IMETHOD ParseAndAppendDeclaration(const nsString& aBuffer,
|
|
nsIURI* aBaseURL,
|
|
nsICSSDeclaration* aDeclaration,
|
|
PRInt32* aHint);
|
|
|
|
protected:
|
|
PRBool GetToken(PRInt32& aErrorCode, PRBool aSkipWS);
|
|
PRBool GetURLToken(PRInt32& aErrorCode, PRBool aSkipWS);
|
|
void UngetToken();
|
|
|
|
PRBool ExpectSymbol(PRInt32& aErrorCode, PRUnichar aSymbol, PRBool aSkipWS);
|
|
PRBool ExpectEndProperty(PRInt32& aErrorCode, PRBool aSkipWS);
|
|
nsString* NextIdent(PRInt32& aErrorCode);
|
|
void SkipUntil(PRInt32& aErrorCode, PRUnichar aStopSymbol);
|
|
void SkipRuleSet(PRInt32& aErrorCode);
|
|
PRBool SkipAtRule(PRInt32& aErrorCode);
|
|
PRBool SkipDeclaration(PRInt32& aErrorCode, PRBool aCheckForBraces);
|
|
|
|
PRBool PushGroup(nsICSSGroupRule* aRule);
|
|
void PopGroup(void);
|
|
void AppendRule(nsICSSRule* aRule);
|
|
|
|
PRBool ParseRuleSet(PRInt32& aErrorCode);
|
|
PRBool ParseAtRule(PRInt32& aErrorCode);
|
|
PRBool ParseCharsetRule(PRInt32& aErrorCode);
|
|
PRBool ParseImportRule(PRInt32& aErrorCode);
|
|
PRBool GatherMedia(PRInt32& aErrorCode, nsString& aMedia, nsISupportsArray* aMediaAtoms);
|
|
PRBool ProcessImport(PRInt32& aErrorCode, const nsString& aURLSpec, const nsString& aMedia);
|
|
PRBool ParseMediaRule(PRInt32& aErrorCode);
|
|
PRBool ParseNameSpaceRule(PRInt32& aErrorCode);
|
|
PRBool ProcessNameSpace(PRInt32& aErrorCode, const nsString& aPrefix,
|
|
const nsString& aURLSpec);
|
|
PRBool ParseFontFaceRule(PRInt32& aErrorCode);
|
|
PRBool ParsePageRule(PRInt32& aErrorCode);
|
|
|
|
PRBool ParseSelectorList(PRInt32& aErrorCode, SelectorList*& aListHead);
|
|
PRBool ParseSelectorGroup(PRInt32& aErrorCode, SelectorList*& aListHead);
|
|
PRBool ParseSelector(PRInt32& aErrorCode, nsCSSSelector& aSelectorResult, nsString& aSource);
|
|
nsICSSDeclaration* ParseDeclarationBlock(PRInt32& aErrorCode,
|
|
PRBool aCheckForBraces);
|
|
PRBool ParseDeclaration(PRInt32& aErrorCode,
|
|
nsICSSDeclaration* aDeclaration,
|
|
PRBool aCheckForBraces,
|
|
PRInt32& aChangeHint);
|
|
PRBool ParseProperty(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration,
|
|
nsCSSProperty aPropID, PRInt32& aChangeHint);
|
|
PRBool ParseProperty(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration,
|
|
nsCSSProperty aPropID);
|
|
PRBool ParseSingleValueProperty(PRInt32& aErrorCode, nsCSSValue& aValue,
|
|
nsCSSProperty aPropID);
|
|
|
|
// Property specific parsing routines
|
|
PRBool ParseAzimuth(PRInt32& aErrorCode, nsCSSValue& aValue);
|
|
PRBool ParseBackground(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
PRBool ParseBackgroundPosition(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
PRBool ParseBorder(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
PRBool ParseBorderColor(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
PRBool ParseBorderSpacing(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
PRBool ParseBorderSide(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration,
|
|
const nsCSSProperty aPropIDs[]);
|
|
PRBool ParseBorderStyle(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
PRBool ParseBorderWidth(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
PRBool ParseClip(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
PRBool ParseContent(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
PRBool ParseCounterData(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration,
|
|
nsCSSProperty aPropID);
|
|
PRBool ParseCue(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
PRBool ParseCursor(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
PRBool ParseFont(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
PRBool ParseFamily(PRInt32& aErrorCode, nsCSSValue& aValue);
|
|
PRBool ParseListStyle(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
PRBool ParseMargin(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
PRBool ParseMarks(PRInt32& aErrorCode, nsCSSValue& aValue);
|
|
PRBool ParseOutline(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
PRBool ParsePadding(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
PRBool ParsePause(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
PRBool ParsePlayDuring(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
PRBool ParseQuotes(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
PRBool ParseSize(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
PRBool ParseTextDecoration(PRInt32& aErrorCode, nsCSSValue& aValue);
|
|
PRBool ParseTextShadow(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration);
|
|
|
|
// Reused utility parsing routines
|
|
PRBool ParseBoxProperties(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration,
|
|
const nsCSSProperty aPropIDs[]);
|
|
PRInt32 ParseChoice(PRInt32& aErrorCode, nsCSSValue aValues[],
|
|
const nsCSSProperty aPropIDs[], PRInt32 aNumIDs);
|
|
PRBool ParseColor(PRInt32& aErrorCode, nsCSSValue& aValue);
|
|
PRBool ParseColorComponent(PRInt32& aErrorCode, PRUint8& aComponent,
|
|
char aStop);
|
|
PRBool ParseEnum(PRInt32& aErrorCode, nsCSSValue& aValue, const PRInt32 aKeywordTable[]);
|
|
PRInt32 SearchKeywordTable(nsCSSKeyword aKeyword, const PRInt32 aTable[]);
|
|
PRBool ParseVariant(PRInt32& aErrorCode, nsCSSValue& aValue,
|
|
PRInt32 aVariantMask,
|
|
const PRInt32 aKeywordTable[]);
|
|
PRBool ParsePositiveVariant(PRInt32& aErrorCode, nsCSSValue& aValue,
|
|
PRInt32 aVariantMask,
|
|
const PRInt32 aKeywordTable[]);
|
|
PRBool ParseCounter(PRInt32& aErrorCode, nsCSSValue& aValue);
|
|
PRBool ParseAttr(PRInt32& aErrorCode, nsCSSValue& aValue);
|
|
PRBool ParseURL(PRInt32& aErrorCode, nsCSSValue& aValue);
|
|
PRBool TranslateDimension(nsCSSValue& aValue, PRInt32 aVariantMask,
|
|
float aNumber, const nsString& aUnit);
|
|
|
|
// Current token. The value is valid after calling GetToken
|
|
nsCSSToken mToken;
|
|
|
|
// After an UngetToken is done this flag is true. The next call to
|
|
// GetToken clears the flag.
|
|
PRBool mHavePushBack;
|
|
|
|
nsCSSScanner* mScanner;
|
|
nsIURI* mURL;
|
|
nsICSSStyleSheet* mSheet;
|
|
PRInt32 mChildSheetCount;
|
|
nsICSSLoader* mChildLoader; // not ref counted, it owns us
|
|
|
|
enum nsCSSSection {
|
|
eCSSSection_Charset,
|
|
eCSSSection_Import,
|
|
eCSSSection_NameSpace,
|
|
eCSSSection_General
|
|
};
|
|
|
|
nsCSSSection mSection;
|
|
|
|
PRBool mNavQuirkMode;
|
|
PRBool mCaseSensitive;
|
|
|
|
nsINameSpace* mNameSpace;
|
|
|
|
nsISupportsArray* mGroupStack;
|
|
};
|
|
|
|
NS_HTML nsresult
|
|
NS_NewCSSParser(nsICSSParser** aInstancePtrResult)
|
|
{
|
|
CSSParserImpl *it = new CSSParserImpl();
|
|
|
|
if (it == nsnull) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
return it->QueryInterface(kICSSParserIID, (void **) aInstancePtrResult);
|
|
}
|
|
|
|
CSSParserImpl::CSSParserImpl()
|
|
: mToken(),
|
|
mHavePushBack(PR_FALSE),
|
|
mScanner(nsnull),
|
|
mURL(nsnull),
|
|
mSheet(nsnull),
|
|
mChildSheetCount(0),
|
|
mChildLoader(nsnull),
|
|
mSection(eCSSSection_Charset),
|
|
mNavQuirkMode(PR_FALSE),
|
|
mCaseSensitive(PR_FALSE),
|
|
mNameSpace(nsnull),
|
|
mGroupStack(nsnull)
|
|
{
|
|
NS_INIT_REFCNT();
|
|
nsCSSAtoms::AddRefAtoms();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
CSSParserImpl::Init(nsICSSStyleSheet* aSheet)
|
|
{
|
|
NS_IF_RELEASE(mGroupStack);
|
|
NS_IF_RELEASE(mNameSpace);
|
|
NS_IF_RELEASE(mSheet);
|
|
mSheet = aSheet;
|
|
if (mSheet) {
|
|
NS_ADDREF(aSheet);
|
|
mSheet->StyleSheetCount(mChildSheetCount);
|
|
mSheet->GetNameSpace(mNameSpace);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS(CSSParserImpl,kICSSParserIID)
|
|
|
|
CSSParserImpl::~CSSParserImpl()
|
|
{
|
|
NS_IF_RELEASE(mGroupStack);
|
|
NS_IF_RELEASE(mNameSpace);
|
|
NS_IF_RELEASE(mSheet);
|
|
nsCSSAtoms::ReleaseAtoms();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
CSSParserImpl::GetInfoMask(PRUint32& aResult)
|
|
{
|
|
aResult = NS_CSS_GETINFO_CSS1 | NS_CSS_GETINFO_CSSP;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
CSSParserImpl::SetStyleSheet(nsICSSStyleSheet* aSheet)
|
|
{
|
|
NS_PRECONDITION(nsnull != aSheet, "null ptr");
|
|
if (nsnull == aSheet) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
|
|
if (aSheet != mSheet) {
|
|
// Switch to using the new sheet
|
|
NS_IF_RELEASE(mGroupStack);
|
|
NS_IF_RELEASE(mNameSpace);
|
|
NS_IF_RELEASE(mSheet);
|
|
mSheet = aSheet;
|
|
NS_ADDREF(mSheet);
|
|
mSheet->StyleSheetCount(mChildSheetCount);
|
|
mSheet->GetNameSpace(mNameSpace);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
CSSParserImpl::SetCaseSensitive(PRBool aCaseSensitive)
|
|
{
|
|
mCaseSensitive = aCaseSensitive;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
CSSParserImpl::SetQuirkMode(PRBool aQuirkMode)
|
|
{
|
|
mNavQuirkMode = aQuirkMode;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
CSSParserImpl::SetChildLoader(nsICSSLoader* aChildLoader)
|
|
{
|
|
mChildLoader = aChildLoader; // not ref counted, it owns us
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
NS_IMETHODIMP
|
|
CSSParserImpl::Parse(nsIUnicharInputStream* aInput,
|
|
nsIURI* aInputURL,
|
|
nsICSSStyleSheet*& aResult)
|
|
{
|
|
NS_ASSERTION(nsnull != aInputURL, "need base URL");
|
|
|
|
if (! mSheet) {
|
|
NS_NewCSSStyleSheet(&mSheet, aInputURL);
|
|
mChildSheetCount = 0;
|
|
}
|
|
if (! mSheet) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
|
|
PRInt32 errorCode = NS_OK;
|
|
mScanner = new nsCSSScanner();
|
|
if (nsnull == mScanner) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
mScanner->Init(aInput);
|
|
mURL = aInputURL;
|
|
NS_IF_ADDREF(aInputURL);
|
|
|
|
PRInt32 ruleCount = 0;
|
|
mSheet->StyleRuleCount(ruleCount);
|
|
if (0 < ruleCount) {
|
|
nsICSSRule* lastRule = nsnull;
|
|
mSheet->GetStyleRuleAt(ruleCount - 1, lastRule);
|
|
if (lastRule) {
|
|
PRInt32 type;
|
|
lastRule->GetType(type);
|
|
switch (type) {
|
|
case nsICSSRule::CHARSET_RULE:
|
|
case nsICSSRule::IMPORT_RULE:
|
|
mSection = eCSSSection_Import;
|
|
break;
|
|
case nsICSSRule::NAMESPACE_RULE:
|
|
mSection = eCSSSection_NameSpace;
|
|
break;
|
|
default:
|
|
mSection = eCSSSection_General;
|
|
break;
|
|
}
|
|
NS_RELEASE(lastRule);
|
|
}
|
|
}
|
|
else {
|
|
mSection = eCSSSection_Charset; // sheet is empty, any rules are fair
|
|
}
|
|
|
|
nsCSSToken* tk = &mToken;
|
|
for (;;) {
|
|
// Get next non-whitespace token
|
|
if (!GetToken(errorCode, PR_TRUE)) {
|
|
break;
|
|
}
|
|
if (eCSSToken_HTMLComment == tk->mType) {
|
|
continue; // legal here only
|
|
}
|
|
if (eCSSToken_AtKeyword == tk->mType) {
|
|
ParseAtRule(errorCode);
|
|
continue;
|
|
}
|
|
UngetToken();
|
|
if (ParseRuleSet(errorCode)) {
|
|
mSection = eCSSSection_General;
|
|
}
|
|
}
|
|
delete mScanner;
|
|
mScanner = nsnull;
|
|
NS_IF_RELEASE(mURL);
|
|
|
|
aResult = mSheet;
|
|
NS_ADDREF(aResult);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
CSSParserImpl::ParseDeclarations(const nsString& aDeclaration,
|
|
nsIURI* aBaseURL,
|
|
nsIStyleRule*& aResult)
|
|
{
|
|
NS_ASSERTION(nsnull != aBaseURL, "need base URL");
|
|
|
|
nsString* str = new nsString(aDeclaration);
|
|
if (nsnull == str) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
nsIUnicharInputStream* input = nsnull;
|
|
nsresult rv = NS_NewStringUnicharInputStream(&input, str);
|
|
if (NS_OK != rv) {
|
|
delete str;
|
|
return rv;
|
|
}
|
|
|
|
mScanner = new nsCSSScanner();
|
|
if (nsnull == mScanner) {
|
|
NS_RELEASE(input);
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
mScanner->Init(input);
|
|
NS_RELEASE(input);
|
|
|
|
mURL = aBaseURL;
|
|
NS_IF_ADDREF(mURL);
|
|
|
|
mSection = eCSSSection_General;
|
|
PRInt32 errorCode = NS_OK;
|
|
|
|
nsICSSDeclaration* declaration = ParseDeclarationBlock(errorCode, PR_FALSE);
|
|
if (nsnull != declaration) {
|
|
// Create a style rule for the delcaration
|
|
nsICSSStyleRule* rule = nsnull;
|
|
NS_NewCSSStyleRule(&rule, nsCSSSelector());
|
|
rule->SetDeclaration(declaration);
|
|
aResult = rule;
|
|
NS_RELEASE(declaration);
|
|
}
|
|
else {
|
|
aResult = nsnull;
|
|
}
|
|
|
|
delete mScanner;
|
|
mScanner = nsnull;
|
|
NS_IF_RELEASE(mURL);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
CSSParserImpl::ParseAndAppendDeclaration(const nsString& aBuffer,
|
|
nsIURI* aBaseURL,
|
|
nsICSSDeclaration* aDeclaration,
|
|
PRInt32* aHint)
|
|
{
|
|
NS_ASSERTION(nsnull != aBaseURL, "need base URL");
|
|
|
|
nsString* str = new nsString(aBuffer);
|
|
if (nsnull == str) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
nsIUnicharInputStream* input = nsnull;
|
|
nsresult rv = NS_NewStringUnicharInputStream(&input, str);
|
|
if (NS_OK != rv) {
|
|
delete str;
|
|
return rv;
|
|
}
|
|
|
|
mScanner = new nsCSSScanner();
|
|
if (nsnull == mScanner) {
|
|
NS_RELEASE(input);
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
mScanner->Init(input);
|
|
NS_RELEASE(input);
|
|
|
|
mURL = aBaseURL;
|
|
NS_IF_ADDREF(mURL);
|
|
|
|
mSection = eCSSSection_General;
|
|
PRInt32 errorCode = NS_OK;
|
|
|
|
PRInt32 hint = NS_STYLE_HINT_UNKNOWN;
|
|
|
|
ParseDeclaration(errorCode, aDeclaration, PR_FALSE, hint);
|
|
if (nsnull != aHint) {
|
|
*aHint = hint;
|
|
}
|
|
|
|
delete mScanner;
|
|
mScanner = nsnull;
|
|
NS_IF_RELEASE(mURL);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
PRBool CSSParserImpl::GetToken(PRInt32& aErrorCode, PRBool aSkipWS)
|
|
{
|
|
for (;;) {
|
|
if (!mHavePushBack) {
|
|
if (!mScanner->Next(aErrorCode, mToken)) {
|
|
break;
|
|
}
|
|
}
|
|
mHavePushBack = PR_FALSE;
|
|
if (aSkipWS && (eCSSToken_WhiteSpace == mToken.mType)) {
|
|
continue;
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::GetURLToken(PRInt32& aErrorCode, PRBool aSkipWS)
|
|
{
|
|
for (;;) {
|
|
if (! mHavePushBack) {
|
|
if (! mScanner->NextURL(aErrorCode, mToken)) {
|
|
break;
|
|
}
|
|
}
|
|
mHavePushBack = PR_FALSE;
|
|
if (aSkipWS && (eCSSToken_WhiteSpace == mToken.mType)) {
|
|
continue;
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
void CSSParserImpl::UngetToken()
|
|
{
|
|
NS_PRECONDITION(mHavePushBack == PR_FALSE, "double pushback");
|
|
mHavePushBack = PR_TRUE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ExpectSymbol(PRInt32& aErrorCode,
|
|
PRUnichar aSymbol,
|
|
PRBool aSkipWS)
|
|
{
|
|
if (!GetToken(aErrorCode, aSkipWS)) {
|
|
return PR_FALSE;
|
|
}
|
|
if (mToken.IsSymbol(aSymbol)) {
|
|
return PR_TRUE;
|
|
}
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ExpectEndProperty(PRInt32& aErrorCode, PRBool aSkipWS)
|
|
{
|
|
if (!GetToken(aErrorCode, aSkipWS)) {
|
|
return PR_TRUE; // properties may end with eof
|
|
}
|
|
if ((eCSSToken_Symbol == mToken.mType) &&
|
|
((';' == mToken.mSymbol) || ('!' == mToken.mSymbol) || ('}' == mToken.mSymbol))) {
|
|
// XXX need to verify that ! is only followed by "important [;|}]
|
|
// XXX this requires a multi-token pushback buffer
|
|
UngetToken();
|
|
return PR_TRUE;
|
|
}
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
|
|
|
|
nsString* CSSParserImpl::NextIdent(PRInt32& aErrorCode)
|
|
{
|
|
if (!GetToken(aErrorCode, PR_TRUE)) {
|
|
return nsnull;
|
|
}
|
|
if (eCSSToken_Ident != mToken.mType) {
|
|
UngetToken();
|
|
return nsnull;
|
|
}
|
|
return &mToken.mIdent;
|
|
}
|
|
|
|
PRBool CSSParserImpl::SkipAtRule(PRInt32& aErrorCode)
|
|
{
|
|
for (;;) {
|
|
if (!GetToken(aErrorCode, PR_TRUE)) {
|
|
return PR_FALSE;
|
|
}
|
|
if (eCSSToken_Symbol == mToken.mType) {
|
|
PRUnichar symbol = mToken.mSymbol;
|
|
if (symbol == ';') {
|
|
break;
|
|
}
|
|
if (symbol == '{') {
|
|
SkipUntil(aErrorCode, '}');
|
|
break;
|
|
} else if (symbol == '(') {
|
|
SkipUntil(aErrorCode, ')');
|
|
} else if (symbol == '[') {
|
|
SkipUntil(aErrorCode, ']');
|
|
}
|
|
}
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseAtRule(PRInt32& aErrorCode)
|
|
{
|
|
if ((mSection <= eCSSSection_Charset) &&
|
|
(mToken.mIdent.EqualsIgnoreCase("charset"))) {
|
|
if (ParseCharsetRule(aErrorCode)) {
|
|
mSection = eCSSSection_Import; // only one charset allowed
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
if ((mSection <= eCSSSection_Import) &&
|
|
mToken.mIdent.EqualsIgnoreCase("import")) {
|
|
if (ParseImportRule(aErrorCode)) {
|
|
mSection = eCSSSection_Import;
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
if ((mSection <= eCSSSection_NameSpace) &&
|
|
mToken.mIdent.EqualsIgnoreCase("namespace")) {
|
|
if (ParseNameSpaceRule(aErrorCode)) {
|
|
mSection = eCSSSection_NameSpace;
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
if (mToken.mIdent.EqualsIgnoreCase("media")) {
|
|
if (ParseMediaRule(aErrorCode)) {
|
|
mSection = eCSSSection_General;
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
if (mToken.mIdent.EqualsIgnoreCase("font-face")) {
|
|
if (ParseFontFaceRule(aErrorCode)) {
|
|
mSection = eCSSSection_General;
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
if (mToken.mIdent.EqualsIgnoreCase("page")) {
|
|
if (ParsePageRule(aErrorCode)) {
|
|
mSection = eCSSSection_General;
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
|
|
// Skip over unsupported at rule, don't advance section
|
|
return SkipAtRule(aErrorCode);
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseCharsetRule(PRInt32& aErrorCode)
|
|
{
|
|
// XXX not yet implemented
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::GatherMedia(PRInt32& aErrorCode, nsString& aMedia,
|
|
nsISupportsArray* aMediaAtoms)
|
|
{
|
|
PRBool first = PR_TRUE;
|
|
PRBool expectIdent = PR_TRUE;
|
|
for (;;) {
|
|
if (!GetToken(aErrorCode, PR_TRUE)) {
|
|
break;
|
|
}
|
|
if (eCSSToken_Symbol == mToken.mType) {
|
|
PRUnichar symbol = mToken.mSymbol;
|
|
if ((';' == symbol) || ('{' == symbol)) {
|
|
UngetToken();
|
|
return PR_TRUE;
|
|
} else if (',' != symbol) {
|
|
UngetToken();
|
|
break;
|
|
} else if (expectIdent) {
|
|
UngetToken();
|
|
break;
|
|
}
|
|
else {
|
|
expectIdent = PR_TRUE;
|
|
}
|
|
}
|
|
else if (eCSSToken_Ident == mToken.mType) {
|
|
if (expectIdent) {
|
|
if (! first) {
|
|
aMedia.Append(',');
|
|
}
|
|
mToken.mIdent.ToLowerCase(); // case insensitive from CSS - must be lower cased
|
|
if (aMediaAtoms) {
|
|
nsIAtom* medium = NS_NewAtom(mToken.mIdent);
|
|
aMediaAtoms->AppendElement(medium);
|
|
NS_RELEASE(medium);
|
|
}
|
|
aMedia.Append(mToken.mIdent);
|
|
first = PR_FALSE;
|
|
expectIdent = PR_FALSE;
|
|
}
|
|
else {
|
|
UngetToken();
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
UngetToken();
|
|
break;
|
|
}
|
|
}
|
|
aMedia.Truncate();
|
|
if (aMediaAtoms) {
|
|
aMediaAtoms->Clear();
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// Parse a CSS2 import rule: "@import STRING | URL [medium [, mdeium]]"
|
|
PRBool CSSParserImpl::ParseImportRule(PRInt32& aErrorCode)
|
|
{
|
|
if (!GetToken(aErrorCode, PR_TRUE)) {
|
|
return PR_FALSE;
|
|
}
|
|
nsAutoString url;
|
|
nsAutoString media;
|
|
|
|
if (eCSSToken_String == mToken.mType) {
|
|
url = mToken.mIdent;
|
|
if (GatherMedia(aErrorCode, media, nsnull)) {
|
|
if (ExpectSymbol(aErrorCode, ';', PR_TRUE)) {
|
|
ProcessImport(aErrorCode, url, media);
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
}
|
|
else if ((eCSSToken_Function == mToken.mType) &&
|
|
(mToken.mIdent.EqualsIgnoreCase("url"))) {
|
|
if (ExpectSymbol(aErrorCode, '(', PR_FALSE)) {
|
|
if (GetURLToken(aErrorCode, PR_TRUE)) {
|
|
if ((eCSSToken_String == mToken.mType) || (eCSSToken_URL == mToken.mType)) {
|
|
url = mToken.mIdent;
|
|
if (ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
|
|
if (GatherMedia(aErrorCode, media, nsnull)) {
|
|
if (ExpectSymbol(aErrorCode, ';', PR_TRUE)) {
|
|
ProcessImport(aErrorCode, url, media);
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// don't advance section, simply ignore invalid @import
|
|
return PR_FALSE;
|
|
}
|
|
|
|
|
|
PRBool CSSParserImpl::ProcessImport(PRInt32& aErrorCode, const nsString& aURLSpec, const nsString& aMedia)
|
|
{
|
|
PRBool result = PR_FALSE;
|
|
|
|
nsICSSImportRule* rule = nsnull;
|
|
NS_NewCSSImportRule(&rule, aURLSpec, aMedia);
|
|
if (rule) {
|
|
AppendRule(rule);
|
|
NS_RELEASE(rule);
|
|
}
|
|
|
|
if (mChildLoader) {
|
|
// XXX probably need a way to encode unicode junk for the part of
|
|
// the url that follows a "?"
|
|
nsIURI* url;
|
|
#ifdef NECKO
|
|
// XXX need to have nsILoadGroup passed in here
|
|
aErrorCode = NS_NewURI(&url, aURLSpec, mURL/*, group*/);
|
|
#else
|
|
nsILoadGroup* loadGroup = nsnull;
|
|
mURL->GetLoadGroup(&loadGroup);
|
|
if (loadGroup) {
|
|
aErrorCode = loadGroup->CreateURL(&url, mURL, aURLSpec, nsnull);
|
|
NS_RELEASE(loadGroup);
|
|
}
|
|
else {
|
|
aErrorCode = NS_NewURL(&url, aURLSpec, mURL);
|
|
}
|
|
#endif
|
|
|
|
if (NS_FAILED(aErrorCode)) {
|
|
// import url is bad
|
|
// XXX log this somewhere for easier web page debugging
|
|
return PR_FALSE;
|
|
}
|
|
|
|
if (NS_COMFALSE == mSheet->ContainsStyleSheet(url)) { // don't allow circular references
|
|
mChildLoader->LoadChildSheet(mSheet, url, aMedia, kNameSpaceID_Unknown, mChildSheetCount++);
|
|
}
|
|
NS_RELEASE(url);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// Parse a CSS2 media rule: "@media medium [, mdeium] { ... }"
|
|
PRBool CSSParserImpl::ParseMediaRule(PRInt32& aErrorCode)
|
|
{
|
|
nsAutoString mediaStr;
|
|
nsISupportsArray* media = nsnull;
|
|
NS_NewISupportsArray(&media);
|
|
if (media) {
|
|
if (GatherMedia(aErrorCode, mediaStr, media)) {
|
|
if ((0 < mediaStr.Length()) &&
|
|
ExpectSymbol(aErrorCode, '{', PR_TRUE)) {
|
|
// push media rule on stack, loop over children
|
|
nsICSSMediaRule* rule = nsnull;
|
|
NS_NewCSSMediaRule(&rule);
|
|
if (rule) {
|
|
if (PushGroup(rule)) {
|
|
|
|
nsCSSSection holdSection = mSection;
|
|
mSection = eCSSSection_General;
|
|
|
|
for (;;) {
|
|
// Get next non-whitespace token
|
|
if (! GetToken(aErrorCode, PR_TRUE)) {
|
|
break;
|
|
}
|
|
if (mToken.IsSymbol('}')) { // done!
|
|
UngetToken();
|
|
break;
|
|
}
|
|
if (eCSSToken_AtKeyword == mToken.mType) {
|
|
SkipAtRule(aErrorCode); // @media cannot contain @rules
|
|
continue;
|
|
}
|
|
UngetToken();
|
|
ParseRuleSet(aErrorCode);
|
|
}
|
|
PopGroup();
|
|
|
|
if (ExpectSymbol(aErrorCode, '}', PR_TRUE)) {
|
|
rule->SetMedia(media);
|
|
AppendRule(rule);
|
|
NS_RELEASE(rule);
|
|
NS_RELEASE(media);
|
|
return PR_TRUE;
|
|
}
|
|
mSection = holdSection;
|
|
}
|
|
NS_RELEASE(rule);
|
|
}
|
|
else { // failed to create rule, backup and skip block
|
|
UngetToken();
|
|
}
|
|
}
|
|
}
|
|
NS_RELEASE(media);
|
|
}
|
|
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// Parse a CSS3 namespace rule: "@namespace [prefix] STRING | URL;"
|
|
PRBool CSSParserImpl::ParseNameSpaceRule(PRInt32& aErrorCode)
|
|
{
|
|
if (!GetToken(aErrorCode, PR_TRUE)) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
nsAutoString prefix;
|
|
nsAutoString url;
|
|
|
|
if (eCSSToken_Ident == mToken.mType) {
|
|
prefix = mToken.mIdent;
|
|
if (! GetToken(aErrorCode, PR_TRUE)) {
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
|
|
if (eCSSToken_String == mToken.mType) {
|
|
url = mToken.mIdent;
|
|
if (ExpectSymbol(aErrorCode, ';', PR_TRUE)) {
|
|
ProcessNameSpace(aErrorCode, prefix, url);
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
else if ((eCSSToken_Function == mToken.mType) &&
|
|
(mToken.mIdent.EqualsIgnoreCase("url"))) {
|
|
if (ExpectSymbol(aErrorCode, '(', PR_FALSE)) {
|
|
if (GetURLToken(aErrorCode, PR_TRUE)) {
|
|
if ((eCSSToken_String == mToken.mType) || (eCSSToken_URL == mToken.mType)) {
|
|
url = mToken.mIdent;
|
|
if (ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
|
|
if (ExpectSymbol(aErrorCode, ';', PR_TRUE)) {
|
|
ProcessNameSpace(aErrorCode, prefix, url);
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ProcessNameSpace(PRInt32& aErrorCode, const nsString& aPrefix,
|
|
const nsString& aURLSpec)
|
|
{
|
|
PRBool result = PR_FALSE;
|
|
|
|
nsICSSNameSpaceRule* rule = nsnull;
|
|
nsIAtom* prefix = nsnull;
|
|
|
|
if (0 < aPrefix.Length()) {
|
|
prefix = NS_NewAtom(aPrefix);
|
|
}
|
|
|
|
NS_NewCSSNameSpaceRule(&rule, prefix, aURLSpec);
|
|
if (rule) {
|
|
AppendRule(rule);
|
|
NS_RELEASE(rule);
|
|
NS_IF_RELEASE(mNameSpace);
|
|
mSheet->GetNameSpace(mNameSpace);
|
|
}
|
|
|
|
NS_IF_RELEASE(prefix);
|
|
return result;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseFontFaceRule(PRInt32& aErrorCode)
|
|
{
|
|
// XXX not yet implemented
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParsePageRule(PRInt32& aErrorCode)
|
|
{
|
|
// XXX not yet implemented
|
|
return PR_FALSE;
|
|
}
|
|
|
|
void CSSParserImpl::SkipUntil(PRInt32& aErrorCode, PRUnichar aStopSymbol)
|
|
{
|
|
nsCSSToken* tk = &mToken;
|
|
for (;;) {
|
|
if (!GetToken(aErrorCode, PR_TRUE)) {
|
|
break;
|
|
}
|
|
if (eCSSToken_Symbol == tk->mType) {
|
|
PRUnichar symbol = tk->mSymbol;
|
|
if (symbol == aStopSymbol) {
|
|
break;
|
|
} else if ('{' == symbol) {
|
|
SkipUntil(aErrorCode, '}');
|
|
} else if ('[' == symbol) {
|
|
SkipUntil(aErrorCode, ']');
|
|
} else if ('(' == symbol) {
|
|
SkipUntil(aErrorCode, ')');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
PRBool
|
|
CSSParserImpl::SkipDeclaration(PRInt32& aErrorCode, PRBool aCheckForBraces)
|
|
{
|
|
nsCSSToken* tk = &mToken;
|
|
for (;;) {
|
|
if (!GetToken(aErrorCode, PR_TRUE)) {
|
|
return PR_FALSE;
|
|
}
|
|
if (eCSSToken_Symbol == tk->mType) {
|
|
PRUnichar symbol = tk->mSymbol;
|
|
if (';' == symbol) {
|
|
break;
|
|
}
|
|
if (aCheckForBraces) {
|
|
if ('}' == symbol) {
|
|
UngetToken();
|
|
break;
|
|
}
|
|
}
|
|
if ('{' == symbol) {
|
|
SkipUntil(aErrorCode, '}');
|
|
} else if ('(' == symbol) {
|
|
SkipUntil(aErrorCode, ')');
|
|
} else if ('[' == symbol) {
|
|
SkipUntil(aErrorCode, ']');
|
|
}
|
|
}
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
void CSSParserImpl::SkipRuleSet(PRInt32& aErrorCode)
|
|
{
|
|
nsCSSToken* tk = &mToken;
|
|
for (;;) {
|
|
if (!GetToken(aErrorCode, PR_TRUE)) {
|
|
break;
|
|
}
|
|
if (eCSSToken_Symbol == tk->mType) {
|
|
PRUnichar symbol = tk->mSymbol;
|
|
if ('{' == symbol) {
|
|
SkipUntil(aErrorCode, '}');
|
|
break;
|
|
}
|
|
if ('(' == symbol) {
|
|
SkipUntil(aErrorCode, ')');
|
|
} else if ('[' == symbol) {
|
|
SkipUntil(aErrorCode, ']');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
PRBool CSSParserImpl::PushGroup(nsICSSGroupRule* aRule)
|
|
{
|
|
if (! mGroupStack) {
|
|
NS_NewISupportsArray(&mGroupStack);
|
|
}
|
|
if (mGroupStack) {
|
|
mGroupStack->AppendElement(aRule);
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
void CSSParserImpl::PopGroup(void)
|
|
{
|
|
if (mGroupStack) {
|
|
PRUint32 count;
|
|
mGroupStack->Count(&count);
|
|
if (0 < count) {
|
|
mGroupStack->RemoveElementAt(count - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CSSParserImpl::AppendRule(nsICSSRule* aRule)
|
|
{
|
|
PRUint32 count = 0;
|
|
if (mGroupStack) {
|
|
mGroupStack->Count(&count);
|
|
}
|
|
if (0 < count) {
|
|
nsICSSGroupRule* group = (nsICSSGroupRule*)mGroupStack->ElementAt(count - 1);
|
|
group->AppendStyleRule(aRule);
|
|
NS_RELEASE(group);
|
|
}
|
|
else {
|
|
mSheet->AppendStyleRule(aRule);
|
|
}
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseRuleSet(PRInt32& aErrorCode)
|
|
{
|
|
// First get the list of selectors for the rule
|
|
SelectorList* slist = nsnull;
|
|
if (! ParseSelectorList(aErrorCode, slist)) {
|
|
SkipRuleSet(aErrorCode);
|
|
return PR_FALSE;
|
|
}
|
|
NS_ASSERTION(nsnull != slist, "null selector list");
|
|
|
|
// Next parse the declaration block
|
|
nsICSSDeclaration* declaration = ParseDeclarationBlock(aErrorCode, PR_TRUE);
|
|
if (nsnull == declaration) {
|
|
// XXX skip something here
|
|
delete slist;
|
|
return PR_FALSE;
|
|
}
|
|
|
|
#if 0
|
|
slist->Dump();
|
|
fputs("{\n", stdout);
|
|
declaration->List();
|
|
fputs("}\n", stdout);
|
|
#endif
|
|
|
|
// Translate the selector list and declaration block into style data
|
|
|
|
SelectorList* list = slist;
|
|
|
|
while (nsnull != list) {
|
|
nsICSSStyleRule* rule = nsnull;
|
|
NS_NewCSSStyleRule(&rule, *(list->mSelectors));
|
|
if (nsnull != rule) {
|
|
if (nsnull != list->mSelectors->mNext) { // hand off other selectors to new rule
|
|
nsCSSSelector* ruleFirst = rule->FirstSelector();
|
|
ruleFirst->mNext = list->mSelectors->mNext;
|
|
list->mSelectors->mNext = nsnull;
|
|
}
|
|
rule->SetDeclaration(declaration);
|
|
rule->SetWeight(list->mWeight);
|
|
rule->SetSourceSelectorText(list->mSourceString); // capture the original input (need this for namespace prefixes)
|
|
// rule->List();
|
|
AppendRule(rule);
|
|
NS_RELEASE(rule);
|
|
}
|
|
|
|
list = list->mNext;
|
|
}
|
|
|
|
// Release temporary storage
|
|
delete slist;
|
|
NS_RELEASE(declaration);
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseSelectorList(PRInt32& aErrorCode,
|
|
SelectorList*& aListHead)
|
|
{
|
|
SelectorList* list = nsnull;
|
|
if (! ParseSelectorGroup(aErrorCode, list)) {
|
|
// must have at least one selector group
|
|
aListHead = nsnull;
|
|
return PR_FALSE;
|
|
}
|
|
NS_ASSERTION(nsnull != list, "no selector list");
|
|
aListHead = list;
|
|
|
|
// After that there must either be a "," or a "{"
|
|
nsCSSToken* tk = &mToken;
|
|
for (;;) {
|
|
if (! GetToken(aErrorCode, PR_TRUE)) {
|
|
break;
|
|
}
|
|
if (eCSSToken_Symbol != tk->mType) {
|
|
UngetToken();
|
|
break;
|
|
}
|
|
if (',' == tk->mSymbol) {
|
|
SelectorList* newList = nsnull;
|
|
// Another selector group must follow
|
|
if (! ParseSelectorGroup(aErrorCode, newList)) {
|
|
break;
|
|
}
|
|
// add new list to the end of the selector list
|
|
list->mNext = newList;
|
|
list = newList;
|
|
continue;
|
|
} else if ('{' == tk->mSymbol) {
|
|
UngetToken();
|
|
return PR_TRUE;
|
|
} else {
|
|
UngetToken();
|
|
break;
|
|
}
|
|
}
|
|
|
|
delete aListHead;
|
|
aListHead = nsnull;
|
|
return PR_FALSE;
|
|
}
|
|
|
|
static PRBool IsPseudoClass(const nsIAtom* aAtom)
|
|
{
|
|
return PRBool((nsCSSAtoms::activePseudo == aAtom) ||
|
|
(nsCSSAtoms::checkedPseudo == aAtom) ||
|
|
(nsCSSAtoms::disabledPseudo == aAtom) ||
|
|
(nsCSSAtoms::dragOverPseudo == aAtom) ||
|
|
(nsCSSAtoms::dragPseudo == aAtom) ||
|
|
(nsCSSAtoms::enabledPseudo == aAtom) ||
|
|
(nsCSSAtoms::firstChildPseudo == aAtom) ||
|
|
(nsCSSAtoms::focusPseudo == aAtom) ||
|
|
(nsCSSAtoms::hoverPseudo == aAtom) ||
|
|
(nsCSSAtoms::langPseudo == aAtom) ||
|
|
(nsCSSAtoms::linkPseudo == aAtom) ||
|
|
(nsCSSAtoms::outOfDatePseudo == aAtom) ||
|
|
(nsCSSAtoms::visitedPseudo == aAtom));
|
|
}
|
|
|
|
static PRBool IsSinglePseudoClass(const nsCSSSelector& aSelector)
|
|
{
|
|
return PRBool((aSelector.mNameSpace == kNameSpaceID_Unknown) &&
|
|
(aSelector.mTag == nsnull) &&
|
|
(aSelector.mID == nsnull) &&
|
|
(aSelector.mClassList == nsnull) &&
|
|
(aSelector.mAttrList == nsnull) &&
|
|
(aSelector.mPseudoClassList != nsnull) &&
|
|
(aSelector.mPseudoClassList->mNext == nsnull));
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseSelectorGroup(PRInt32& aErrorCode,
|
|
SelectorList*& aList)
|
|
{
|
|
nsAutoString sourceBuffer;
|
|
SelectorList* list = nsnull;
|
|
PRUnichar combinator = PRUnichar(0);
|
|
PRInt32 weight = 0;
|
|
PRBool havePseudoElement = PR_FALSE;
|
|
for (;;) {
|
|
nsCSSSelector selector;
|
|
if (! ParseSelector(aErrorCode, selector, sourceBuffer)) {
|
|
break;
|
|
}
|
|
if (nsnull == list) {
|
|
list = new SelectorList();
|
|
if (nsnull == list) {
|
|
aErrorCode = NS_ERROR_OUT_OF_MEMORY;
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
sourceBuffer.Append(PRUnichar(' '));
|
|
list->AddSelector(selector);
|
|
nsCSSSelector* listSel = list->mSelectors;
|
|
|
|
// pull out pseudo elements here
|
|
nsAtomList* prevList = nsnull;
|
|
nsAtomList* pseudoClassList = listSel->mPseudoClassList;
|
|
while (nsnull != pseudoClassList) {
|
|
if (! IsPseudoClass(pseudoClassList->mAtom)) {
|
|
havePseudoElement = PR_TRUE;
|
|
if (IsSinglePseudoClass(*listSel)) { // convert to pseudo element selector
|
|
nsIAtom* pseudoElement = pseudoClassList->mAtom; // steal ref count
|
|
pseudoClassList->mAtom = nsnull;
|
|
listSel->Reset();
|
|
if (listSel->mNext) {// more to the selector
|
|
listSel->mOperator = PRUnichar('>');
|
|
nsCSSSelector empty;
|
|
list->AddSelector(empty); // leave a blank (universal) selector in the middle
|
|
listSel = list->mSelectors; // use the new one for the pseudo
|
|
}
|
|
listSel->mTag = pseudoElement;
|
|
}
|
|
else { // append new pseudo element selector
|
|
selector.Reset();
|
|
selector.mTag = pseudoClassList->mAtom; // steal ref count
|
|
list->AddSelector(selector);
|
|
pseudoClassList->mAtom = nsnull;
|
|
listSel->mOperator = PRUnichar('>');
|
|
if (nsnull == prevList) { // delete list entry
|
|
listSel->mPseudoClassList = pseudoClassList->mNext;
|
|
}
|
|
else {
|
|
prevList->mNext = pseudoClassList->mNext;
|
|
}
|
|
pseudoClassList->mNext = nsnull;
|
|
delete pseudoClassList;
|
|
weight += listSel->CalcWeight(); // capture weight from remainder
|
|
}
|
|
break; // only one pseudo element per selector
|
|
}
|
|
prevList = pseudoClassList;
|
|
pseudoClassList = pseudoClassList->mNext;
|
|
}
|
|
|
|
combinator = PRUnichar(0);
|
|
if (GetToken(aErrorCode, PR_TRUE)) {
|
|
if ((eCSSToken_Symbol == mToken.mType) &&
|
|
(('+' == mToken.mSymbol) || ('>' == mToken.mSymbol))) {
|
|
combinator = mToken.mSymbol;
|
|
list->mSelectors->SetOperator(combinator);
|
|
sourceBuffer.Append(combinator);
|
|
sourceBuffer.Append(PRUnichar(' '));
|
|
}
|
|
else {
|
|
UngetToken(); // give it back to selector
|
|
}
|
|
}
|
|
|
|
if (havePseudoElement) {
|
|
break;
|
|
}
|
|
else {
|
|
weight += selector.CalcWeight();
|
|
}
|
|
}
|
|
if (PRUnichar(0) != combinator) { // no dangling combinators
|
|
if (list) {
|
|
delete list;
|
|
}
|
|
list = nsnull;
|
|
}
|
|
aList = list;
|
|
if (nsnull != list) {
|
|
sourceBuffer.Truncate(sourceBuffer.Length() - 1); // kill trailing space
|
|
list->mSourceString = sourceBuffer;
|
|
list->mWeight = weight;
|
|
}
|
|
return PRBool(nsnull != aList);
|
|
}
|
|
|
|
#define SEL_MASK_NSPACE 0x01
|
|
#define SEL_MASK_ELEM 0x02
|
|
#define SEL_MASK_ID 0x04
|
|
#define SEL_MASK_CLASS 0x08
|
|
#define SEL_MASK_ATTRIB 0x10
|
|
#define SEL_MASK_PCLASS 0x20
|
|
#define SEL_MASK_PELEM 0x40
|
|
|
|
/**
|
|
* This is the format for selectors:
|
|
* operator? [[namespace |]? element_name]? [ ID | class | attrib | pseudo ]*
|
|
*/
|
|
PRBool CSSParserImpl::ParseSelector(PRInt32& aErrorCode,
|
|
nsCSSSelector& aSelector,
|
|
nsString& aSource)
|
|
{
|
|
PRInt32 dataMask = 0;
|
|
nsAutoString buffer;
|
|
|
|
if (! GetToken(aErrorCode, PR_TRUE)) {
|
|
return PR_FALSE;
|
|
}
|
|
if (mToken.IsSymbol('*')) { // universal element selector, or universal namespace
|
|
mToken.AppendToString(aSource);
|
|
if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) { // was namespace
|
|
mToken.AppendToString(aSource);
|
|
dataMask |= SEL_MASK_NSPACE;
|
|
aSelector.SetNameSpace(kNameSpaceID_Unknown); // namespace wildcard
|
|
|
|
if (! GetToken(aErrorCode, PR_FALSE)) {
|
|
return PR_FALSE;
|
|
}
|
|
mToken.AppendToString(aSource);
|
|
if (eCSSToken_Ident == mToken.mType) { // element name
|
|
dataMask |= SEL_MASK_ELEM;
|
|
if (mCaseSensitive) {
|
|
aSelector.SetTag(mToken.mIdent);
|
|
}
|
|
else {
|
|
mToken.mIdent.ToLowerCase(buffer);
|
|
aSelector.SetTag(buffer);
|
|
}
|
|
}
|
|
else if (mToken.IsSymbol('*')) { // universal selector
|
|
dataMask |= SEL_MASK_ELEM;
|
|
// don't set tag
|
|
}
|
|
else {
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
else { // was universal element selector
|
|
aSelector.SetNameSpace(kNameSpaceID_Unknown); // wildcard
|
|
if (mNameSpace) { // look for default namespace
|
|
nsINameSpace* defaultNameSpace = nsnull;
|
|
mNameSpace->FindNameSpace(nsnull, defaultNameSpace);
|
|
if (defaultNameSpace) {
|
|
PRInt32 defaultID;
|
|
defaultNameSpace->GetNameSpaceID(defaultID);
|
|
aSelector.SetNameSpace(defaultID);
|
|
NS_RELEASE(defaultNameSpace);
|
|
}
|
|
}
|
|
dataMask |= SEL_MASK_ELEM;
|
|
// don't set any tag in the selector
|
|
}
|
|
if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
else if (eCSSToken_Ident == mToken.mType) { // element name or namespace name
|
|
mToken.AppendToString(aSource);
|
|
buffer = mToken.mIdent; // hang on to ident
|
|
|
|
if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) { // was namespace
|
|
mToken.AppendToString(aSource);
|
|
dataMask |= SEL_MASK_NSPACE;
|
|
PRInt32 nameSpaceID = kNameSpaceID_Unknown;
|
|
if (mNameSpace) {
|
|
nsIAtom* prefix;
|
|
if (mCaseSensitive) {
|
|
prefix = NS_NewAtom(buffer);
|
|
}
|
|
else {
|
|
buffer.ToLowerCase();
|
|
prefix = NS_NewAtom(buffer);
|
|
}
|
|
mNameSpace->FindNameSpaceID(prefix, nameSpaceID);
|
|
NS_IF_RELEASE(prefix);
|
|
} // else, no delcared namespaces
|
|
if (kNameSpaceID_Unknown == nameSpaceID) { // unknown prefix, dump it
|
|
return PR_FALSE;
|
|
}
|
|
aSelector.SetNameSpace(nameSpaceID);
|
|
|
|
if (! GetToken(aErrorCode, PR_FALSE)) {
|
|
return PR_FALSE;
|
|
}
|
|
mToken.AppendToString(aSource);
|
|
if (eCSSToken_Ident == mToken.mType) { // element name
|
|
dataMask |= SEL_MASK_ELEM;
|
|
if (mCaseSensitive) {
|
|
aSelector.SetTag(mToken.mIdent);
|
|
}
|
|
else {
|
|
mToken.mIdent.ToLowerCase(buffer);
|
|
aSelector.SetTag(buffer);
|
|
}
|
|
}
|
|
else if (mToken.IsSymbol('*')) { // universal selector
|
|
dataMask |= SEL_MASK_ELEM;
|
|
// don't set tag
|
|
}
|
|
else {
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
else { // was element name
|
|
aSelector.SetNameSpace(kNameSpaceID_Unknown); // wildcard
|
|
if (mNameSpace) { // look for default namespace
|
|
nsINameSpace* defaultNameSpace = nsnull;
|
|
mNameSpace->FindNameSpace(nsnull, defaultNameSpace);
|
|
if (defaultNameSpace) {
|
|
PRInt32 defaultID;
|
|
defaultNameSpace->GetNameSpaceID(defaultID);
|
|
aSelector.SetNameSpace(defaultID);
|
|
NS_RELEASE(defaultNameSpace);
|
|
}
|
|
}
|
|
if (mCaseSensitive) {
|
|
aSelector.SetTag(buffer);
|
|
}
|
|
else {
|
|
buffer.ToLowerCase();
|
|
aSelector.SetTag(buffer);
|
|
}
|
|
dataMask |= SEL_MASK_ELEM;
|
|
}
|
|
if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
else if (mToken.IsSymbol('|')) { // No namespace
|
|
mToken.AppendToString(aSource);
|
|
dataMask |= SEL_MASK_NSPACE;
|
|
aSelector.SetNameSpace(kNameSpaceID_None); // explicit NO namespace
|
|
|
|
// get mandatory tag
|
|
if (! GetToken(aErrorCode, PR_FALSE)) {
|
|
return PR_FALSE;
|
|
}
|
|
mToken.AppendToString(aSource);
|
|
if (eCSSToken_Ident == mToken.mType) { // element name
|
|
dataMask |= SEL_MASK_ELEM;
|
|
if (mCaseSensitive) {
|
|
aSelector.SetTag(mToken.mIdent);
|
|
}
|
|
else {
|
|
mToken.mIdent.ToLowerCase(buffer);
|
|
aSelector.SetTag(buffer);
|
|
}
|
|
}
|
|
else if (mToken.IsSymbol('*')) { // universal selector
|
|
dataMask |= SEL_MASK_ELEM;
|
|
// don't set tag
|
|
}
|
|
else {
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
for (;;) {
|
|
if (eCSSToken_ID == mToken.mType) { // #id
|
|
if ((0 == (dataMask & SEL_MASK_ID)) && // only one ID
|
|
(0 < mToken.mIdent.Length())) { // verify is legal ID
|
|
mToken.AppendToString(aSource);
|
|
dataMask |= SEL_MASK_ID;
|
|
aSelector.SetID(mToken.mIdent);
|
|
}
|
|
else {
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
else if (mToken.IsSymbol('.')) { // .class
|
|
mToken.AppendToString(aSource);
|
|
if (! GetToken(aErrorCode, PR_FALSE)) { // get ident
|
|
return PR_FALSE;
|
|
}
|
|
if (eCSSToken_Ident != mToken.mType) { // malformed selector (XXX what about leading digits?)
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
mToken.AppendToString(aSource);
|
|
dataMask |= SEL_MASK_CLASS;
|
|
aSelector.AddClass(mToken.mIdent); // class always case sensitive
|
|
}
|
|
else if (mToken.IsSymbol(':')) { // :pseudo
|
|
mToken.AppendToString(aSource);
|
|
if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof
|
|
return PR_FALSE;
|
|
}
|
|
if (eCSSToken_Ident != mToken.mType) { // malformed selector
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
mToken.AppendToString(aSource);
|
|
buffer.Truncate();
|
|
buffer.Append(':');
|
|
buffer.Append(mToken.mIdent);
|
|
buffer.ToLowerCase();
|
|
nsIAtom* pseudo = NS_NewAtom(buffer);
|
|
if (IsPseudoClass(pseudo)) {
|
|
// XXX parse lang pseudo class
|
|
dataMask |= SEL_MASK_PCLASS;
|
|
aSelector.AddPseudoClass(pseudo);
|
|
}
|
|
else {
|
|
if (0 == (dataMask & SEL_MASK_PELEM)) {
|
|
dataMask |= SEL_MASK_PELEM;
|
|
aSelector.AddPseudoClass(pseudo); // store it here, it gets pulled later
|
|
|
|
// ensure selector ends here, must be followed by EOF, space, '{' or ','
|
|
if (GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
|
|
if ((eCSSToken_WhiteSpace == mToken.mType) ||
|
|
(mToken.IsSymbol('{') || mToken.IsSymbol(','))) {
|
|
UngetToken();
|
|
return PR_TRUE;
|
|
}
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
else { // multiple pseudo elements, not legal
|
|
UngetToken();
|
|
NS_RELEASE(pseudo);
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
NS_RELEASE(pseudo);
|
|
}
|
|
else if (mToken.IsSymbol('[')) { // attribute
|
|
mToken.AppendToString(aSource);
|
|
if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF
|
|
return PR_FALSE;
|
|
}
|
|
mToken.AppendToString(aSource);
|
|
|
|
PRInt32 nameSpaceID = kNameSpaceID_None;
|
|
nsAutoString attr;
|
|
if (mToken.IsSymbol('*')) { // wildcard namespace
|
|
nameSpaceID = kNameSpaceID_Unknown;
|
|
if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) {
|
|
mToken.AppendToString(aSource);
|
|
if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF
|
|
return PR_FALSE;
|
|
}
|
|
mToken.AppendToString(aSource);
|
|
if (eCSSToken_Ident == mToken.mType) { // attr name
|
|
attr = mToken.mIdent;
|
|
}
|
|
else {
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
else {
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
else if (mToken.IsSymbol('|')) { // NO namespace
|
|
if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF
|
|
return PR_FALSE;
|
|
}
|
|
mToken.AppendToString(aSource);
|
|
if (eCSSToken_Ident == mToken.mType) { // attr name
|
|
attr = mToken.mIdent;
|
|
}
|
|
else {
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
else if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
|
|
attr = mToken.mIdent; // hang on to it
|
|
if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) { // was a namespace
|
|
mToken.AppendToString(aSource);
|
|
nameSpaceID = kNameSpaceID_Unknown;
|
|
if (mNameSpace) {
|
|
nsIAtom* prefix;
|
|
if (mCaseSensitive) {
|
|
prefix = NS_NewAtom(attr);
|
|
}
|
|
else {
|
|
attr.ToLowerCase();
|
|
prefix = NS_NewAtom(attr);
|
|
}
|
|
mNameSpace->FindNameSpaceID(prefix, nameSpaceID);
|
|
NS_IF_RELEASE(prefix);
|
|
} // else, no delcared namespaces
|
|
if (kNameSpaceID_Unknown == nameSpaceID) { // unknown prefix, dump it
|
|
return PR_FALSE;
|
|
}
|
|
if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF
|
|
return PR_FALSE;
|
|
}
|
|
mToken.AppendToString(aSource);
|
|
if (eCSSToken_Ident == mToken.mType) { // attr name
|
|
attr = mToken.mIdent;
|
|
}
|
|
else {
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
}
|
|
else { // malformed
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
|
|
if (! mCaseSensitive) {
|
|
attr.ToLowerCase();
|
|
}
|
|
if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF
|
|
return PR_FALSE;
|
|
}
|
|
if (eCSSToken_Symbol == mToken.mType) {
|
|
mToken.AppendToString(aSource);
|
|
PRUint8 func;
|
|
if (']' == mToken.mSymbol) {
|
|
dataMask |= SEL_MASK_ATTRIB;
|
|
aSelector.AddAttribute(nameSpaceID, attr);
|
|
func = NS_ATTR_FUNC_SET;
|
|
}
|
|
else if ('=' == mToken.mSymbol) {
|
|
func = NS_ATTR_FUNC_EQUALS;
|
|
}
|
|
else if ('~' == mToken.mSymbol) {
|
|
if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF
|
|
return PR_FALSE;
|
|
}
|
|
mToken.AppendToString(aSource);
|
|
if (mToken.IsSymbol('=')) {
|
|
func = NS_ATTR_FUNC_INCLUDES;
|
|
}
|
|
else {
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
else if ('|' == mToken.mSymbol) {
|
|
if (! GetToken(aErrorCode, PR_FALSE)) { // premature EOF
|
|
return PR_FALSE;
|
|
}
|
|
mToken.AppendToString(aSource);
|
|
if (mToken.IsSymbol('=')) {
|
|
func = NS_ATTR_FUNC_DASHMATCH;
|
|
}
|
|
else {
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
else {
|
|
UngetToken(); // bad function
|
|
return PR_FALSE;
|
|
}
|
|
if (NS_ATTR_FUNC_SET != func) { // get value
|
|
if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF
|
|
return PR_FALSE;
|
|
}
|
|
if ((eCSSToken_Ident == mToken.mType) || (eCSSToken_String == mToken.mType)) {
|
|
mToken.AppendToString(aSource);
|
|
nsAutoString value(mToken.mIdent);
|
|
if (! GetToken(aErrorCode, PR_TRUE)) { // premature EOF
|
|
return PR_FALSE;
|
|
}
|
|
if (mToken.IsSymbol(']')) {
|
|
mToken.AppendToString(aSource);
|
|
dataMask |= SEL_MASK_ATTRIB;
|
|
aSelector.AddAttribute(nameSpaceID, attr, func, value, mCaseSensitive);
|
|
}
|
|
else {
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
else {
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
UngetToken(); // bad dog, no biscut!
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
else { // not a selector token, we're done
|
|
break;
|
|
}
|
|
|
|
if (! GetToken(aErrorCode, PR_FALSE)) { // premature eof is ok (here!)
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
UngetToken();
|
|
return PRBool(0 != dataMask);
|
|
}
|
|
|
|
nsICSSDeclaration*
|
|
CSSParserImpl::ParseDeclarationBlock(PRInt32& aErrorCode,
|
|
PRBool aCheckForBraces)
|
|
{
|
|
if (aCheckForBraces) {
|
|
if (!ExpectSymbol(aErrorCode, '{', PR_TRUE)) {
|
|
return nsnull;
|
|
}
|
|
}
|
|
nsICSSDeclaration* declaration = nsnull;
|
|
if (NS_OK == NS_NewCSSDeclaration(&declaration)) {
|
|
PRInt32 count = 0;
|
|
for (;;) {
|
|
PRInt32 hint;
|
|
if (ParseDeclaration(aErrorCode, declaration, aCheckForBraces, hint)) {
|
|
count++; // count declarations
|
|
}
|
|
else {
|
|
if (!SkipDeclaration(aErrorCode, aCheckForBraces)) {
|
|
break;
|
|
}
|
|
if (aCheckForBraces) {
|
|
if (ExpectSymbol(aErrorCode, '}', PR_TRUE)) {
|
|
break;
|
|
}
|
|
}
|
|
// Since the skipped declaration didn't end the block we parse
|
|
// the next declaration.
|
|
}
|
|
}
|
|
if (0 == count) { // didn't get any XXX is this ok with the DOM?
|
|
NS_RELEASE(declaration);
|
|
}
|
|
}
|
|
return declaration;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseColor(PRInt32& aErrorCode, nsCSSValue& aValue)
|
|
{
|
|
if (!GetToken(aErrorCode, PR_TRUE)) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
nsCSSToken* tk = &mToken;
|
|
nscolor rgba;
|
|
switch (tk->mType) {
|
|
case eCSSToken_ID:
|
|
// #xxyyzz
|
|
if (NS_HexToRGB(tk->mIdent, &rgba)) {
|
|
aValue.SetColorValue(rgba);
|
|
return PR_TRUE;
|
|
}
|
|
break;
|
|
|
|
case eCSSToken_Ident:
|
|
if (NS_ColorNameToRGB(tk->mIdent, &rgba)) {
|
|
aValue.SetStringValue(tk->mIdent, eCSSUnit_String);
|
|
return PR_TRUE;
|
|
}
|
|
break;
|
|
case eCSSToken_Function:
|
|
if (mToken.mIdent.EqualsIgnoreCase("rgb")) {
|
|
// rgb ( component , component , component )
|
|
PRUint8 r, g, b;
|
|
if (ExpectSymbol(aErrorCode, '(', PR_FALSE) &&
|
|
ParseColorComponent(aErrorCode, r, ',') &&
|
|
ParseColorComponent(aErrorCode, g, ',') &&
|
|
ParseColorComponent(aErrorCode, b, ')')) {
|
|
rgba = NS_RGB(r,g,b);
|
|
aValue.SetColorValue(rgba);
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE; // already pushed back
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// It's not a color
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseColorComponent(PRInt32& aErrorCode,
|
|
PRUint8& aComponent,
|
|
char aStop)
|
|
{
|
|
if (!GetToken(aErrorCode, PR_TRUE)) {
|
|
return PR_FALSE;
|
|
}
|
|
float value;
|
|
nsCSSToken* tk = &mToken;
|
|
switch (tk->mType) {
|
|
case eCSSToken_Number:
|
|
value = tk->mNumber;
|
|
break;
|
|
case eCSSToken_Percentage:
|
|
value = tk->mNumber * 255.0f;
|
|
break;
|
|
default:
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
if (ExpectSymbol(aErrorCode, aStop, PR_TRUE)) {
|
|
if (value < 0.0f) value = 0.0f;
|
|
if (value > 255.0f) value = 255.0f;
|
|
aComponent = (PRUint8) value;
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
PRBool
|
|
CSSParserImpl::ParseDeclaration(PRInt32& aErrorCode,
|
|
nsICSSDeclaration* aDeclaration,
|
|
PRBool aCheckForBraces,
|
|
PRInt32& aChangeHint)
|
|
{
|
|
aChangeHint = NS_STYLE_HINT_UNKNOWN;
|
|
|
|
// Get property name
|
|
nsCSSToken* tk = &mToken;
|
|
nsAutoString propertyName;
|
|
for (;;) {
|
|
if (!GetToken(aErrorCode, PR_TRUE)) {
|
|
return PR_FALSE;
|
|
}
|
|
if (eCSSToken_Ident == tk->mType) {
|
|
propertyName = tk->mIdent;
|
|
// grab the ident before the ExpectSymbol trashes the token
|
|
if (!ExpectSymbol(aErrorCode, ':', PR_TRUE)) {
|
|
return PR_FALSE;
|
|
}
|
|
break;
|
|
}
|
|
if (tk->IsSymbol(';')) {
|
|
// dangling semicolons are skipped
|
|
continue;
|
|
}
|
|
|
|
// Not a declaration...
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// Map property name to it's ID and then parse the property
|
|
nsCSSProperty propID = nsCSSProps::LookupProperty(propertyName);
|
|
if (eCSSProperty_UNKNOWN == propID) { // unknown property
|
|
return PR_FALSE;
|
|
}
|
|
if (! ParseProperty(aErrorCode, aDeclaration, propID, aChangeHint)) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// See if the declaration is followed by a "!important" declaration
|
|
PRBool isImportant = PR_FALSE;
|
|
if (!GetToken(aErrorCode, PR_TRUE)) {
|
|
if (aCheckForBraces) {
|
|
// Premature eof is not ok when proper termination is mandated
|
|
return PR_FALSE;
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
else {
|
|
if (eCSSToken_Symbol == tk->mType) {
|
|
if ('!' == tk->mSymbol) {
|
|
// Look for important ident
|
|
if (!GetToken(aErrorCode, PR_TRUE)) {
|
|
// Premature eof is not ok
|
|
return PR_FALSE;
|
|
}
|
|
if ((eCSSToken_Ident != tk->mType) ||
|
|
!tk->mIdent.EqualsIgnoreCase("important")) {
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
isImportant = PR_TRUE;
|
|
}
|
|
else {
|
|
// Not a !important declaration
|
|
UngetToken();
|
|
}
|
|
}
|
|
else {
|
|
// Not a !important declaration
|
|
UngetToken();
|
|
}
|
|
}
|
|
|
|
if (PR_TRUE == isImportant) {
|
|
aDeclaration->SetValueImportant(propID);
|
|
}
|
|
|
|
// Make sure valid property declaration is terminated with either a
|
|
// semicolon or a right-curly-brace (when aCheckForBraces is true).
|
|
// When aCheckForBraces is false, proper termination is either
|
|
// semicolon or EOF.
|
|
if (!GetToken(aErrorCode, PR_TRUE)) {
|
|
if (aCheckForBraces) {
|
|
// Premature eof is not ok
|
|
return PR_FALSE;
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
if (eCSSToken_Symbol == tk->mType) {
|
|
if (';' == tk->mSymbol) {
|
|
return PR_TRUE;
|
|
}
|
|
if (!aCheckForBraces) {
|
|
// If we didn't hit eof and we didn't see a semicolon then the
|
|
// declaration is not properly terminated.
|
|
return PR_FALSE;
|
|
}
|
|
if ('}' == tk->mSymbol) {
|
|
UngetToken();
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// Flags for ParseVariant method
|
|
#define VARIANT_KEYWORD 0x000001 // K
|
|
#define VARIANT_LENGTH 0x000002 // L
|
|
#define VARIANT_PERCENT 0x000004 // P
|
|
#define VARIANT_COLOR 0x000008 // C
|
|
#define VARIANT_URL 0x000010 // U
|
|
#define VARIANT_NUMBER 0x000020 // N
|
|
#define VARIANT_INTEGER 0x000040 // I
|
|
#define VARIANT_ANGLE 0x000080 // G
|
|
#define VARIANT_FREQUENCY 0x000100 // F
|
|
#define VARIANT_TIME 0x000200 // T
|
|
#define VARIANT_STRING 0x000400 // S
|
|
#define VARIANT_COUNTER 0x000800 //
|
|
#define VARIANT_ATTR 0x001000 //
|
|
#define VARIANT_IDENTIFIER 0x002000 // D
|
|
#define VARIANT_AUTO 0x010000 // A
|
|
#define VARIANT_INHERIT 0x020000 // H
|
|
#define VARIANT_NONE 0x040000 // O
|
|
#define VARIANT_NORMAL 0x080000 // M
|
|
|
|
// Common combinations of variants
|
|
#define VARIANT_AL (VARIANT_AUTO | VARIANT_LENGTH)
|
|
#define VARIANT_LP (VARIANT_LENGTH | VARIANT_PERCENT)
|
|
#define VARIANT_AH (VARIANT_AUTO | VARIANT_INHERIT)
|
|
#define VARIANT_AHLP (VARIANT_AH | VARIANT_LP)
|
|
#define VARIANT_AHI (VARIANT_AH | VARIANT_INTEGER)
|
|
#define VARIANT_AHK (VARIANT_AH | VARIANT_KEYWORD)
|
|
#define VARIANT_AHUK (VARIANT_AH | VARIANT_URL | VARIANT_KEYWORD)
|
|
#define VARIANT_AHL (VARIANT_AH | VARIANT_LENGTH)
|
|
#define VARIANT_AHKL (VARIANT_AHK | VARIANT_LENGTH)
|
|
#define VARIANT_HK (VARIANT_INHERIT | VARIANT_KEYWORD)
|
|
#define VARIANT_HKF (VARIANT_HK | VARIANT_FREQUENCY)
|
|
#define VARIANT_HKL (VARIANT_HK | VARIANT_LENGTH)
|
|
#define VARIANT_HKLP (VARIANT_HK | VARIANT_LP)
|
|
#define VARIANT_HL (VARIANT_INHERIT | VARIANT_LENGTH)
|
|
#define VARIANT_HI (VARIANT_INHERIT | VARIANT_INTEGER)
|
|
#define VARIANT_HLP (VARIANT_HL | VARIANT_PERCENT)
|
|
#define VARIANT_HLPN (VARIANT_HLP | VARIANT_NUMBER)
|
|
#define VARIANT_HLPO (VARIANT_HLP | VARIANT_NONE)
|
|
#define VARIANT_HTP (VARIANT_INHERIT | VARIANT_TIME | VARIANT_PERCENT)
|
|
#define VARIANT_HMK (VARIANT_HK | VARIANT_NORMAL)
|
|
#define VARIANT_HMKI (VARIANT_HMK | VARIANT_INTEGER)
|
|
#define VARIANT_HC (VARIANT_INHERIT | VARIANT_COLOR)
|
|
#define VARIANT_HCK (VARIANT_HK | VARIANT_COLOR)
|
|
#define VARIANT_HUO (VARIANT_INHERIT | VARIANT_URL | VARIANT_NONE)
|
|
#define VARIANT_AHUO (VARIANT_AUTO | VARIANT_HUO)
|
|
#define VARIANT_HPN (VARIANT_INHERIT | VARIANT_PERCENT | VARIANT_NUMBER)
|
|
#define VARIANT_HOK (VARIANT_HK | VARIANT_NONE)
|
|
#define VARIANT_HN (VARIANT_INHERIT | VARIANT_NUMBER)
|
|
#define VARIANT_HON (VARIANT_HN | VARIANT_NONE)
|
|
#define VARIANT_HOS (VARIANT_INHERIT | VARIANT_NONE | VARIANT_STRING)
|
|
|
|
static const nsCSSProperty kBorderTopIDs[] = {
|
|
eCSSProperty_border_top_width,
|
|
eCSSProperty_border_top_style,
|
|
eCSSProperty_border_top_color
|
|
};
|
|
static const nsCSSProperty kBorderRightIDs[] = {
|
|
eCSSProperty_border_right_width,
|
|
eCSSProperty_border_right_style,
|
|
eCSSProperty_border_right_color
|
|
};
|
|
static const nsCSSProperty kBorderBottomIDs[] = {
|
|
eCSSProperty_border_bottom_width,
|
|
eCSSProperty_border_bottom_style,
|
|
eCSSProperty_border_bottom_color
|
|
};
|
|
static const nsCSSProperty kBorderLeftIDs[] = {
|
|
eCSSProperty_border_left_width,
|
|
eCSSProperty_border_left_style,
|
|
eCSSProperty_border_left_color
|
|
};
|
|
|
|
PRInt32 CSSParserImpl::SearchKeywordTable(nsCSSKeyword aKeyword, const PRInt32 aKeywordTable[])
|
|
{
|
|
PRInt32 index = 0;
|
|
while (0 <= aKeywordTable[index]) {
|
|
if (aKeyword == nsCSSKeyword(aKeywordTable[index++])) {
|
|
return index;
|
|
}
|
|
index++;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseEnum(PRInt32& aErrorCode, nsCSSValue& aValue,
|
|
const PRInt32 aKeywordTable[])
|
|
{
|
|
nsString* ident = NextIdent(aErrorCode);
|
|
if (nsnull == ident) {
|
|
return PR_FALSE;
|
|
}
|
|
nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(*ident);
|
|
if (eCSSKeyword_UNKNOWN < keyword) {
|
|
PRInt32 index = SearchKeywordTable(keyword, aKeywordTable);
|
|
if (0 < index) {
|
|
aValue.SetIntValue(aKeywordTable[index], eCSSUnit_Enumerated);
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
|
|
// Put the unknown identifier back and return
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::TranslateDimension(nsCSSValue& aValue,
|
|
PRInt32 aVariantMask,
|
|
float aNumber,
|
|
const nsString& aUnit)
|
|
{
|
|
nsCSSUnit units;
|
|
PRInt32 type = 0;
|
|
if (0 != aUnit.Length()) {
|
|
nsCSSKeyword id = nsCSSKeywords::LookupKeyword(aUnit);
|
|
switch (id) {
|
|
case eCSSKeyword_em: units = eCSSUnit_EM; type = VARIANT_LENGTH; break;
|
|
case eCSSKeyword_ex: units = eCSSUnit_XHeight; type = VARIANT_LENGTH; break;
|
|
case eCSSKeyword_ch: units = eCSSUnit_Char; type = VARIANT_LENGTH; break;
|
|
case eCSSKeyword_px: units = eCSSUnit_Pixel; type = VARIANT_LENGTH; break;
|
|
case eCSSKeyword_in: units = eCSSUnit_Inch; type = VARIANT_LENGTH; break;
|
|
case eCSSKeyword_cm: units = eCSSUnit_Centimeter; type = VARIANT_LENGTH; break;
|
|
case eCSSKeyword_mm: units = eCSSUnit_Millimeter; type = VARIANT_LENGTH; break;
|
|
case eCSSKeyword_pt: units = eCSSUnit_Point; type = VARIANT_LENGTH; break;
|
|
case eCSSKeyword_pc: units = eCSSUnit_Pica; type = VARIANT_LENGTH; break;
|
|
|
|
case eCSSKeyword_deg: units = eCSSUnit_Degree; type = VARIANT_ANGLE; break;
|
|
case eCSSKeyword_grad: units = eCSSUnit_Grad; type = VARIANT_ANGLE; break;
|
|
case eCSSKeyword_rad: units = eCSSUnit_Radian; type = VARIANT_ANGLE; break;
|
|
|
|
case eCSSKeyword_hz: units = eCSSUnit_Hertz; type = VARIANT_FREQUENCY; break;
|
|
case eCSSKeyword_khz: units = eCSSUnit_Kilohertz; type = VARIANT_FREQUENCY; break;
|
|
|
|
case eCSSKeyword_s: units = eCSSUnit_Seconds; type = VARIANT_TIME; break;
|
|
case eCSSKeyword_ms: units = eCSSUnit_Milliseconds; type = VARIANT_TIME; break;
|
|
default:
|
|
// unknown unit
|
|
return PR_FALSE;
|
|
}
|
|
} else {
|
|
// Must be a zero number...
|
|
NS_ASSERTION(0 == aNumber, "numbers without units must be 0");
|
|
if ((VARIANT_LENGTH & aVariantMask) != 0) {
|
|
units = eCSSUnit_Point;
|
|
type = VARIANT_LENGTH;
|
|
}
|
|
else if ((VARIANT_ANGLE & aVariantMask) != 0) {
|
|
units = eCSSUnit_Degree;
|
|
type = VARIANT_ANGLE;
|
|
}
|
|
else if ((VARIANT_FREQUENCY & aVariantMask) != 0) {
|
|
units = eCSSUnit_Hertz;
|
|
type = VARIANT_FREQUENCY;
|
|
}
|
|
else if ((VARIANT_TIME & aVariantMask) != 0) {
|
|
units = eCSSUnit_Seconds;
|
|
type = VARIANT_TIME;
|
|
}
|
|
}
|
|
if ((type & aVariantMask) != 0) {
|
|
aValue.SetFloatValue(aNumber, units);
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParsePositiveVariant(PRInt32& aErrorCode,
|
|
nsCSSValue& aValue,
|
|
PRInt32 aVariantMask,
|
|
const PRInt32 aKeywordTable[])
|
|
{
|
|
if (ParseVariant(aErrorCode, aValue, aVariantMask, aKeywordTable)) {
|
|
if (eCSSUnit_Number == aValue.GetUnit() ||
|
|
aValue.IsLengthUnit()){
|
|
if (aValue.GetFloatValue() < 0) {
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
else if(aValue.GetUnit() == eCSSUnit_Percent) {
|
|
if (aValue.GetPercentValue() < 0) {
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseVariant(PRInt32& aErrorCode, nsCSSValue& aValue,
|
|
PRInt32 aVariantMask,
|
|
const PRInt32 aKeywordTable[])
|
|
{
|
|
if (!GetToken(aErrorCode, PR_TRUE)) {
|
|
return PR_FALSE;
|
|
}
|
|
nsCSSToken* tk = &mToken;
|
|
if (((aVariantMask & (VARIANT_AHK | VARIANT_NORMAL | VARIANT_NONE)) != 0) &&
|
|
(eCSSToken_Ident == tk->mType)) {
|
|
nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(tk->mIdent);
|
|
if (eCSSKeyword_UNKNOWN < keyword) { // known keyword
|
|
if ((aVariantMask & VARIANT_AUTO) != 0) {
|
|
if (eCSSKeyword_auto == keyword) {
|
|
aValue.SetAutoValue();
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
if ((aVariantMask & VARIANT_INHERIT) != 0) {
|
|
if (eCSSKeyword_inherit == keyword) {
|
|
aValue.SetInheritValue();
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
if ((aVariantMask & VARIANT_NONE) != 0) {
|
|
if (eCSSKeyword_none == keyword) {
|
|
aValue.SetNoneValue();
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
if ((aVariantMask & VARIANT_NORMAL) != 0) {
|
|
if (eCSSKeyword_normal == keyword) {
|
|
aValue.SetNormalValue();
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
if ((aVariantMask & VARIANT_KEYWORD) != 0) {
|
|
PRInt32 index = SearchKeywordTable(keyword, aKeywordTable);
|
|
if (0 < index) {
|
|
aValue.SetIntValue(aKeywordTable[index], eCSSUnit_Enumerated);
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (((aVariantMask & (VARIANT_LENGTH | VARIANT_ANGLE | VARIANT_FREQUENCY | VARIANT_TIME)) != 0) &&
|
|
tk->IsDimension()) {
|
|
return TranslateDimension(aValue, aVariantMask, tk->mNumber, tk->mIdent);
|
|
}
|
|
if (((aVariantMask & VARIANT_PERCENT) != 0) &&
|
|
(eCSSToken_Percentage == tk->mType)) {
|
|
aValue.SetPercentValue(tk->mNumber);
|
|
return PR_TRUE;
|
|
}
|
|
if (((aVariantMask & VARIANT_NUMBER) != 0) &&
|
|
(eCSSToken_Number == tk->mType)) {
|
|
aValue.SetFloatValue(tk->mNumber, eCSSUnit_Number);
|
|
return PR_TRUE;
|
|
}
|
|
if (((aVariantMask & VARIANT_INTEGER) != 0) &&
|
|
(eCSSToken_Number == tk->mType) && tk->mIntegerValid) {
|
|
aValue.SetIntValue(tk->mInteger, eCSSUnit_Integer);
|
|
return PR_TRUE;
|
|
}
|
|
if (PR_TRUE == mNavQuirkMode) { // NONSTANDARD: Nav interprets unitless numbers as px
|
|
if (((aVariantMask & VARIANT_LENGTH) != 0) &&
|
|
(eCSSToken_Number == tk->mType)) {
|
|
aValue.SetFloatValue(tk->mNumber, eCSSUnit_Pixel);
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
if (((aVariantMask & VARIANT_URL) != 0) &&
|
|
(eCSSToken_Function == tk->mType) &&
|
|
tk->mIdent.EqualsIgnoreCase("url")) {
|
|
if (ParseURL(aErrorCode, aValue)) {
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
if ((aVariantMask & VARIANT_COLOR) != 0) {
|
|
if ((eCSSToken_ID == tk->mType) ||
|
|
(eCSSToken_Ident == tk->mType) ||
|
|
((eCSSToken_Function == tk->mType) &&
|
|
(tk->mIdent.EqualsIgnoreCase("rgb")))) {
|
|
// Put token back so that parse color can get it
|
|
UngetToken();
|
|
if (ParseColor(aErrorCode, aValue)) {
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
if (((aVariantMask & VARIANT_STRING) != 0) &&
|
|
(eCSSToken_String == tk->mType)) {
|
|
nsAutoString buffer;
|
|
buffer.Append(tk->mSymbol);
|
|
buffer.Append(tk->mIdent);
|
|
buffer.Append(tk->mSymbol);
|
|
aValue.SetStringValue(buffer, eCSSUnit_String);
|
|
return PR_TRUE;
|
|
}
|
|
if (((aVariantMask & VARIANT_IDENTIFIER) != 0) &&
|
|
(eCSSToken_Ident == tk->mType)) {
|
|
aValue.SetStringValue(tk->mIdent, eCSSUnit_String);
|
|
return PR_TRUE;
|
|
}
|
|
if (((aVariantMask & VARIANT_COUNTER) != 0) &&
|
|
(eCSSToken_Function == tk->mType) &&
|
|
(tk->mIdent.EqualsIgnoreCase("counter") ||
|
|
tk->mIdent.EqualsIgnoreCase("counters"))) {
|
|
if (ParseCounter(aErrorCode, aValue)) {
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
if (((aVariantMask & VARIANT_ATTR) != 0) &&
|
|
(eCSSToken_Function == tk->mType) &&
|
|
tk->mIdent.EqualsIgnoreCase("attr")) {
|
|
|
|
if (ParseAttr(aErrorCode, aValue)) {
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
|
|
|
|
PRBool CSSParserImpl::ParseCounter(PRInt32& aErrorCode, nsCSSValue& aValue)
|
|
{
|
|
nsCSSUnit unit = (mToken.mIdent.EqualsIgnoreCase("counter") ?
|
|
eCSSUnit_Counter : eCSSUnit_Counters);
|
|
|
|
if (ExpectSymbol(aErrorCode, '(', PR_FALSE)) {
|
|
if (GetToken(aErrorCode, PR_TRUE)) {
|
|
if (eCSSToken_Ident == mToken.mType) {
|
|
nsAutoString counter;
|
|
counter.Append(mToken.mIdent);
|
|
|
|
if (eCSSUnit_Counters == unit) {
|
|
// get mandatory string
|
|
if (! ExpectSymbol(aErrorCode, ',', PR_TRUE)) {
|
|
return PR_FALSE;
|
|
}
|
|
if (GetToken(aErrorCode, PR_TRUE) && (eCSSToken_String == mToken.mType)) {
|
|
counter.Append(',');
|
|
counter.Append(mToken.mSymbol); // quote too
|
|
counter.Append(mToken.mIdent);
|
|
counter.Append(mToken.mSymbol); // quote too
|
|
}
|
|
else {
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
// get optional type
|
|
if (ExpectSymbol(aErrorCode, ',', PR_TRUE)) {
|
|
if (GetToken(aErrorCode, PR_TRUE) && (eCSSToken_Ident == mToken.mType)) {
|
|
nsCSSKeyword keyword = nsCSSKeywords::LookupKeyword(mToken.mIdent);
|
|
if ((eCSSKeyword_UNKNOWN < keyword) &&
|
|
(0 < SearchKeywordTable(keyword, nsCSSProps::kListStyleKTable))) {
|
|
counter.Append(',');
|
|
counter.Append(mToken.mIdent);
|
|
}
|
|
else {
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
else {
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
|
|
if (ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
|
|
aValue.SetStringValue(counter, unit);
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
else {
|
|
UngetToken();
|
|
}
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseAttr(PRInt32& aErrorCode, nsCSSValue& aValue)
|
|
{
|
|
if (ExpectSymbol(aErrorCode, '(', PR_FALSE)) {
|
|
if (GetToken(aErrorCode, PR_TRUE)) {
|
|
nsAutoString attr;
|
|
if (eCSSToken_Ident == mToken.mType) { // attr name or namespace
|
|
nsAutoString holdIdent(mToken.mIdent);
|
|
if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) { // namespace
|
|
PRInt32 nameSpaceID = kNameSpaceID_Unknown;
|
|
if (mNameSpace) {
|
|
nsIAtom* prefix;
|
|
if (mCaseSensitive) {
|
|
prefix = NS_NewAtom(holdIdent);
|
|
}
|
|
else {
|
|
holdIdent.ToLowerCase();
|
|
prefix = NS_NewAtom(holdIdent);
|
|
}
|
|
mNameSpace->FindNameSpaceID(prefix, nameSpaceID);
|
|
NS_IF_RELEASE(prefix);
|
|
} // else, no delcared namespaces
|
|
if (kNameSpaceID_Unknown == nameSpaceID) { // unknown prefix, dump it
|
|
return PR_FALSE;
|
|
}
|
|
attr.Append(nameSpaceID, 10);
|
|
attr.Append('|');
|
|
if (! GetToken(aErrorCode, PR_FALSE)) {
|
|
return PR_FALSE;
|
|
}
|
|
if (eCSSToken_Ident == mToken.mType) {
|
|
attr.Append(mToken.mIdent);
|
|
}
|
|
else {
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
else { // no namespace
|
|
attr = holdIdent;
|
|
}
|
|
}
|
|
else if (mToken.IsSymbol('*')) { // namespace wildcard
|
|
if (ExpectSymbol(aErrorCode, '|', PR_FALSE)) {
|
|
attr.Append(kNameSpaceID_Unknown, 10);
|
|
attr.Append('|');
|
|
if (! GetToken(aErrorCode, PR_FALSE)) {
|
|
return PR_FALSE;
|
|
}
|
|
if (eCSSToken_Ident == mToken.mType) {
|
|
attr.Append(mToken.mIdent);
|
|
}
|
|
else {
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
else {
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
else if (mToken.IsSymbol('|')) { // explicit NO namespace
|
|
if (! GetToken(aErrorCode, PR_FALSE)) {
|
|
return PR_FALSE;
|
|
}
|
|
if (eCSSToken_Ident == mToken.mType) {
|
|
attr.Append(mToken.mIdent);
|
|
}
|
|
else {
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
else {
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
if (ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
|
|
aValue.SetStringValue(attr, eCSSUnit_Attr);
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseURL(PRInt32& aErrorCode, nsCSSValue& aValue)
|
|
{
|
|
if (ExpectSymbol(aErrorCode, '(', PR_FALSE)) {
|
|
if (! GetURLToken(aErrorCode, PR_TRUE)) {
|
|
return PR_FALSE;
|
|
}
|
|
nsCSSToken* tk = &mToken;
|
|
if ((eCSSToken_String == tk->mType) || (eCSSToken_URL == tk->mType)) {
|
|
// Translate url into an absolute url if the url is relative to
|
|
// the style sheet.
|
|
// XXX editors won't like this - too bad for now
|
|
nsAutoString absURL;
|
|
if (nsnull != mURL) {
|
|
nsString baseURL;
|
|
nsresult rv;
|
|
#ifdef NECKO
|
|
nsCOMPtr<nsIURI> base;
|
|
rv = mURL->Clone(getter_AddRefs(base));
|
|
if (NS_SUCCEEDED(rv))
|
|
rv = NS_MakeAbsoluteURI(nsCAutoString(tk->mIdent), base, absURL);
|
|
#else
|
|
rv = NS_MakeAbsoluteURL(mURL, baseURL, tk->mIdent, absURL);
|
|
#endif // NECKO
|
|
if (NS_FAILED(rv)) {
|
|
absURL = tk->mIdent;
|
|
}
|
|
}
|
|
else {
|
|
absURL = tk->mIdent;
|
|
}
|
|
if (ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
|
|
aValue.SetStringValue(absURL, eCSSUnit_URL);
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRInt32 CSSParserImpl::ParseChoice(PRInt32& aErrorCode, nsCSSValue aValues[],
|
|
const nsCSSProperty aPropIDs[], PRInt32 aNumIDs)
|
|
{
|
|
PRInt32 found = 0;
|
|
PRInt32 loop;
|
|
for (loop = 0; loop < aNumIDs; loop++) {
|
|
// Try each property parser in order
|
|
PRInt32 hadFound = found;
|
|
PRInt32 index;
|
|
for (index = 0; index < aNumIDs; index++) {
|
|
PRInt32 bit = 1 << index;
|
|
if ((found & bit) == 0) {
|
|
if (ParseSingleValueProperty(aErrorCode, aValues[index], aPropIDs[index])) {
|
|
found |= bit;
|
|
}
|
|
}
|
|
}
|
|
if (found == hadFound) { // found nothing new
|
|
break;
|
|
}
|
|
}
|
|
if (0 < found) {
|
|
if (1 == found) { // only first property
|
|
if (eCSSUnit_Inherit == aValues[0].GetUnit()) { // one inherit, all inherit
|
|
for (loop = 1; loop < aNumIDs; loop++) {
|
|
aValues[loop].SetInheritValue();
|
|
}
|
|
found = ((1 << aNumIDs) - 1);
|
|
}
|
|
}
|
|
else { // more than one value, verify no inherits
|
|
for (loop = 0; loop < aNumIDs; loop++) {
|
|
if (eCSSUnit_Inherit == aValues[loop].GetUnit()) {
|
|
found = -1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return found;
|
|
}
|
|
|
|
/**
|
|
* Parse a "box" property. Box properties have 1 to 4 values. When less
|
|
* than 4 values are provided a standard mapping is used to replicate
|
|
* existing values.
|
|
*/
|
|
PRBool CSSParserImpl::ParseBoxProperties(PRInt32& aErrorCode,
|
|
nsICSSDeclaration* aDeclaration,
|
|
const nsCSSProperty aPropIDs[])
|
|
{
|
|
// Get up to four values for the property
|
|
nsCSSValue values[4];
|
|
PRInt32 count = 0;
|
|
PRInt32 index;
|
|
for (index = 0; index < 4; index++) {
|
|
if (! ParseSingleValueProperty(aErrorCode, values[index], aPropIDs[index])) {
|
|
break;
|
|
}
|
|
count++;
|
|
}
|
|
if ((count == 0) || (PR_FALSE == ExpectEndProperty(aErrorCode, PR_TRUE))) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
if (1 < count) { // verify no more than single inherit
|
|
for (index = 0; index < 4; index++) {
|
|
if (eCSSUnit_Inherit == values[index].GetUnit()) {
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Provide missing values by replicating some of the values found
|
|
switch (count) {
|
|
case 1: // Make right == top
|
|
values[1] = values[0];
|
|
case 2: // Make bottom == top
|
|
values[2] = values[0];
|
|
case 3: // Make left == right
|
|
values[3] = values[1];
|
|
}
|
|
|
|
for (index = 0; index < 4; index++) {
|
|
aDeclaration->AppendValue(aPropIDs[index], values[index]);
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseProperty(PRInt32& aErrorCode,
|
|
nsICSSDeclaration* aDeclaration,
|
|
nsCSSProperty aPropID,
|
|
PRInt32& aChangeHint)
|
|
{
|
|
// Strip out properties we use internally. These properties are used
|
|
// by compound property parsing routines (e.g. "background-position").
|
|
switch (aPropID) {
|
|
case eCSSProperty_background_x_position:
|
|
case eCSSProperty_background_y_position:
|
|
case eCSSProperty_border_x_spacing:
|
|
case eCSSProperty_border_y_spacing:
|
|
case eCSSProperty_clip_bottom:
|
|
case eCSSProperty_clip_left:
|
|
case eCSSProperty_clip_right:
|
|
case eCSSProperty_clip_top:
|
|
case eCSSProperty_play_during_flags:
|
|
case eCSSProperty_quotes_close:
|
|
case eCSSProperty_quotes_open:
|
|
case eCSSProperty_size_height:
|
|
case eCSSProperty_size_width:
|
|
case eCSSProperty_text_shadow_color:
|
|
case eCSSProperty_text_shadow_radius:
|
|
case eCSSProperty_text_shadow_x:
|
|
case eCSSProperty_text_shadow_y:
|
|
// The user can't use these
|
|
return PR_FALSE;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (aChangeHint < nsCSSProps::kHintTable[aPropID]) {
|
|
aChangeHint = nsCSSProps::kHintTable[aPropID];
|
|
}
|
|
|
|
return ParseProperty(aErrorCode, aDeclaration, aPropID);
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseProperty(PRInt32& aErrorCode,
|
|
nsICSSDeclaration* aDeclaration,
|
|
nsCSSProperty aPropID)
|
|
{
|
|
switch (aPropID) { // handle shorthand or multiple properties
|
|
case eCSSProperty_background:
|
|
return ParseBackground(aErrorCode, aDeclaration);
|
|
case eCSSProperty_background_position:
|
|
return ParseBackgroundPosition(aErrorCode, aDeclaration);
|
|
case eCSSProperty_border:
|
|
return ParseBorder(aErrorCode, aDeclaration);
|
|
case eCSSProperty_border_color:
|
|
return ParseBorderColor(aErrorCode, aDeclaration);
|
|
case eCSSProperty_border_spacing:
|
|
return ParseBorderSpacing(aErrorCode, aDeclaration);
|
|
case eCSSProperty_border_style:
|
|
return ParseBorderStyle(aErrorCode, aDeclaration);
|
|
case eCSSProperty_border_bottom:
|
|
return ParseBorderSide(aErrorCode, aDeclaration, kBorderBottomIDs);
|
|
case eCSSProperty_border_left:
|
|
return ParseBorderSide(aErrorCode, aDeclaration, kBorderLeftIDs);
|
|
case eCSSProperty_border_right:
|
|
return ParseBorderSide(aErrorCode, aDeclaration, kBorderRightIDs);
|
|
case eCSSProperty_border_top:
|
|
return ParseBorderSide(aErrorCode, aDeclaration, kBorderTopIDs);
|
|
case eCSSProperty_border_width:
|
|
return ParseBorderWidth(aErrorCode, aDeclaration);
|
|
case eCSSProperty_clip:
|
|
return ParseClip(aErrorCode, aDeclaration);
|
|
case eCSSProperty_content:
|
|
return ParseContent(aErrorCode, aDeclaration);
|
|
case eCSSProperty_counter_increment:
|
|
case eCSSProperty_counter_reset:
|
|
return ParseCounterData(aErrorCode, aDeclaration, aPropID);
|
|
case eCSSProperty_cue:
|
|
return ParseCue(aErrorCode, aDeclaration);
|
|
case eCSSProperty_cursor:
|
|
return ParseCursor(aErrorCode, aDeclaration);
|
|
case eCSSProperty_font:
|
|
return ParseFont(aErrorCode, aDeclaration);
|
|
case eCSSProperty_list_style:
|
|
return ParseListStyle(aErrorCode, aDeclaration);
|
|
case eCSSProperty_margin:
|
|
return ParseMargin(aErrorCode, aDeclaration);
|
|
case eCSSProperty_outline:
|
|
return ParseOutline(aErrorCode, aDeclaration);
|
|
case eCSSProperty_padding:
|
|
return ParsePadding(aErrorCode, aDeclaration);
|
|
case eCSSProperty_pause:
|
|
return ParsePause(aErrorCode, aDeclaration);
|
|
case eCSSProperty_play_during:
|
|
return ParsePlayDuring(aErrorCode, aDeclaration);
|
|
case eCSSProperty_quotes:
|
|
return ParseQuotes(aErrorCode, aDeclaration);
|
|
case eCSSProperty_size:
|
|
return ParseSize(aErrorCode, aDeclaration);
|
|
case eCSSProperty_text_shadow:
|
|
return ParseTextShadow(aErrorCode, aDeclaration);
|
|
|
|
default: // must be single property
|
|
{
|
|
nsCSSValue value;
|
|
if (ParseSingleValueProperty(aErrorCode, value, aPropID)) {
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aDeclaration->AppendValue(aPropID, value);
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// Bits used in determining which background position info we have
|
|
#define BG_CENTER 0x01
|
|
#define BG_TOP 0x02
|
|
#define BG_BOTTOM 0x04
|
|
#define BG_LEFT 0x08
|
|
#define BG_RIGHT 0x10
|
|
#define BG_CENTER1 0x20
|
|
#define BG_CENTER2 0x40
|
|
|
|
// Note: Don't change this table unless you update
|
|
// parseBackgroundPosition!
|
|
|
|
static const PRInt32 kBackgroundXYPositionKTable[] = {
|
|
eCSSKeyword_center, BG_CENTER,
|
|
eCSSKeyword_top, BG_TOP,
|
|
eCSSKeyword_bottom, BG_BOTTOM,
|
|
eCSSKeyword_left, BG_LEFT,
|
|
eCSSKeyword_right, BG_RIGHT,
|
|
-1,
|
|
};
|
|
|
|
PRBool CSSParserImpl::ParseSingleValueProperty(PRInt32& aErrorCode,
|
|
nsCSSValue& aValue,
|
|
nsCSSProperty aPropID)
|
|
{
|
|
switch (aPropID) {
|
|
case eCSSProperty_UNKNOWN:
|
|
case eCSSProperty_background:
|
|
case eCSSProperty_background_position:
|
|
case eCSSProperty_border:
|
|
case eCSSProperty_border_color:
|
|
case eCSSProperty_border_spacing:
|
|
case eCSSProperty_border_style:
|
|
case eCSSProperty_border_bottom:
|
|
case eCSSProperty_border_left:
|
|
case eCSSProperty_border_right:
|
|
case eCSSProperty_border_top:
|
|
case eCSSProperty_border_width:
|
|
case eCSSProperty_clip:
|
|
case eCSSProperty_content:
|
|
case eCSSProperty_counter_increment:
|
|
case eCSSProperty_counter_reset:
|
|
case eCSSProperty_cue:
|
|
case eCSSProperty_cursor:
|
|
case eCSSProperty_font:
|
|
case eCSSProperty_list_style:
|
|
case eCSSProperty_margin:
|
|
case eCSSProperty_outline:
|
|
case eCSSProperty_padding:
|
|
case eCSSProperty_pause:
|
|
case eCSSProperty_play_during:
|
|
case eCSSProperty_quotes:
|
|
case eCSSProperty_size:
|
|
case eCSSProperty_text_shadow:
|
|
case eCSSProperty_COUNT:
|
|
NS_ERROR("not a single value property");
|
|
return PR_FALSE;
|
|
|
|
case eCSSProperty_border_x_spacing:
|
|
case eCSSProperty_border_y_spacing:
|
|
case eCSSProperty_clip_bottom:
|
|
case eCSSProperty_clip_left:
|
|
case eCSSProperty_clip_right:
|
|
case eCSSProperty_clip_top:
|
|
case eCSSProperty_play_during_flags:
|
|
case eCSSProperty_quotes_close:
|
|
case eCSSProperty_quotes_open:
|
|
case eCSSProperty_size_height:
|
|
case eCSSProperty_size_width:
|
|
case eCSSProperty_text_shadow_color:
|
|
case eCSSProperty_text_shadow_radius:
|
|
case eCSSProperty_text_shadow_x:
|
|
case eCSSProperty_text_shadow_y:
|
|
NS_ERROR("not currently parsed here");
|
|
return PR_FALSE;
|
|
|
|
case eCSSProperty_auto_select:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
|
|
nsCSSProps::kAutoSelectKTable);
|
|
case eCSSProperty_auto_tab:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_AHK | VARIANT_NONE,
|
|
nsCSSProps::kAutoTabKTable);
|
|
case eCSSProperty_azimuth:
|
|
return ParseAzimuth(aErrorCode, aValue);
|
|
case eCSSProperty_background_attachment:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HK,
|
|
nsCSSProps::kBackgroundAttachmentKTable);
|
|
case eCSSProperty_background_color:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HCK,
|
|
nsCSSProps::kBackgroundColorKTable);
|
|
case eCSSProperty_background_image:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HUO, nsnull);
|
|
case eCSSProperty_background_repeat:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HK,
|
|
nsCSSProps::kBackgroundRepeatKTable);
|
|
case eCSSProperty_background_x_position:
|
|
case eCSSProperty_background_y_position:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HKLP,
|
|
kBackgroundXYPositionKTable);
|
|
case eCSSProperty_border_collapse:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HK,
|
|
nsCSSProps::kBorderCollapseKTable);
|
|
case eCSSProperty_border_bottom_color:
|
|
case eCSSProperty_border_left_color:
|
|
case eCSSProperty_border_right_color:
|
|
case eCSSProperty_border_top_color:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HCK,
|
|
nsCSSProps::kBorderColorKTable);
|
|
case eCSSProperty_border_bottom_style:
|
|
case eCSSProperty_border_left_style:
|
|
case eCSSProperty_border_right_style:
|
|
case eCSSProperty_border_top_style:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
|
|
nsCSSProps::kBorderStyleKTable);
|
|
case eCSSProperty_border_bottom_width:
|
|
case eCSSProperty_border_left_width:
|
|
case eCSSProperty_border_right_width:
|
|
case eCSSProperty_border_top_width:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HKL,
|
|
nsCSSProps::kBorderWidthKTable);
|
|
case eCSSProperty__moz_border_radius:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HLP, nsnull);
|
|
case eCSSProperty_bottom:
|
|
case eCSSProperty_top:
|
|
case eCSSProperty_left:
|
|
case eCSSProperty_right:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_AHLP, nsnull);
|
|
case eCSSProperty_box_sizing:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HK,
|
|
nsCSSProps::kBoxSizingKTable);
|
|
case eCSSProperty_height:
|
|
case eCSSProperty_width:
|
|
return ParsePositiveVariant(aErrorCode, aValue, VARIANT_AHLP, nsnull);
|
|
case eCSSProperty_caption_side:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HK,
|
|
nsCSSProps::kCaptionSideKTable);
|
|
case eCSSProperty_clear:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
|
|
nsCSSProps::kClearKTable);
|
|
case eCSSProperty_color:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HC, nsnull);
|
|
case eCSSProperty_cue_after:
|
|
case eCSSProperty_cue_before:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HUO, nsnull);
|
|
case eCSSProperty_direction:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HK,
|
|
nsCSSProps::kDirectionKTable);
|
|
case eCSSProperty_display:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
|
|
nsCSSProps::kDisplayKTable);
|
|
case eCSSProperty_elevation:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HK | VARIANT_ANGLE,
|
|
nsCSSProps::kElevationKTable);
|
|
case eCSSProperty_empty_cells:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HK,
|
|
nsCSSProps::kEmptyCellsKTable);
|
|
case eCSSProperty_float:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
|
|
nsCSSProps::kFloatKTable);
|
|
case eCSSProperty_float_edge:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HK,
|
|
nsCSSProps::kFloatEdgeKTable);
|
|
case eCSSProperty_font_family:
|
|
return ParseFamily(aErrorCode, aValue);
|
|
case eCSSProperty_font_size:
|
|
return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HKLP,
|
|
nsCSSProps::kFontSizeKTable);
|
|
case eCSSProperty_font_size_adjust:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HON,
|
|
nsnull);
|
|
case eCSSProperty_font_stretch:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HMK,
|
|
nsCSSProps::kFontStretchKTable);
|
|
case eCSSProperty_font_style:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HMK,
|
|
nsCSSProps::kFontStyleKTable);
|
|
case eCSSProperty_font_variant:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HMK,
|
|
nsCSSProps::kFontVariantKTable);
|
|
case eCSSProperty_font_weight:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HMKI,
|
|
nsCSSProps::kFontWeightKTable);
|
|
case eCSSProperty_letter_spacing:
|
|
case eCSSProperty_word_spacing:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HL | VARIANT_NORMAL, nsnull);
|
|
case eCSSProperty_key_equivalent:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
|
|
nsCSSProps::kKeyEquivalentKTable);
|
|
case eCSSProperty_line_height:
|
|
return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HLPN | VARIANT_NORMAL, nsnull);
|
|
case eCSSProperty_list_style_image:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HUO, nsnull);
|
|
case eCSSProperty_list_style_position:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HK, nsCSSProps::kListStylePositionKTable);
|
|
case eCSSProperty_list_style_type:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HOK, nsCSSProps::kListStyleKTable);
|
|
case eCSSProperty_margin_bottom:
|
|
case eCSSProperty_margin_left:
|
|
case eCSSProperty_margin_right:
|
|
case eCSSProperty_margin_top:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_AHLP, nsnull);
|
|
case eCSSProperty_marker_offset:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_AHL, nsnull);
|
|
case eCSSProperty_marks:
|
|
return ParseMarks(aErrorCode, aValue);
|
|
case eCSSProperty_max_height:
|
|
case eCSSProperty_max_width:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HLPO, nsnull);
|
|
case eCSSProperty_min_height:
|
|
case eCSSProperty_min_width:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HLP, nsnull);
|
|
case eCSSProperty_modify_content:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HK,
|
|
nsCSSProps::kModifyContentKTable);
|
|
case eCSSProperty_opacity:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HPN, nsnull);
|
|
case eCSSProperty_orphans:
|
|
case eCSSProperty_widows:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HI, nsnull);
|
|
case eCSSProperty_outline_color:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HCK,
|
|
nsCSSProps::kOutlineColorKTable);
|
|
case eCSSProperty_outline_style:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
|
|
nsCSSProps::kBorderStyleKTable);
|
|
case eCSSProperty_outline_width:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HKL,
|
|
nsCSSProps::kBorderWidthKTable);
|
|
case eCSSProperty_overflow:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_AHK,
|
|
nsCSSProps::kOverflowKTable);
|
|
case eCSSProperty_padding_bottom:
|
|
case eCSSProperty_padding_left:
|
|
case eCSSProperty_padding_right:
|
|
case eCSSProperty_padding_top:
|
|
return ParsePositiveVariant(aErrorCode, aValue, VARIANT_HLP, nsnull);
|
|
case eCSSProperty_page:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_AUTO | VARIANT_IDENTIFIER, nsnull);
|
|
case eCSSProperty_page_break_after:
|
|
case eCSSProperty_page_break_before:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_AHK,
|
|
nsCSSProps::kPageBreakKTable);
|
|
case eCSSProperty_page_break_inside:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_AHK,
|
|
nsCSSProps::kPageBreakInsideKTable);
|
|
case eCSSProperty_pause_after:
|
|
case eCSSProperty_pause_before:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HTP, nsnull);
|
|
case eCSSProperty_pitch:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HKF, nsCSSProps::kPitchKTable);
|
|
case eCSSProperty_pitch_range:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HN, nsnull);
|
|
case eCSSProperty_position:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HK, nsCSSProps::kPositionKTable);
|
|
case eCSSProperty_resizer:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_AHK | VARIANT_NONE,
|
|
nsCSSProps::kResizerKTable);
|
|
case eCSSProperty_richness:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HN, nsnull);
|
|
case eCSSProperty_selection_style:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
|
|
nsCSSProps::kSelectionStyleKTable);
|
|
case eCSSProperty_speak:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HMK | VARIANT_NONE,
|
|
nsCSSProps::kSpeakKTable);
|
|
case eCSSProperty_speak_header:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HK,
|
|
nsCSSProps::kSpeakHeaderKTable);
|
|
case eCSSProperty_speak_numeral:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HK,
|
|
nsCSSProps::kSpeakNumeralKTable);
|
|
case eCSSProperty_speak_punctuation:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HK,
|
|
nsCSSProps::kSpeakPunctuationKTable);
|
|
case eCSSProperty_speech_rate:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HN | VARIANT_KEYWORD,
|
|
nsCSSProps::kSpeechRateKTable);
|
|
case eCSSProperty_stress:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HN, nsnull);
|
|
case eCSSProperty_table_layout:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_AHK,
|
|
nsCSSProps::kTableLayoutKTable);
|
|
case eCSSProperty_text_align:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HK | VARIANT_STRING,
|
|
nsCSSProps::kTextAlignKTable);
|
|
case eCSSProperty_text_decoration:
|
|
return ParseTextDecoration(aErrorCode, aValue);
|
|
case eCSSProperty_text_indent:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HLP, nsnull);
|
|
case eCSSProperty_text_transform:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HOK,
|
|
nsCSSProps::kTextTransformKTable);
|
|
case eCSSProperty_unicode_bidi:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HMK,
|
|
nsCSSProps::kUnicodeBidiKTable);
|
|
case eCSSProperty_user_input:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_AHK | VARIANT_NONE,
|
|
nsCSSProps::kUserInputKTable);
|
|
case eCSSProperty_vertical_align:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HKLP,
|
|
nsCSSProps::kVerticalAlignKTable);
|
|
case eCSSProperty_visibility:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HK,
|
|
nsCSSProps::kVisibilityKTable);
|
|
case eCSSProperty_voice_family:
|
|
return ParseFamily(aErrorCode, aValue);
|
|
case eCSSProperty_volume:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HPN | VARIANT_KEYWORD,
|
|
nsCSSProps::kVolumeKTable);
|
|
case eCSSProperty_white_space:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_HMK,
|
|
nsCSSProps::kWhitespaceKTable);
|
|
case eCSSProperty_z_index:
|
|
return ParseVariant(aErrorCode, aValue, VARIANT_AHI, nsnull);
|
|
}
|
|
// explicitly do NOT have a default case to let the compiler
|
|
// help find missing properties
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseAzimuth(PRInt32& aErrorCode, nsCSSValue& aValue)
|
|
{
|
|
if (ParseVariant(aErrorCode, aValue, VARIANT_HK | VARIANT_ANGLE,
|
|
nsCSSProps::kAzimuthKTable)) {
|
|
if (eCSSUnit_Enumerated == aValue.GetUnit()) {
|
|
PRInt32 intValue = aValue.GetIntValue();
|
|
if ((NS_STYLE_AZIMUTH_LEFT_SIDE <= intValue) &&
|
|
(intValue <= NS_STYLE_AZIMUTH_BEHIND)) { // look for optional modifier
|
|
nsCSSValue modifier;
|
|
if (ParseEnum(aErrorCode, modifier, nsCSSProps::kAzimuthKTable)) {
|
|
PRInt32 enumValue = modifier.GetIntValue();
|
|
if (((intValue == NS_STYLE_AZIMUTH_BEHIND) &&
|
|
(NS_STYLE_AZIMUTH_LEFT_SIDE <= enumValue) && (enumValue <= NS_STYLE_AZIMUTH_RIGHT_SIDE)) ||
|
|
((enumValue == NS_STYLE_AZIMUTH_BEHIND) &&
|
|
(NS_STYLE_AZIMUTH_LEFT_SIDE <= intValue) && (intValue <= NS_STYLE_AZIMUTH_RIGHT_SIDE))) {
|
|
aValue.SetIntValue(intValue | enumValue, eCSSUnit_Enumerated);
|
|
return PR_TRUE;
|
|
}
|
|
// Put the unknown identifier back and return
|
|
UngetToken();
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseBackground(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration)
|
|
{
|
|
const PRInt32 numProps = 6;
|
|
static const nsCSSProperty kBackgroundIDs[numProps] = {
|
|
eCSSProperty_background_color,
|
|
eCSSProperty_background_image,
|
|
eCSSProperty_background_repeat,
|
|
eCSSProperty_background_attachment,
|
|
eCSSProperty_background_x_position,
|
|
eCSSProperty_background_y_position
|
|
};
|
|
|
|
nsCSSValue values[numProps];
|
|
PRInt32 found = ParseChoice(aErrorCode, values, kBackgroundIDs, numProps);
|
|
if ((found < 1) || (PR_FALSE == ExpectEndProperty(aErrorCode, PR_TRUE))) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
if (0 != (found & 0x30)) { // found one or more position values, validate them
|
|
if (0 == (found & 0x20)) { // x value only
|
|
if (eCSSUnit_Enumerated == values[4].GetUnit()) {
|
|
switch (values[4].GetIntValue()) {
|
|
case BG_CENTER:
|
|
values[4].SetIntValue(50, eCSSUnit_Enumerated);
|
|
values[5].SetIntValue(50, eCSSUnit_Enumerated);
|
|
break;
|
|
case BG_TOP:
|
|
values[4].SetIntValue(50, eCSSUnit_Enumerated);
|
|
values[5].SetIntValue(0, eCSSUnit_Enumerated);
|
|
break;
|
|
case BG_BOTTOM:
|
|
values[4].SetIntValue(50, eCSSUnit_Enumerated);
|
|
values[5].SetIntValue(100, eCSSUnit_Enumerated);
|
|
break;
|
|
case BG_LEFT:
|
|
values[4].SetIntValue(0, eCSSUnit_Enumerated);
|
|
values[5].SetIntValue(50, eCSSUnit_Enumerated);
|
|
break;
|
|
case BG_RIGHT:
|
|
values[4].SetIntValue(100, eCSSUnit_Enumerated);
|
|
values[5].SetIntValue(50, eCSSUnit_Enumerated);
|
|
break;
|
|
}
|
|
}
|
|
else if (eCSSUnit_Inherit == values[4].GetUnit()) {
|
|
values[5].SetInheritValue();
|
|
}
|
|
else {
|
|
values[5].SetPercentValue(50.0f);
|
|
}
|
|
}
|
|
else { // both x & y values
|
|
nsCSSUnit xUnit = values[4].GetUnit();
|
|
nsCSSUnit yUnit = values[5].GetUnit();
|
|
if (eCSSUnit_Enumerated == xUnit) { // if one is enumerated, both must be
|
|
if (eCSSUnit_Enumerated == yUnit) {
|
|
PRInt32 xValue = values[4].GetIntValue();
|
|
PRInt32 yValue = values[5].GetIntValue();
|
|
if (0 != (xValue & (BG_LEFT | BG_CENTER | BG_RIGHT))) { // x is really an x value
|
|
if (0 != (yValue & (BG_LEFT | BG_RIGHT))) { // y is also an x value
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
else { // x is a y value
|
|
if (0 != (yValue & (BG_TOP | BG_BOTTOM))) { // y is also a y value
|
|
return PR_FALSE;
|
|
}
|
|
PRInt32 holdXValue = xValue;
|
|
xValue = yValue;
|
|
yValue = holdXValue;
|
|
}
|
|
switch (xValue) {
|
|
case BG_LEFT:
|
|
values[4].SetIntValue(0, eCSSUnit_Enumerated);
|
|
break;
|
|
case BG_CENTER:
|
|
values[4].SetIntValue(50, eCSSUnit_Enumerated);
|
|
break;
|
|
case BG_RIGHT:
|
|
values[4].SetIntValue(100, eCSSUnit_Enumerated);
|
|
break;
|
|
default:
|
|
NS_ERROR("bad x value");
|
|
}
|
|
switch (yValue) {
|
|
case BG_TOP:
|
|
values[5].SetIntValue(0, eCSSUnit_Enumerated);
|
|
break;
|
|
case BG_CENTER:
|
|
values[5].SetIntValue(50, eCSSUnit_Enumerated);
|
|
break;
|
|
case BG_BOTTOM:
|
|
values[5].SetIntValue(100, eCSSUnit_Enumerated);
|
|
break;
|
|
default:
|
|
NS_ERROR("bad y value");
|
|
}
|
|
}
|
|
else {
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
else {
|
|
if (eCSSUnit_Enumerated == yUnit) {
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Provide missing values
|
|
if ((found & 0x01) == 0) {
|
|
values[0].SetIntValue(NS_STYLE_BG_COLOR_TRANSPARENT, eCSSUnit_Enumerated);
|
|
}
|
|
if ((found & 0x02) == 0) {
|
|
values[1].SetNoneValue();
|
|
}
|
|
if ((found & 0x04) == 0) {
|
|
values[2].SetIntValue(NS_STYLE_BG_REPEAT_XY, eCSSUnit_Enumerated);
|
|
}
|
|
if ((found & 0x08) == 0) {
|
|
values[3].SetIntValue(NS_STYLE_BG_ATTACHMENT_SCROLL, eCSSUnit_Enumerated);
|
|
}
|
|
if ((found & 0x30) == 0) {
|
|
values[4].SetPercentValue(0.0f);
|
|
values[5].SetPercentValue(0.0f);
|
|
}
|
|
|
|
PRInt32 index;
|
|
for (index = 0; index < numProps; index++) {
|
|
aDeclaration->AppendValue(kBackgroundIDs[index], values[index]);
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseBackgroundPosition(PRInt32& aErrorCode,
|
|
nsICSSDeclaration* aDeclaration)
|
|
{
|
|
// First try a number or a length value
|
|
nsCSSValue xValue;
|
|
if (ParseVariant(aErrorCode, xValue, VARIANT_HLP, nsnull)) {
|
|
if (eCSSUnit_Inherit == xValue.GetUnit()) { // both are inherited
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aDeclaration->AppendValue(eCSSProperty_background_x_position, xValue);
|
|
aDeclaration->AppendValue(eCSSProperty_background_y_position, xValue);
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
// We have one number/length. Get the optional second number/length.
|
|
nsCSSValue yValue;
|
|
if (ParseVariant(aErrorCode, yValue, VARIANT_LP, nsnull)) {
|
|
// We have two numbers
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aDeclaration->AppendValue(eCSSProperty_background_x_position, xValue);
|
|
aDeclaration->AppendValue(eCSSProperty_background_y_position, yValue);
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// We have one number which is the x position. Create an value for
|
|
// the vertical position which is of value 50%
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aDeclaration->AppendValue(eCSSProperty_background_x_position, xValue);
|
|
aDeclaration->AppendValue(eCSSProperty_background_y_position, nsCSSValue(0.5f, eCSSUnit_Percent));
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// Now try keywords. We do this manually to allow for the first
|
|
// appearance of "center" to apply to the either the x or y
|
|
// position (it's ambiguous so we have to disambiguate). Each
|
|
// allowed keyword value is assigned it's own bit. We don't allow
|
|
// any duplicate keywords other than center. We try to get two
|
|
// keywords but it's okay if there is only one.
|
|
PRInt32 mask = 0;
|
|
PRInt32 centerBit = BG_CENTER1;
|
|
for (PRInt32 i = 0; i < 2; i++) {
|
|
if (PR_FALSE == ParseEnum(aErrorCode, xValue, kBackgroundXYPositionKTable)) {
|
|
break;
|
|
}
|
|
PRInt32 bit = xValue.GetIntValue();
|
|
if (BG_CENTER == bit) {
|
|
// Special hack for center bits: We can have two of them
|
|
mask |= centerBit;
|
|
centerBit <<= 1;
|
|
continue;
|
|
} else if ((mask & bit) != 0) {
|
|
// no duplicate values allowed (other than center)
|
|
return PR_FALSE;
|
|
}
|
|
mask |= bit;
|
|
}
|
|
|
|
// Check for bad input. Bad input consists of no matching keywords,
|
|
// or pairs of x keywords or pairs of y keywords.
|
|
if ((mask == 0) || (mask == (BG_TOP | BG_BOTTOM)) ||
|
|
(mask == (BG_LEFT | BG_RIGHT))) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// Map good input
|
|
PRInt32 xEnumValue = 50;
|
|
if ((mask & (BG_LEFT | BG_RIGHT)) != 0) {
|
|
// We have an x value
|
|
xEnumValue = ((mask & BG_LEFT) != 0) ? 0 : 100;
|
|
}
|
|
PRInt32 yEnumValue = 50;
|
|
if ((mask & (BG_TOP | BG_BOTTOM)) != 0) {
|
|
// We have a y value
|
|
yEnumValue = ((mask & BG_TOP) != 0) ? 0 : 100;
|
|
}
|
|
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
// Create style values
|
|
aDeclaration->AppendValue(eCSSProperty_background_x_position, nsCSSValue(xEnumValue, eCSSUnit_Enumerated));
|
|
aDeclaration->AppendValue(eCSSProperty_background_y_position, nsCSSValue(yEnumValue, eCSSUnit_Enumerated));
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// These must be in CSS order (top,right,bottom,left) for indexing to work
|
|
static const nsCSSProperty kBorderStyleIDs[] = {
|
|
eCSSProperty_border_top_style,
|
|
eCSSProperty_border_right_style,
|
|
eCSSProperty_border_bottom_style,
|
|
eCSSProperty_border_left_style
|
|
};
|
|
static const nsCSSProperty kBorderWidthIDs[] = {
|
|
eCSSProperty_border_top_width,
|
|
eCSSProperty_border_right_width,
|
|
eCSSProperty_border_bottom_width,
|
|
eCSSProperty_border_left_width
|
|
};
|
|
static const nsCSSProperty kBorderColorIDs[] = {
|
|
eCSSProperty_border_top_color,
|
|
eCSSProperty_border_right_color,
|
|
eCSSProperty_border_bottom_color,
|
|
eCSSProperty_border_left_color
|
|
};
|
|
|
|
|
|
PRBool CSSParserImpl::ParseBorder(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration)
|
|
{
|
|
const PRInt32 numProps = 3;
|
|
static const nsCSSProperty kBorderIDs[] = {
|
|
eCSSProperty_border_top_width, // only one value per property
|
|
eCSSProperty_border_top_style,
|
|
eCSSProperty_border_top_color
|
|
};
|
|
|
|
nsCSSValue values[numProps];
|
|
|
|
PRInt32 found = ParseChoice(aErrorCode, values, kBorderIDs, numProps);
|
|
if ((found < 1) || (PR_FALSE == ExpectEndProperty(aErrorCode, PR_TRUE))) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
if (0 == (found & 1)) { // provide missing border width's
|
|
values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
|
|
}
|
|
|
|
if (0 == (found & 2)) { // provide missing border style's
|
|
values[1].SetNoneValue();
|
|
}
|
|
|
|
if (0 == (found & 4)) { // clear missing color values so color property will be used
|
|
values[2].Reset();
|
|
}
|
|
|
|
PRInt32 index;
|
|
for (index = 0; index < 4; index++) {
|
|
aDeclaration->AppendValue(kBorderWidthIDs[index], values[0]);
|
|
aDeclaration->AppendValue(kBorderStyleIDs[index], values[1]);
|
|
aDeclaration->AppendValue(kBorderColorIDs[index], values[2]);
|
|
}
|
|
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseBorderColor(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration)
|
|
{
|
|
return ParseBoxProperties(aErrorCode, aDeclaration, kBorderColorIDs);
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseBorderSpacing(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration)
|
|
{
|
|
nsCSSValue xValue;
|
|
if (ParseVariant(aErrorCode, xValue, VARIANT_HL, nsnull)) {
|
|
if (xValue.IsLengthUnit()) {
|
|
// We have one length. Get the optional second length.
|
|
nsCSSValue yValue;
|
|
if (ParseVariant(aErrorCode, yValue, VARIANT_LENGTH, nsnull)) {
|
|
// We have two numbers
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aDeclaration->AppendValue(eCSSProperty_border_x_spacing, xValue);
|
|
aDeclaration->AppendValue(eCSSProperty_border_y_spacing, yValue);
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
|
|
// We have one length which is the horizontal spacing. Create a value for
|
|
// the vertical spacing which is equal
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aDeclaration->AppendValue(eCSSProperty_border_x_spacing, xValue);
|
|
aDeclaration->AppendValue(eCSSProperty_border_y_spacing, xValue);
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseBorderSide(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration,
|
|
const nsCSSProperty aPropIDs[])
|
|
{
|
|
const PRInt32 numProps = 3;
|
|
nsCSSValue values[numProps];
|
|
|
|
PRInt32 found = ParseChoice(aErrorCode, values, aPropIDs, numProps);
|
|
if ((found < 1) || (PR_FALSE == ExpectEndProperty(aErrorCode, PR_TRUE))) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
if ((found & 1) == 0) { // Provide default border-width
|
|
values[0].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
|
|
}
|
|
if ((found & 2) == 0) { // Provide default border-style
|
|
values[1].SetNoneValue();
|
|
}
|
|
if ((found & 4) == 0) { // reset border-color so color property gets used
|
|
values[2].Reset();
|
|
}
|
|
|
|
PRInt32 index;
|
|
for (index = 0; index < numProps; index++) {
|
|
aDeclaration->AppendValue(aPropIDs[index], values[index]);
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseBorderStyle(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration)
|
|
{
|
|
return ParseBoxProperties(aErrorCode, aDeclaration, kBorderStyleIDs);
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseBorderWidth(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration)
|
|
{
|
|
return ParseBoxProperties(aErrorCode, aDeclaration, kBorderWidthIDs);
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseClip(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration)
|
|
{
|
|
static const nsCSSProperty kClipIDs[] = {
|
|
eCSSProperty_clip_top,
|
|
eCSSProperty_clip_right,
|
|
eCSSProperty_clip_bottom,
|
|
eCSSProperty_clip_left
|
|
};
|
|
if (! GetToken(aErrorCode, PR_TRUE)) {
|
|
return PR_FALSE;
|
|
}
|
|
PRInt32 index;
|
|
if ((eCSSToken_Ident == mToken.mType) &&
|
|
mToken.mIdent.EqualsIgnoreCase("auto")) {
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
nsCSSValue valueAuto(eCSSUnit_Auto);
|
|
for (index = 0; index < 4; index++) {
|
|
aDeclaration->AppendValue(kClipIDs[index], valueAuto);
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
} else if ((eCSSToken_Ident == mToken.mType) &&
|
|
mToken.mIdent.EqualsIgnoreCase("inherit")) {
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
nsCSSValue inherit(eCSSUnit_Inherit);
|
|
for (index = 0; index < 4; index++) {
|
|
aDeclaration->AppendValue(kClipIDs[index], inherit);
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
} else if ((eCSSToken_Function == mToken.mType) &&
|
|
mToken.mIdent.EqualsIgnoreCase("rect")) {
|
|
if (!ExpectSymbol(aErrorCode, '(', PR_TRUE)) {
|
|
return PR_FALSE;
|
|
}
|
|
nsCSSValue values[4];
|
|
for (index = 0; index < 4; index++) {
|
|
if (! ParseVariant(aErrorCode, values[index], VARIANT_AL, nsnull)) {
|
|
return PR_FALSE;
|
|
}
|
|
if (3 != index) {
|
|
// skip optional commas between elements
|
|
ExpectSymbol(aErrorCode, ',', PR_TRUE);
|
|
}
|
|
}
|
|
if (!ExpectSymbol(aErrorCode, ')', PR_TRUE)) {
|
|
return PR_FALSE;
|
|
}
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
for (index = 0; index < 4; index++) {
|
|
aDeclaration->AppendValue(kClipIDs[index], values[index]);
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
} else {
|
|
UngetToken();
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
#define VARIANT_CONTENT (VARIANT_STRING | VARIANT_URL | VARIANT_COUNTER | VARIANT_ATTR | \
|
|
VARIANT_KEYWORD)
|
|
PRBool CSSParserImpl::ParseContent(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration)
|
|
{
|
|
nsCSSValue value;
|
|
if (ParseVariant(aErrorCode, value, VARIANT_CONTENT | VARIANT_INHERIT,
|
|
nsCSSProps::kContentKTable)) {
|
|
if (eCSSUnit_Inherit == value.GetUnit()) {
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aDeclaration->AppendValue(eCSSProperty_content, value);
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
nsCSSValueList* listHead = new nsCSSValueList();
|
|
nsCSSValueList* list = listHead;
|
|
if (nsnull == list) {
|
|
aErrorCode = NS_ERROR_OUT_OF_MEMORY;
|
|
return PR_FALSE;
|
|
}
|
|
list->mValue = value;
|
|
|
|
while (nsnull != list) {
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aErrorCode = aDeclaration->AppendStructValue(eCSSProperty_content, listHead);
|
|
return NS_SUCCEEDED(aErrorCode);
|
|
}
|
|
if (ParseVariant(aErrorCode, value, VARIANT_CONTENT, nsCSSProps::kContentKTable)) {
|
|
list->mNext = new nsCSSValueList();
|
|
list = list->mNext;
|
|
if (nsnull != list) {
|
|
list->mValue = value;
|
|
}
|
|
else {
|
|
aErrorCode = NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
delete listHead;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseCounterData(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration,
|
|
nsCSSProperty aPropID)
|
|
{
|
|
nsString* ident = NextIdent(aErrorCode);
|
|
if (nsnull == ident) {
|
|
return PR_FALSE;
|
|
}
|
|
if (ident->EqualsIgnoreCase("none")) {
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aDeclaration->AppendValue(aPropID, nsCSSValue(eCSSUnit_None));
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
else if (ident->EqualsIgnoreCase("inherit")) {
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aDeclaration->AppendValue(aPropID, nsCSSValue(eCSSUnit_Inherit));
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
else {
|
|
nsCSSCounterData* dataHead = new nsCSSCounterData();
|
|
nsCSSCounterData* data = dataHead;
|
|
if (nsnull == data) {
|
|
aErrorCode = NS_ERROR_OUT_OF_MEMORY;
|
|
return PR_FALSE;
|
|
}
|
|
data->mCounter.SetStringValue(*ident, eCSSUnit_String);
|
|
|
|
while (nsnull != data) {
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aErrorCode = aDeclaration->AppendStructValue(aPropID, dataHead);
|
|
return NS_SUCCEEDED(aErrorCode);
|
|
}
|
|
if (! GetToken(aErrorCode, PR_TRUE)) {
|
|
break;
|
|
}
|
|
if ((eCSSToken_Number == mToken.mType) && (mToken.mIntegerValid)) {
|
|
data->mValue.SetIntValue(mToken.mInteger, eCSSUnit_Integer);
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aErrorCode = aDeclaration->AppendStructValue(aPropID, dataHead);
|
|
return NS_SUCCEEDED(aErrorCode);
|
|
}
|
|
if (! GetToken(aErrorCode, PR_TRUE)) {
|
|
break;
|
|
}
|
|
}
|
|
if (eCSSToken_Ident == mToken.mType) {
|
|
data->mNext = new nsCSSCounterData();
|
|
data = data->mNext;
|
|
if (nsnull != data) {
|
|
data->mCounter.SetStringValue(mToken.mIdent, eCSSUnit_String);
|
|
}
|
|
else {
|
|
aErrorCode = NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
delete dataHead;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseCue(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration)
|
|
{
|
|
nsCSSValue before;
|
|
if (ParseSingleValueProperty(aErrorCode, before, eCSSProperty_cue_before)) {
|
|
if (eCSSUnit_URL == before.GetUnit()) {
|
|
nsCSSValue after;
|
|
if (ParseSingleValueProperty(aErrorCode, after, eCSSProperty_cue_after)) {
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aDeclaration->AppendValue(eCSSProperty_cue_before, before);
|
|
aDeclaration->AppendValue(eCSSProperty_cue_after, after);
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aDeclaration->AppendValue(eCSSProperty_cue_before, before);
|
|
aDeclaration->AppendValue(eCSSProperty_cue_after, before);
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseCursor(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration)
|
|
{
|
|
nsCSSValue value;
|
|
if (ParseVariant(aErrorCode, value, VARIANT_AHUK, nsCSSProps::kCursorKTable)) {
|
|
if (eCSSUnit_URL == value.GetUnit()) {
|
|
nsCSSValueList* listHead = new nsCSSValueList();
|
|
nsCSSValueList* list = listHead;
|
|
if (nsnull == list) {
|
|
aErrorCode = NS_ERROR_OUT_OF_MEMORY;
|
|
return PR_FALSE;
|
|
}
|
|
list->mValue = value;
|
|
while (nsnull != list) {
|
|
if (eCSSUnit_URL != value.GetUnit()) {
|
|
if (PR_FALSE == ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aErrorCode = aDeclaration->AppendStructValue(eCSSProperty_cursor, listHead);
|
|
return NS_SUCCEEDED(aErrorCode);
|
|
}
|
|
if (ParseVariant(aErrorCode, value, VARIANT_AHUK, nsCSSProps::kCursorKTable)) {
|
|
list->mNext = new nsCSSValueList();
|
|
list = list->mNext;
|
|
if (nsnull != list) {
|
|
list->mValue = value;
|
|
}
|
|
else {
|
|
aErrorCode = NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
delete listHead;
|
|
return PR_FALSE;
|
|
}
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aDeclaration->AppendValue(eCSSProperty_cursor, value);
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
|
|
PRBool CSSParserImpl::ParseFont(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration)
|
|
{
|
|
static const nsCSSProperty fontIDs[] = {
|
|
eCSSProperty_font_style,
|
|
eCSSProperty_font_variant,
|
|
eCSSProperty_font_weight
|
|
};
|
|
|
|
nsCSSValue family;
|
|
if (ParseVariant(aErrorCode, family, VARIANT_HK, nsCSSProps::kFontKTable)) {
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
if (eCSSUnit_Inherit == family.GetUnit()) {
|
|
aDeclaration->AppendValue(eCSSProperty_font_family, family);
|
|
aDeclaration->AppendValue(eCSSProperty_font_style, family);
|
|
aDeclaration->AppendValue(eCSSProperty_font_variant, family);
|
|
aDeclaration->AppendValue(eCSSProperty_font_weight, family);
|
|
aDeclaration->AppendValue(eCSSProperty_font_size, family);
|
|
aDeclaration->AppendValue(eCSSProperty_line_height, family);
|
|
aDeclaration->AppendValue(eCSSProperty_font_stretch, family);
|
|
aDeclaration->AppendValue(eCSSProperty_font_size_adjust, family);
|
|
}
|
|
else {
|
|
aDeclaration->AppendValue(eCSSProperty_font_family, family); // keyword value overrides everything else
|
|
nsCSSValue empty;
|
|
aDeclaration->AppendValue(eCSSProperty_font_style, empty);
|
|
aDeclaration->AppendValue(eCSSProperty_font_variant, empty);
|
|
aDeclaration->AppendValue(eCSSProperty_font_weight, empty);
|
|
aDeclaration->AppendValue(eCSSProperty_font_size, empty);
|
|
aDeclaration->AppendValue(eCSSProperty_line_height, empty);
|
|
aDeclaration->AppendValue(eCSSProperty_font_stretch, empty);
|
|
aDeclaration->AppendValue(eCSSProperty_font_size_adjust, empty);
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// Get optional font-style, font-variant and font-weight (in any order)
|
|
const PRInt32 numProps = 3;
|
|
nsCSSValue values[numProps];
|
|
PRInt32 found = ParseChoice(aErrorCode, values, fontIDs, numProps);
|
|
if ((found < 0) || (eCSSUnit_Inherit == values[0].GetUnit())) { // illegal data
|
|
return PR_FALSE;
|
|
}
|
|
if ((found & 1) == 0) {
|
|
// Provide default font-style
|
|
values[0].SetNormalValue();
|
|
}
|
|
if ((found & 2) == 0) {
|
|
// Provide default font-variant
|
|
values[1].SetNormalValue();
|
|
}
|
|
if ((found & 4) == 0) {
|
|
// Provide default font-weight
|
|
values[2].SetNormalValue();
|
|
}
|
|
|
|
// Get mandatory font-size
|
|
nsCSSValue size;
|
|
if (! ParseVariant(aErrorCode, size, VARIANT_KEYWORD | VARIANT_LP, nsCSSProps::kFontSizeKTable)) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// Get optional "/" line-height
|
|
nsCSSValue lineHeight;
|
|
if (ExpectSymbol(aErrorCode, '/', PR_TRUE)) {
|
|
if (! ParseVariant(aErrorCode, lineHeight, VARIANT_NUMBER | VARIANT_LP | VARIANT_NORMAL, nsnull)) {
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
else {
|
|
lineHeight.SetNormalValue();
|
|
}
|
|
|
|
// Get final mandatory font-family
|
|
if (ParseFamily(aErrorCode, family)) {
|
|
if ((eCSSUnit_Inherit != family.GetUnit()) && ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aDeclaration->AppendValue(eCSSProperty_font_family, family);
|
|
aDeclaration->AppendValue(eCSSProperty_font_style, values[0]);
|
|
aDeclaration->AppendValue(eCSSProperty_font_variant, values[1]);
|
|
aDeclaration->AppendValue(eCSSProperty_font_weight, values[2]);
|
|
aDeclaration->AppendValue(eCSSProperty_font_size, size);
|
|
aDeclaration->AppendValue(eCSSProperty_line_height, lineHeight);
|
|
aDeclaration->AppendValue(eCSSProperty_font_stretch, nsCSSValue(eCSSUnit_Normal));
|
|
aDeclaration->AppendValue(eCSSProperty_font_size_adjust, nsCSSValue(eCSSUnit_None));
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseFamily(PRInt32& aErrorCode, nsCSSValue& aValue)
|
|
{
|
|
nsCSSToken* tk = &mToken;
|
|
nsAutoString family;
|
|
PRBool firstOne = PR_TRUE;
|
|
for (;;) {
|
|
if (!GetToken(aErrorCode, PR_TRUE)) {
|
|
break;
|
|
}
|
|
if (eCSSToken_Ident == tk->mType) {
|
|
if (firstOne) {
|
|
if (tk->mIdent.EqualsIgnoreCase("inherit")) {
|
|
aValue.SetInheritValue();
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
else {
|
|
family.Append(PRUnichar(','));
|
|
}
|
|
family.Append(tk->mIdent);
|
|
for (;;) {
|
|
if (!GetToken(aErrorCode, PR_FALSE)) {
|
|
break;
|
|
}
|
|
if (eCSSToken_Ident == tk->mType) {
|
|
family.Append(tk->mIdent);
|
|
} else if (eCSSToken_WhiteSpace == tk->mType) {
|
|
// Lookahead one token and drop whitespace if we ending the
|
|
// font name.
|
|
if (!GetToken(aErrorCode, PR_TRUE)) {
|
|
break;
|
|
}
|
|
if (eCSSToken_Ident != tk->mType) {
|
|
UngetToken();
|
|
break;
|
|
}
|
|
UngetToken();
|
|
family.Append(PRUnichar(' '));
|
|
} else {
|
|
UngetToken();
|
|
break;
|
|
}
|
|
}
|
|
firstOne = PR_FALSE;
|
|
} else if (eCSSToken_String == tk->mType) {
|
|
if (!firstOne) {
|
|
family.Append(PRUnichar(','));
|
|
}
|
|
family.Append(tk->mSymbol); // replace the quotes
|
|
family.Append(tk->mIdent);
|
|
family.Append(tk->mSymbol);
|
|
firstOne = PR_FALSE;
|
|
} else if (eCSSToken_Symbol == tk->mType) {
|
|
if (',' != tk->mSymbol) {
|
|
UngetToken();
|
|
break;
|
|
}
|
|
} else {
|
|
UngetToken();
|
|
break;
|
|
}
|
|
}
|
|
if (family.Length() == 0) {
|
|
return PR_FALSE;
|
|
}
|
|
aValue.SetStringValue(family, eCSSUnit_String);
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseListStyle(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration)
|
|
{
|
|
const PRInt32 numProps = 3;
|
|
static const nsCSSProperty listStyleIDs[] = {
|
|
eCSSProperty_list_style_type,
|
|
eCSSProperty_list_style_position,
|
|
eCSSProperty_list_style_image
|
|
};
|
|
|
|
nsCSSValue values[numProps];
|
|
PRInt32 index;
|
|
PRInt32 found = ParseChoice(aErrorCode, values, listStyleIDs, numProps);
|
|
if ((found < 1) || (PR_FALSE == ExpectEndProperty(aErrorCode, PR_TRUE))) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// Provide default values
|
|
if ((found & 1) == 0) {
|
|
values[0].SetIntValue(NS_STYLE_LIST_STYLE_DISC, eCSSUnit_Enumerated);
|
|
}
|
|
if ((found & 2) == 0) {
|
|
values[1].SetIntValue(NS_STYLE_LIST_STYLE_POSITION_OUTSIDE, eCSSUnit_Enumerated);
|
|
}
|
|
if ((found & 4) == 0) {
|
|
values[2].SetNoneValue();
|
|
}
|
|
|
|
for (index = 0; index < numProps; index++) {
|
|
aDeclaration->AppendValue(listStyleIDs[index], values[index]);
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseMargin(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration)
|
|
{
|
|
static const nsCSSProperty kMarginSideIDs[] = {
|
|
eCSSProperty_margin_top,
|
|
eCSSProperty_margin_right,
|
|
eCSSProperty_margin_bottom,
|
|
eCSSProperty_margin_left
|
|
};
|
|
return ParseBoxProperties(aErrorCode, aDeclaration, kMarginSideIDs);
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseMarks(PRInt32& aErrorCode, nsCSSValue& aValue)
|
|
{
|
|
if (ParseVariant(aErrorCode, aValue, VARIANT_HOK, nsCSSProps::kPageMarksKTable)) {
|
|
if (eCSSUnit_Enumerated == aValue.GetUnit()) {
|
|
if (PR_FALSE == ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
nsCSSValue second;
|
|
if (ParseEnum(aErrorCode, second, nsCSSProps::kPageMarksKTable)) {
|
|
aValue.SetIntValue(aValue.GetIntValue() | second.GetIntValue(), eCSSUnit_Enumerated);
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseOutline(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration)
|
|
{
|
|
const PRInt32 numProps = 3;
|
|
static const nsCSSProperty kOutlineIDs[] = {
|
|
eCSSProperty_outline_color,
|
|
eCSSProperty_outline_style,
|
|
eCSSProperty_outline_width
|
|
};
|
|
|
|
nsCSSValue values[numProps];
|
|
PRInt32 found = ParseChoice(aErrorCode, values, kOutlineIDs, numProps);
|
|
if ((found < 1) || (PR_FALSE == ExpectEndProperty(aErrorCode, PR_TRUE))) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
// Provide default values
|
|
if ((found & 1) == 0) {
|
|
values[0].SetIntValue(NS_STYLE_COLOR_INVERT, eCSSUnit_Enumerated);
|
|
}
|
|
if ((found & 2) == 0) {
|
|
values[1].SetNoneValue();
|
|
}
|
|
if ((found & 4) == 0) {
|
|
values[2].SetIntValue(NS_STYLE_BORDER_WIDTH_MEDIUM, eCSSUnit_Enumerated);
|
|
}
|
|
|
|
PRInt32 index;
|
|
for (index = 0; index < numProps; index++) {
|
|
aDeclaration->AppendValue(kOutlineIDs[index], values[index]);
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParsePadding(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration)
|
|
{
|
|
static const nsCSSProperty kPaddingSideIDs[] = {
|
|
eCSSProperty_padding_top,
|
|
eCSSProperty_padding_right,
|
|
eCSSProperty_padding_bottom,
|
|
eCSSProperty_padding_left
|
|
};
|
|
return ParseBoxProperties(aErrorCode, aDeclaration, kPaddingSideIDs);
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParsePause(PRInt32& aErrorCode,
|
|
nsICSSDeclaration* aDeclaration)
|
|
{
|
|
nsCSSValue before;
|
|
if (ParseSingleValueProperty(aErrorCode, before, eCSSProperty_pause_before)) {
|
|
if (eCSSUnit_Inherit != before.GetUnit()) {
|
|
nsCSSValue after;
|
|
if (ParseSingleValueProperty(aErrorCode, after, eCSSProperty_pause_after)) {
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aDeclaration->AppendValue(eCSSProperty_pause_before, before);
|
|
aDeclaration->AppendValue(eCSSProperty_pause_after, after);
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aDeclaration->AppendValue(eCSSProperty_pause_before, before);
|
|
aDeclaration->AppendValue(eCSSProperty_pause_after, before);
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParsePlayDuring(PRInt32& aErrorCode,
|
|
nsICSSDeclaration* aDeclaration)
|
|
{
|
|
nsCSSValue playDuring;
|
|
nsCSSValue flags;
|
|
if (ParseVariant(aErrorCode, playDuring, VARIANT_AHUO, nsnull)) {
|
|
if (eCSSUnit_URL == playDuring.GetUnit()) {
|
|
if (ParseEnum(aErrorCode, flags, nsCSSProps::kPlayDuringKTable)) {
|
|
PRInt32 intValue = flags.GetIntValue();
|
|
if (ParseEnum(aErrorCode, flags, nsCSSProps::kPlayDuringKTable)) {
|
|
flags.SetIntValue(intValue | flags.GetIntValue(), eCSSUnit_Enumerated);
|
|
}
|
|
}
|
|
}
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aDeclaration->AppendValue(eCSSProperty_play_during, playDuring);
|
|
aDeclaration->AppendValue(eCSSProperty_play_during_flags, flags);
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseQuotes(PRInt32& aErrorCode,
|
|
nsICSSDeclaration* aDeclaration)
|
|
{
|
|
nsCSSValue open;
|
|
if (ParseVariant(aErrorCode, open, VARIANT_HOS, nsnull)) {
|
|
if (eCSSUnit_String == open.GetUnit()) {
|
|
nsCSSQuotes* quotesHead = new nsCSSQuotes();
|
|
nsCSSQuotes* quotes = quotesHead;
|
|
if (nsnull == quotes) {
|
|
aErrorCode = NS_ERROR_OUT_OF_MEMORY;
|
|
return PR_FALSE;
|
|
}
|
|
quotes->mOpen = open;
|
|
while (nsnull != quotes) {
|
|
// get mandatory close
|
|
if (ParseVariant(aErrorCode, quotes->mClose, VARIANT_STRING, nsnull)) {
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aErrorCode = aDeclaration->AppendStructValue(eCSSProperty_quotes, quotesHead);
|
|
return NS_SUCCEEDED(aErrorCode);
|
|
}
|
|
// look for another open
|
|
if (ParseVariant(aErrorCode, open, VARIANT_STRING, nsnull)) {
|
|
quotes->mNext = new nsCSSQuotes();
|
|
quotes = quotes->mNext;
|
|
if (nsnull != quotes) {
|
|
quotes->mOpen = open;
|
|
continue;
|
|
}
|
|
aErrorCode = NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
delete quotesHead;
|
|
return PR_FALSE;
|
|
}
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aDeclaration->AppendValue(eCSSProperty_quotes_open, open);
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseSize(PRInt32& aErrorCode, nsICSSDeclaration* aDeclaration)
|
|
{
|
|
nsCSSValue width;
|
|
if (ParseVariant(aErrorCode, width, VARIANT_AHKL, nsCSSProps::kPageSizeKTable)) {
|
|
if (width.IsLengthUnit()) {
|
|
nsCSSValue height;
|
|
if (ParseVariant(aErrorCode, height, VARIANT_LENGTH, nsnull)) {
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aDeclaration->AppendValue(eCSSProperty_size_width, width);
|
|
aDeclaration->AppendValue(eCSSProperty_size_height, height);
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aDeclaration->AppendValue(eCSSProperty_size_width, width);
|
|
aDeclaration->AppendValue(eCSSProperty_size_height, width);
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseTextDecoration(PRInt32& aErrorCode, nsCSSValue& aValue)
|
|
{
|
|
if (ParseVariant(aErrorCode, aValue, VARIANT_HOK, nsCSSProps::kTextDecorationKTable)) {
|
|
if (eCSSUnit_Enumerated == aValue.GetUnit()) { // look for more keywords
|
|
PRInt32 intValue = aValue.GetIntValue();
|
|
nsCSSValue keyword;
|
|
PRInt32 index;
|
|
for (index = 0; index < 3; index++) {
|
|
if (ParseEnum(aErrorCode, keyword, nsCSSProps::kTextDecorationKTable)) {
|
|
intValue |= keyword.GetIntValue();
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
aValue.SetIntValue(intValue, eCSSUnit_Enumerated);
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool CSSParserImpl::ParseTextShadow(PRInt32& aErrorCode,
|
|
nsICSSDeclaration* aDeclaration)
|
|
{
|
|
nsCSSValue value;
|
|
if (ParseVariant(aErrorCode, value, VARIANT_HC | VARIANT_LENGTH | VARIANT_NONE, nsnull)) {
|
|
nsCSSUnit unit = value.GetUnit();
|
|
if ((eCSSUnit_Color == unit) || (eCSSUnit_String == unit) || value.IsLengthUnit()) {
|
|
nsCSSShadow* shadowHead = new nsCSSShadow();
|
|
nsCSSShadow* shadow = shadowHead;
|
|
if (nsnull == shadow) {
|
|
aErrorCode = NS_ERROR_OUT_OF_MEMORY;
|
|
return PR_FALSE;
|
|
}
|
|
while (nsnull != shadow) {
|
|
PRBool haveColor = PR_FALSE;
|
|
if (value.IsLengthUnit()) {
|
|
shadow->mXOffset = value;
|
|
}
|
|
else {
|
|
haveColor = PR_TRUE;
|
|
shadow->mColor = value;
|
|
if (ParseVariant(aErrorCode, value, VARIANT_LENGTH, nsnull)) {
|
|
shadow->mXOffset = value;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
if (ParseVariant(aErrorCode, value, VARIANT_LENGTH, nsnull)) {
|
|
shadow->mYOffset = value;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
if (ParseVariant(aErrorCode, value, VARIANT_LENGTH, nsnull)) {
|
|
shadow->mRadius = value;
|
|
} // optional
|
|
if (PR_FALSE == haveColor) {
|
|
if (ParseVariant(aErrorCode, value, VARIANT_COLOR, nsnull)) {
|
|
shadow->mColor = value;
|
|
}
|
|
}
|
|
if (ExpectSymbol(aErrorCode, ',', PR_TRUE)) {
|
|
shadow->mNext = new nsCSSShadow();
|
|
shadow = shadow->mNext;
|
|
if (nsnull == shadow) {
|
|
aErrorCode = NS_ERROR_OUT_OF_MEMORY;
|
|
break;
|
|
}
|
|
if (PR_FALSE == ParseVariant(aErrorCode, value, VARIANT_COLOR | VARIANT_LENGTH, nsnull)) {
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aErrorCode = aDeclaration->AppendStructValue(eCSSProperty_text_shadow, shadowHead);
|
|
return NS_SUCCEEDED(aErrorCode);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
delete shadowHead;
|
|
return PR_FALSE;
|
|
}
|
|
// value is inherit or none
|
|
if (ExpectEndProperty(aErrorCode, PR_TRUE)) {
|
|
aDeclaration->AppendValue(eCSSProperty_text_shadow_x, value);
|
|
return PR_TRUE;
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|