/*
* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description: Parser for CSS
*
*/
// INCLUDE FILES
#include <e32def.h> // First to avoid NULL redefine warning (no #ifndef NULL).
#include "CSSParser.h"
#include <nw_string_string.h>
#include "nwx_assert.h"
#include "nwx_http_defs.h"
#include "nwx_string.h"
#include "BrsrStatusCodes.h"
// EXTERNAL DATA STRUCTURES
// EXTERNAL FUNCTION PROTOTYPES
// CONSTANTS
// MACROS
// LOCAL CONSTANTS AND MACROS
// MODULE DATA STRUCTURES
// FORWARD DECLARATIONS
// LOCAL FUNCTION PROTOTYPES
// FORWARD DECLARATIONS
// ============================= LOCAL FUNCTIONS ===============================
// ============================ MEMBER FUNCTIONS ===============================
/* ------------------------------------------------------------------------- */
TBool TCSSParser::ParseElementName(TCSSReaderUnit* aNameSpace, TCSSReaderUnit* aElementName)
{
TInt8 tokenType;
TCSSReaderUnit token;
switch(tokenType = iReader.ReadNextToken(&token))
{
case ALPHA:
if (!iReader.ReadIdentifier(aElementName))
{
return EFalse;
}
tokenType = iReader.ReadNextToken(&token);
break;
case ASTERISK:
tokenType = iReader.ReadNextToken(&token);
break;
case VERTICAL_LINE:
break;
default:
SetPosition(token.iStorage);
return ETrue;
}
if ((tokenType != VERTICAL_LINE) && (tokenType != -1))
{
SetPosition(token.iStorage);
return ETrue;
}
/* There is a VERTICAL_LINE token - implies nameSpace present */
aNameSpace->Init(aElementName->iStorage, aElementName->iLength, aElementName->iNumChars);
aElementName->Init(NULL, 0, 0);
switch(iReader.ReadNextToken(&token))
{
case ALPHA:
return iReader.ReadIdentifier(aElementName);
case ASTERISK:
return ETrue;
default:
SetPosition(token.iStorage);
return ETrue;
}
}
/* ------------------------------------------------------------------------- */
TBrowserStatusCode TCSSParser::SelectorListIterate(TCSSReaderUnit* aTargetElement,
TUint32 *aSpecificity)
{
TBrowserStatusCode status;
TCSSReaderUnit token;
TCSSSimpleSelector simpleSelector;
TUint8 numSimpleSelectors;
*aSpecificity = 0;
numSimpleSelectors = 0;
while ((status = SimpleSelectorListIterate(&simpleSelector)) == KBrsrIterateMore)
{
*aSpecificity += simpleSelector.specificity;
numSimpleSelectors++;
}
/* Error in parsing selector means that ignore this rule */
if ((status != KBrsrIterateDone) || (numSimpleSelectors == 0)){
return KBrsrFailure;
}
/* status is KBrsrIterateDone */
aTargetElement->iStorage = simpleSelector.elementName.iStorage;
aTargetElement->iLength = simpleSelector.elementName.iLength;
aTargetElement->iNumChars = simpleSelector.elementName.iNumChars;
switch(iReader.ReadNextToken(&token))
{
case COMMA:
return KBrsrIterateMore;
case LEFT_BRACE:
return KBrsrIterateDone;
default:
return KBrsrFailure;
}
}
/* ------------------------------------------------------------------------- */
TBrowserStatusCode TCSSParser::ParseRule()
{
TBrowserStatusCode status;
TCSSReaderUnit targetElement;
TUint32 specificity = 0;
/* parse selector */
do
{
TText8* bufferPtr;
iReader.SkipWhiteSpaces();
bufferPtr = iReader.GetBufferPointer();
status = SelectorListIterate(&targetElement, &specificity);
if (iEventListener != NULL)
{
TRAPD(ret, iEventListener->SelectorL(bufferPtr, &targetElement, specificity));
if (ret == KErrNoMemory)
{
return KBrsrOutOfMemory;
}
}
}while (status == KBrsrIterateMore);
/* parse declaration list */
do
{
TCSSReaderUnit property;
TCSSPropertyVal propertyVal;
TBool important;
TUint8 numVals;
status = DeclarationListIterate(&property, &propertyVal, &numVals, 1, &important);
}while (status == KBrsrIterateMore);
if (status != KBrsrIterateDone)
{
TText8 *position;
return iReader.GoToToken(RIGHT_BRACE, &position);
}
return KBrsrSuccess;
}
/* ------------------------------------------------------------------------- */
/* parse comma separated list of media */
TBool TCSSParser::HasValidMedia()
{
TCSSReaderUnit medium;
TInt8 tokenType;
TCSSReaderUnit unit;
TBool applies;
applies = EFalse;
do
{
iReader.SkipWhiteSpaces();
/* Read medium */
if (!iReader.ReadIdentifier(&medium))
{
return EFalse;
}
if (!applies)
{/* Is handleheld mentioned ? */
applies = iReader.IsValidMedia(&medium);
}
iReader.SkipWhiteSpaces();
tokenType = iReader.ReadNextToken(&unit);
if ((tokenType == SEMI_COLON) || (tokenType == LEFT_BRACE))
{
SetPosition(unit.iStorage);
return applies;
}
}while (tokenType == COMMA);
return EFalse;
}
/* ------------------------------------------------------------------------- */
TBrowserStatusCode TCSSParser::ParseImportRule()
{
TCSSReaderUnit uri;
TCSSReaderUnit unit;
TBool applies;
TInt8 tokenType;
/* Skip any spaces */
iReader.SkipWhiteSpaces();
tokenType = iReader.ReadNextToken(&uri);
if (tokenType == URI)
{
if (!iReader.ReadURI(&uri))
{
return KBrsrFailure;
}
}
if ((tokenType != URI) && (tokenType != STRING))
{
return KBrsrFailure;
}
iReader.SkipWhiteSpaces();
switch(iReader.ReadNextToken(&unit))
{
case SEMI_COLON:
break;
case ALPHA:
applies = HasValidMedia();
if (iReader.ReadNextToken(&unit) != SEMI_COLON)
{
return KBrsrFailure;
}
if (!applies)
{
return KBrsrSuccess;
}
break;
default:
/* return advance to semi-colon */
return KBrsrFailure;
}
if (iEventListener)
{
return iEventListener->Import(&uri);
}
return KBrsrSuccess;
}
/* ------------------------------------------------------------------------- */
TBrowserStatusCode TCSSParser::ParseMediaRule()
{
TCSSReaderUnit unit;
if (!HasValidMedia()){
return KBrsrFailure;
}
if (iReader.ReadNextToken(&unit) != LEFT_BRACE)
{
return KBrsrFailure;
}
while(iReader.ReadNextToken(&unit) != RIGHT_BRACE)
{
SetPosition(unit.iStorage);
if (ParseRule() != KBrsrSuccess)
{
return KBrsrFailure;
}
iReader.SkipWhiteSpaces();
}
return KBrsrSuccess;
}
/* ------------------------------------------------------------------------- */
/* parse the header which can be comments, whitespaces, or import rules
* [S|CDO|CDC]* [import [S|CDO|CDC]* ]*
*/
TBrowserStatusCode TCSSParser::ParseStyleRules()
{
TBrowserStatusCode status;
TCSSReaderUnit token;
TUint32 readerPosition;
TText8 *position;
TBool atRuleParsed = EFalse;
status = KBrsrSuccess;
iReader.SkipWhiteSpaces();
while(status == KBrsrSuccess)
{
switch(iReader.ReadNextToken(&token))
{
case -1:
case BUFFER_END:
return KBrsrBufferEnd;
case CDO:
/* Skip SGML comments */
break;
case CDC:
/* Skip SGML comments */
break;
case ATKEYWORD:
case CHARSET_RULE:
readerPosition = iReader.GetPosition();
atRuleParsed = iReader.IgnoreAtRule(readerPosition);
if (!atRuleParsed)
{
status = KBrsrFailure;
}
break;
case IMPORT_RULE:
readerPosition = iReader.GetPosition();
if (ParseImportRule() != KBrsrSuccess)
{
atRuleParsed = iReader.IgnoreAtRule(readerPosition);
if (!atRuleParsed)
{
status = KBrsrFailure;
}
}
break;
case MEDIA_RULE:
readerPosition = iReader.GetPosition();
if (ParseMediaRule() != KBrsrSuccess)
{
atRuleParsed = iReader.IgnoreAtRule(readerPosition);
if (!atRuleParsed)
{
status = KBrsrFailure;
}
}
break;
default:
SetPosition(token.iStorage);
if (ParseRule() != KBrsrSuccess)
{
status = iReader.GoToToken( RIGHT_BRACE, &position);
}
break;
}
iReader.SkipWhiteSpaces();
}
return status;
}
/* ------------------------------------------------------------------------- */
TBrowserStatusCode TCSSParser::ParseHeader()
{
TBrowserStatusCode status;
TText8 *position;
TCSSReaderUnit token;
TUint32 thisPosition;
// read the next token for @charset rule
TInt tok = iReader.ReadNextToken(&token) ;
thisPosition = iReader.GetPosition();
if(tok == -1 )
{
// various failures in read token return -1
return KBrsrFailure;
}
if (tok == CHARSET_RULE)
{
if (iReader.GoToToken(SEMI_COLON, &position) != KBrsrSuccess)
{
return KBrsrFailure;
}
}
//If it is comment in the begginning then continue to read. Do not set
//position again. Otherwise in the embedded style sheet, <style> element
//is skipped.
if(tok != CDO)
{
iReader.SetPosition(thisPosition - token.iLength);
}
/* parse the header which can be comments, whitespaces, or import rules
* [S|CDO|CDC]* [import [S|CDO|CDC]* ]*
*/
status = KBrsrSuccess;
while (status == KBrsrSuccess)
{
switch(iReader.ReadNextToken(&token))
{
case -1:
case BUFFER_END:
return KBrsrBufferEnd;
case SPACE:
break;
case CDO:
/* Skip SGML comments */
break;
case CDC:
/* Skip SGML comments */
break;
case IMPORT_RULE:
if (ParseImportRule() != KBrsrSuccess){
status = iReader.GoToToken(SEMI_COLON, &position);
}
break;
default:
SetPosition(token.iStorage);
return KBrsrSuccess;
}
}
return status;
}
/* -------------------------------------------------------------------------*/
TBrowserStatusCode TCSSParser::ParserPropertyVal(TCSSPropertyVal* aPropertyVal)
{
TCSSReaderUnit token;
TUint32 thisPosition;
NW_ASSERT(aPropertyVal);
aPropertyVal->next = NULL;
iReader.SkipWhiteSpaces();
aPropertyVal->val.Init(NULL, 0, 0);
thisPosition = iReader.GetPosition();
switch (aPropertyVal->type = iReader.ReadNextToken(&aPropertyVal->val))
{
case URI:
if (!iReader.ReadURI(&aPropertyVal->val))
{
if (!iReader.ReadIdentifier(&aPropertyVal->val))
{
return KBrsrFailure;
}
aPropertyVal->type = IDENTIFIER;
}
break;
case STRING:
break;
case HASH:
if (!iReader.ReadName(&aPropertyVal->val))
{
return KBrsrFailure;
}
aPropertyVal->type = HASH;
break;
case ALPHA:
if (!iReader.ReadIdentifier(&aPropertyVal->val))
{
return KBrsrFailure;
}
aPropertyVal->type = IDENTIFIER;
break;
case HYPHEN:
{
thisPosition = iReader.GetPosition();
aPropertyVal->type = iReader.ReadNumberToken(&aPropertyVal->val);
if (aPropertyVal->type == -1)
{
iReader.SetPosition(thisPosition);
if (!iReader.ReadIdentifier(&aPropertyVal->val))
{
return KBrsrFailure;
}
aPropertyVal->type = IDENTIFIER;
}
break;
}
case RGB:
{
TCSSPropertyVal rVal;
TCSSPropertyVal gVal;
TCSSPropertyVal bVal;
aPropertyVal->val.iStorage = GetPosition();
if (ParserRGB(&rVal, &gVal, &bVal) != KBrsrSuccess)
{
return KBrsrFailure;
}
}
break;
case DOT:
iReader.SetPosition(thisPosition);
//Fall through to the default and read the number now
/*lint -fallthrough */
default:
aPropertyVal->type = iReader.ReadNumberToken(&aPropertyVal->val);
if (aPropertyVal->type == -1)
{
return KBrsrFailure;
}
break;
}
iReader.SkipWhiteSpaces();
thisPosition = iReader.GetPosition();
switch(iReader.ReadNextToken(&token))
{
case IMPORTANT:
case RIGHT_BRACE:
case SEMI_COLON:
case BUFFER_END:
SetPosition(token.iStorage);
return KBrsrSuccess;
case URI:
iReader.SetPosition(thisPosition);
aPropertyVal->next = iReader.GetBufferPointer();
return KBrsrSuccess;
default:
SetPosition(token.iStorage);
aPropertyVal->next = iReader.GetBufferPointer();
return KBrsrSuccess;
}
}
/* ------------------------------------------------------------------------- *
public methods
* ------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------*/
TBrowserStatusCode TCSSParser::ParserRGB(TCSSPropertyVal* aRVal,
TCSSPropertyVal* aGVal,
TCSSPropertyVal* aBVal)
{
TCSSReaderUnit token;
NW_ASSERT(aRVal);
NW_ASSERT(aGVal);
NW_ASSERT(aBVal);
iReader.SkipWhiteSpaces();
aRVal->type = iReader.ReadNumberToken(&aRVal->val);
iReader.SkipWhiteSpaces();
if (iReader.ReadNextToken(&token) != COMMA)
{
return KBrsrFailure;
}
iReader.SkipWhiteSpaces();
aGVal->type = iReader.ReadNumberToken(&aGVal->val);
iReader.SkipWhiteSpaces();
if (iReader.ReadNextToken(&token) != COMMA)
{
return KBrsrFailure;
}
iReader.SkipWhiteSpaces();
aBVal->type = iReader.ReadNumberToken(&aBVal->val);
iReader.SkipWhiteSpaces();
if (iReader.ReadNextToken(&token) != RIGHT_PARENTHESIS)
{
return KBrsrFailure;
}
return KBrsrSuccess;
}
/* ----------------------------------------------------------------------- */
/* declaration := property ':' S* expr prio?
property := IDENT S*
prio: ! S* important S*
*/
TBrowserStatusCode
TCSSParser::DeclarationListIterate(TCSSReaderUnit* aProperty,
TCSSPropertyVal* aPropertyVal,
TUint8 *aNumVals,
TUint8 aMaxVals,
TBool* aImportant)
{
TBrowserStatusCode status;
TInt8 tokenType;
TUint8 valIndex = 0;
TCSSReaderUnit token;
TCSSPropertyVal tempVal;
*aImportant = NW_FALSE;
*aNumVals = 0;
iReader.SkipWhiteSpaces();
switch(iReader.ReadNextToken(&token))
{
case RIGHT_BRACE:
case BUFFER_END:
return KBrsrIterateDone;
case ALPHA:
case HYPHEN:
if (!iReader.ReadIdentifier(aProperty))
{
return KBrsrFailure;
}
break;
default:
return KBrsrFailure;
}
iReader.SkipWhiteSpaces();
if (iReader.ReadNextToken(&token) != COLON)
{
return KBrsrFailure;
}
/* Read property value */
status = ParserPropertyVal(&tempVal);
if (status != KBrsrSuccess)
{
return status;
}
if (valIndex < aMaxVals)
{
aPropertyVal[valIndex++] = tempVal;
(*aNumVals)++;
}
/* if there is more than one value read the others e.g. fontborder */
while (tempVal.next != NULL)
{
/* if need be skip over the comma delimiter */
tokenType = iReader.ReadNextToken(&token);
if (tokenType != COMMA)
{
/* "undo" ReadNextToken */
SetPosition(token.iStorage);
}
status = ParserPropertyVal(&tempVal);
if (status != KBrsrSuccess)
{
return status;
}
if (valIndex < aMaxVals)
{
aPropertyVal[valIndex++] = tempVal;
(*aNumVals)++;
}
}
tokenType = iReader.ReadNextToken(&token) ;
if (tokenType == IMPORTANT)
{
*aImportant = NW_TRUE;
iReader.SkipWhiteSpaces();
tokenType = iReader.ReadNextToken(&token);
}
/* Consume semi-colons (there might be more than one (not in spec)*/
if (tokenType == SEMI_COLON)
{
do
{
iReader.SkipWhiteSpaces();
}while(iReader.ReadNextToken(&token) == SEMI_COLON);
}
SetPosition(token.iStorage);
return KBrsrIterateMore;
}
/* ------------------------------------------------------------------------- */
TBrowserStatusCode
TCSSParser::ConditionListIterate(TText8* aConditionType,
TCSSReaderUnit *aValue1,
TCSSReaderUnit *aValue2)
{
TCSSReaderUnit token;
TInt8 tokenType;
TUint32 readerPosition;
NW_ASSERT(aValue1);
NW_ASSERT(aValue2);
NW_ASSERT(aConditionType);
aValue1->Init(NULL, 0, 0);
aValue2->Init(NULL, 0, 0);
*aConditionType = 0;
switch(tokenType = iReader.ReadNextToken(&token))
{
case SPACE:
case COMMA:
case LEFT_BRACE:
SetPosition(token.iStorage);
return KBrsrIterateDone;
case -1:
return KBrsrBufferEnd;
case DOT:
if (!iReader.ReadIdentifier(aValue1))
{
return KBrsrFailure;
}
*aConditionType = CLASS_CONDITION;
break;
case HASH:
*aConditionType = ID_CONDITION;
if (!iReader.ReadName(aValue1))
{
return KBrsrFailure;
}
break;
case COLON:
if ((tokenType = iReader.GetPseudoClass()) == -1)
{
return KBrsrFailure;
}
*aConditionType = tokenType;
break;
case LEFT_BRACKET:
if (!iReader.ReadIdentifier(aValue1))
{
return KBrsrFailure;
}
iReader.SkipWhiteSpaces();
if (iReader.ReadNextToken(&token) != EQUALS)
{
return KBrsrFailure;
}
iReader.SkipWhiteSpaces();
readerPosition = iReader.GetPosition();
if (iReader.ReadNextToken(&token) == STRING)
{
iReader.SetPosition(readerPosition);
if (!iReader.ReadString(aValue2))
{
return KBrsrFailure;
}
}
else
{
if (!iReader.ReadName(aValue2))
{
return KBrsrFailure;
}
}
iReader.SkipWhiteSpaces();
if (iReader.ReadNextToken(&token) != RIGHT_BRACKET)
{
return KBrsrFailure;
}
*aConditionType = ATTRIBUTE_CONDITION;
break;
default:
return KBrsrFailure;
}
if (iEventListener)
{
TRAPD(ret, iEventListener->ConditionL(*aConditionType, aValue1, aValue2));
if (ret == KErrNoMemory)
{
return KBrsrOutOfMemory;
}
}
return KBrsrIterateMore;
}
/* -------------------------------------------------------------------------*/
TBrowserStatusCode
TCSSParser::SimpleSelectorListIterate(TCSSSimpleSelector* aSimpleSelector)
{
TBrowserStatusCode status;
TCSSReaderUnit token;
TCSSReaderUnit value1, value2;
TUint8 conditionType = 0;
NW_ASSERT(aSimpleSelector);
iReader.SkipWhiteSpaces();
switch(iReader.ReadNextToken( &token))
{
case LEFT_BRACE:
case COMMA:
SetPosition(token.iStorage);
return KBrsrIterateDone;
case -1:
return KBrsrBufferEnd;
default:
SetPosition(token.iStorage);
break;
}
/* initialize simple selector */
aSimpleSelector->nameSpace.Init(NULL, 0, 0);
aSimpleSelector->elementName.Init(NULL, 0, 0);
aSimpleSelector->conditionListHandle = NULL;
aSimpleSelector->specificity = 0;
/* Get namespace and elementName */
TBool parsed = ParseElementName(&aSimpleSelector->nameSpace, &aSimpleSelector->elementName);
if (!parsed)
{
return KBrsrFailure;
}
if (iEventListener)
{
TRAPD(ret, iEventListener->SimpleSelectorStartL(&aSimpleSelector->elementName));
if (ret == KErrNoMemory)
{
return KBrsrOutOfMemory;
}
}
if (aSimpleSelector->elementName.iLength != 0)
{
aSimpleSelector->specificity = 1;
}
/* parse conditions */
aSimpleSelector->conditionListHandle = iReader.GetBufferPointer();
status = ConditionListIterate(&conditionType, &value1, &value2);
if (status == KBrsrIterateDone)
{
aSimpleSelector->conditionListHandle = NULL;
}
while (status == KBrsrIterateMore)
{
if (conditionType)
{
if (conditionType == ID_CONDITION)
{
aSimpleSelector->specificity += 100;
}
else
{
aSimpleSelector->specificity += 10;
}
}
status = ConditionListIterate(&conditionType, &value1, &value2);
}
if (iEventListener)
{
TBool failed = (status != KBrsrIterateDone)? ETrue:EFalse;
TRAPD(ret, iEventListener->SimpleSelectorEndL(aSimpleSelector, failed));
if (ret == KErrNoMemory)
{
return KBrsrOutOfMemory;
}
}
if (status != KBrsrIterateDone)
{
return status;
}
return KBrsrIterateMore;
}
/* ------------------------------------------------------------------------- */
TBrowserStatusCode
TCSSParser::ParseStyleSheet(MCSSEventListener* aEventListener)
{
TBrowserStatusCode status;
if (aEventListener != NULL)
{
iEventListener = aEventListener;
iEventListener->SetCurrentEncoding(iReader.GetEncoding());
}
status = ParseHeader();
if (status == KBrsrBufferEnd)
{
return KBrsrSuccess;
}
if (status != KBrsrSuccess)
{
return status;
}
/* Parse style rules
* [ruleset | media | [S|CDO|CDC]* ]*
*/
status = ParseStyleRules();
if (status == KBrsrBufferEnd)
{
return KBrsrSuccess;
}
return status;
}
/* ------------------------------------------------------------------------- */
TUint32 TCSSParser::ParseCharset()
{
TUint32 prevEncoding = GetEncoding();
if (iReader.ReadCharset() != KBrsrSuccess)
{
return prevEncoding;
}
return GetEncoding();
}
/* ------------------------------------------------------------------------- */
void TCSSParser::SetPosition(TText8* aPosition)
{
iReader.SetPosition(aPosition - iReader.GetBuffer());
}