/*
* 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: Contains all CSS styles
*
*/
// INCLUDE FILES
#include "CSSRuleList.h"
#include <nwx_assert.h>
#include "BrsrStatusCodes.h"
#include "nwx_settings.h"
#include "CSSParser.h"
#include "nw_text_ucs2.h"
#include <nwx_string.h>
#include "nw_hed_contenthandler.h"
#include "CSSDeclListHandle.h"
#include "TKeyArrayPtr.h"
// EXTERNAL DATA STRUCTURES
// EXTERNAL FUNCTION PROTOTYPES
// CONSTANTS
// MACROS
// LOCAL CONSTANTS AND MACROS
// MODULE DATA STRUCTURES
// LOCAL FUNCTION PROTOTYPES
// FORWARD DECLARATIONS
// ============================= LOCAL FUNCTIONS ===============================
// ============================ MEMBER FUNCTIONS ===============================
// -----------------------------------------------------------------------------
// CCSSRuleList::CCSSRuleList
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CCSSRuleList::ConstructL(NW_CSS_Processor_t* aProcessor)
{
/* initialize the element map */
iElementList = new (ELeave) CCSSElementList(2);
/* initialize the bufferList */
iBufferList = CCSSBufferList::NewL();
iCurrentBufferIndex = -1;
iProcessor = aProcessor;
}
// -----------------------------------------------------------------------------
// CCSSRuleList::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CCSSRuleList* CCSSRuleList::NewL(NW_CSS_Processor_t* aProcessor)
{
CCSSRuleList* self = new( ELeave ) CCSSRuleList();
CleanupStack::PushL( self );
self->ConstructL(aProcessor);
CleanupStack::Pop();
return self;
}
// Destructor
CCSSRuleList::~CCSSRuleList()
{
if (iElementList != NULL)
{
iElementList->ResetAndDestroy();
delete iElementList;
}
if (iBufferList != NULL)
delete iBufferList;
if (iSelector != NULL)
delete iSelector;
}
// -----------------------------------------------------------------------------
// CCSSRuleList::ProcessBuffer
// parses a stylesheet
// -----------------------------------------------------------------------------
//
TBrowserStatusCode
CCSSRuleList::ProcessBuffer(TText8* aData, TUint32 aLength, TUint32 aEncoding, TBool aDoNotDelete)
{
TBrowserStatusCode status = KBrsrSuccess;
NW_ASSERT(aData != NULL);
TRAPD(ret, iBufferList->AddL(aData, aLength, aEncoding, aDoNotDelete, NULL));
if (ret == KErrNoMemory)
{
status = KBrsrOutOfMemory;
}
if (status == KBrsrSuccess)
{
iCurrentBufferIndex = iBufferList->GetSize() - 1;
TCSSParser parser(aData, aLength, aEncoding);
status = parser.ParseStyleSheet((MCSSEventListener*)(this));
iCurrentBufferIndex = -1;
}
return status;
}
// -----------------------------------------------------------------------------
// CCSSRuleList::GetElementRules
// Returns ruleList (a dynamic vector sorted by precedence) for a given element
// -----------------------------------------------------------------------------
//
CCSSRules*
CCSSRuleList::GetElementRules(TUint16 aToken)
{
CCSSRules* rules = NULL;
TInt index = 0;
CCSSRuleListElementEntry* tempEntry = NULL;
TRAPD( status, tempEntry = new(ELeave)CCSSRuleListElementEntry(aToken) );
if( status == KErrNone )
{
TNumKeyArrayPtr matchkey(_FOFF(CCSSRuleListElementEntry, iElementToken), ECmpTUint16);
if (iElementList->FindIsq( tempEntry, matchkey, index) == 0)
{
CCSSRuleListElementEntry* entry = iElementList->At(index);
rules = entry->iRules;
}
delete tempEntry;
}
return rules;
}
// -----------------------------------------------------------------------------
// CCSSRuleList::GetNextRule
// Returns rule in the list referenced by aIndex. Returns NULL if not valid.
// -----------------------------------------------------------------------------
//
CCSSRuleListElementEntry*
CCSSRuleList::GetElementRulesByIndex(TInt aIndex)
{
if (aIndex >= iElementList->Count())
return NULL;
CCSSRuleListElementEntry* entry = iElementList->At(aIndex);
return entry;
}
// -----------------------------------------------------------------------------
// CCSSRuleList::GetDictionary
// Returns dictionary
// -----------------------------------------------------------------------------
//
NW_WBXML_Dictionary_t*
CCSSRuleList::GetDictionary()
{
return iDictionary;
}
// -----------------------------------------------------------------------------
// CCSSRuleList::GetBufferList
// Returns the buffer list
// -----------------------------------------------------------------------------
//
CCSSBufferList* CCSSRuleList::GetBufferList()
{
return iBufferList;
}
// -----------------------------------------------------------------------------
// CCSSRuleList::SetCurrentBufferIndex
// Sets the index of the buffer being parsed
// -----------------------------------------------------------------------------
//
void CCSSRuleList::SetCurrentBufferIndex(TInt aIndex)
{
iCurrentBufferIndex = aIndex;
}
// -----------------------------------------------------------------------------
// CCSSRuleList::GetSize
// Returns the size of the rules list. This corresponds to the number of
// element entries in the iElementList
// -----------------------------------------------------------------------------
//
TInt CCSSRuleList::GetSize()
{
return iElementList->Count();
}
// -----------------------------------------------------------------------------
// CCSSRuleList::SetDictionary
// Set dictionary
// -----------------------------------------------------------------------------
//
void CCSSRuleList::SetDictionary(NW_WBXML_Dictionary_t* aDictionary)
{
iDictionary = aDictionary;
}
// -----------------------------------------------------------------------------
// CCSSRuleList::StartDocument
//
// -----------------------------------------------------------------------------
//
void CCSSRuleList::StartDocumentL(TText8*)
{
}
// -----------------------------------------------------------------------------
// CCSSRuleList::EndDocument
//
// -----------------------------------------------------------------------------
//
void CCSSRuleList::EndDocumentL(TText8*)
{
}
// -----------------------------------------------------------------------------
// CCSSRuleList::Selector
// Creates a new rule entry for each selector
// -----------------------------------------------------------------------------
//
void CCSSRuleList::SelectorL(TText8* aSelector,
TCSSReaderUnit* aTargetElement,
TUint32 aSpecificity)
{
TInt currentBufferPriority = iBufferList->GetPriority(iCurrentBufferIndex);
NW_REQUIRED_PARAM( aTargetElement );
NW_ASSERT(aSelector != NULL);
NW_ASSERT(aTargetElement != NULL);
if (iSelector)
{
// last simpleSelector will tell us the target token
TInt numSimpleSelectors = iSelector->iSimpleSelectorList->Count();
CCSSSimpleSelector* simpleSelector = iSelector->iSimpleSelectorList->At(numSimpleSelectors-1);
TUint16 elementToken = simpleSelector->iElementToken;
// we need to know where to insert this selector
CCSSRules* elementRules = GetElementRules(elementToken);
if (elementRules == NULL)
{
CCSSRuleListElementEntry* tempElementEntry
= new(ELeave)CCSSRuleListElementEntry(elementToken);
CleanupStack::PushL( tempElementEntry );
tempElementEntry->iRules = new (ELeave) CCSSRules(2);
// we want to insert the element entry in key order
TNumKeyArrayPtr matchkey(_FOFF(CCSSRuleListElementEntry, iElementToken), ECmpTUint16);
/* set the entry in the map */
iElementList->InsertIsqL(tempElementEntry, matchkey);
CleanupStack::Pop(); // tempElementEntry & tempElementEntry->iRules
elementRules = tempElementEntry->iRules;
}
/* Insert the element into Vector according to the specificity */
/* First find the index */
TInt size = elementRules->Count();
TInt index =0;
for(index =0; index < size; index++)
{
CCSSSelector *selector = elementRules->At(index);
TInt selectorBufferPriority = iBufferList->GetPriority(selector->iBufferIndex);
/* Found the index to insert the new rule */
/* if there are 2 rules with same specificity, then the rule from the
latter buffer gets precedence */
if ((aSpecificity > selector->iSpecificity) ||
((aSpecificity == selector->iSpecificity) &&
(selectorBufferPriority <= currentBufferPriority)))
break;
}
/* create a new entry for selector and initialize it */
iSelector->Init(aSelector, (TUint16)aSpecificity, iCurrentBufferIndex);
CCSSSelector* tempSelector = iSelector;
iSelector = NULL;
elementRules->InsertL(index, tempSelector);
}
}
// -----------------------------------------------------------------------------
// CCSSRuleList::SimpleSelector
//
// -----------------------------------------------------------------------------
//
void CCSSRuleList::SimpleSelectorEndL(TCSSSimpleSelector* , TBool aFailed)
{
NW_ASSERT(iSelector != NULL);
if (aFailed)
{
// there was and error removed last simple selector
TInt numSimpleSelectors = iSelector->iSimpleSelectorList->Count();
// removed last simple selector
if (numSimpleSelectors > 0 )
{
CCSSSimpleSelector* simpleSelector = iSelector->iSimpleSelectorList->At(numSimpleSelectors-1);
iSelector->iSimpleSelectorList->Delete(numSimpleSelectors-1);
delete simpleSelector;
}
if (iSelector->iSimpleSelectorList->Count() == 0)
{
delete iSelector;
iSelector = NULL;
}
}
}
// -----------------------------------------------------------------------------
// CCSSRuleList::SimpleSelectorStart
//
// -----------------------------------------------------------------------------
//
void CCSSRuleList::SimpleSelectorStartL(TCSSReaderUnit *aTargetElement)
{
TUint16 elementToken = 0;
NW_ASSERT(aTargetElement != NULL);
// get element token
if (aTargetElement->iLength != 0)
{
TText16* ucs2Str = aTargetElement->GetUnicodeL(iCurrentEncoding);
User::LeaveIfNull(ucs2Str);
elementToken =
(NW_Uint16)NW_WBXML_Dictionary_getTagToken(iDictionary, (NW_String_UCS2Buff_t *)ucs2Str, NW_FALSE);
NW_Mem_Free(ucs2Str);
}
if (iSelector == NULL)
{
iSelector = new (ELeave) CCSSSelector();
iSelector->iSimpleSelectorList = new (ELeave) CCSSSimpleSelectorList(1);
}
// create new Simple Selector entry
CCSSSimpleSelector* simpleSelector = new (ELeave) CCSSSimpleSelector(elementToken);
CleanupStack::PushL(simpleSelector);
iSelector->iSimpleSelectorList->AppendL(simpleSelector);
CleanupStack::Pop();// pop simpleselector
}
// -----------------------------------------------------------------------------
// CCSSRuleList::Condition
//
// -----------------------------------------------------------------------------
//
void CCSSRuleList::ConditionL(TUint8 aConditionType,
TCSSReaderUnit *aValue1,
TCSSReaderUnit *aValue2)
{
TInt numSimpleSelectors = iSelector->iSimpleSelectorList->Count();
// get last simple selector
CCSSSimpleSelector* simpleSelector =
iSelector->iSimpleSelectorList->At(numSimpleSelectors-1);
if (simpleSelector->iConditionList == NULL)
{
simpleSelector->iConditionList = new (ELeave) CCSSConditionList(1);
}
// create a new condition entry
CCSSCondition* condition = new (ELeave) CCSSCondition(aConditionType);
CleanupStack::PushL(condition);
// in case of CLASS or ID condition value1 is the value of attribute
// in case of attribute condition - it is the name of the attribute
if ((aConditionType == CLASS_CONDITION) ||
(aConditionType == ID_CONDITION) ||
(aConditionType == ATTRIBUTE_CONDITION))
{
if (aValue1)
{
condition->iValue1 = aValue1->GetUnicodeL(iCurrentEncoding);
}
else
{
delete condition;// no point of adding condition if it's not proper structure
condition = NULL;
}
}
if (aValue1 && aValue2 && (aConditionType == ATTRIBUTE_CONDITION))
{
//lint -e{644} Variable may not have been initialized
condition->iValue2 = aValue2->GetUnicodeL(iCurrentEncoding);
}
// add it to simple selector list
if (condition)
{
simpleSelector->iConditionList->AppendL(condition);
}
// Pop condition
CleanupStack::Pop();
}
// -----------------------------------------------------------------------------
// CCSSRuleList::Property
//
// -----------------------------------------------------------------------------
//
void CCSSRuleList::PropertyL(TCSSReaderUnit* aName,
TCSSPropertyVal* aPropertyVal,
TBool aImportant)
{
NW_REQUIRED_PARAM(aName);
NW_REQUIRED_PARAM(aPropertyVal);
NW_REQUIRED_PARAM(aImportant);
}
// -----------------------------------------------------------------------------
// CCSSRuleList::Import
// Issues load request for stylesheet pointed by aUrl
// -----------------------------------------------------------------------------
//
TBrowserStatusCode CCSSRuleList::Import(TCSSReaderUnit* aUrl)
{
TText16* ucs2Str;
NW_Text_t* url;
NW_Text_UCS2_t* tempUrl;
TBrowserStatusCode status;
TText16* urlStr = NULL;
NW_Text_Length_t urlLen;
NW_Bool freeNeeded;
TInt cssBufferIndex = 0;
NW_ASSERT(aUrl != NULL);
if (aUrl == NULL){
return KBrsrSuccess;
}
ucs2Str = aUrl->GetUnicodeL(iCurrentEncoding);
if (ucs2Str == NULL)
{
return KBrsrFailure;
}
tempUrl = NW_Text_UCS2_New (ucs2Str, (TUint16)NW_Str_Strlen(ucs2Str),
NW_Text_Flags_TakeOwnership);
if (tempUrl == NULL)
{
return KBrsrOutOfMemory; /* NW_Text_UCS2_New() takes care of deleting string when fails */
}
/* find url to import */
/* NOTE: if ResolveURL() returns a non-NULL pointer then the text object
* passed in is not leaked; if it returns a NULL pointer then the passed
* text object must be freed by the caller
*/
status =
NW_HED_ContentHandler_ResolveURL (iProcessor->owner,
NW_TextOf (tempUrl), &url);
if (status != KBrsrSuccess)
{
NW_Object_Delete(tempUrl);
return KBrsrSuccess; /* ignore import */
}
NW_ASSERT(iCurrentBufferIndex >= 0);
/* Get a string for the resolved url. If the creation fails, it will still be
* NULL, which won't cause any major problems. */
urlStr = NW_Text_GetUCS2Buffer (url, 0, &urlLen, &freeNeeded);
/* add a new entry to the bufferList */
TRAPD(ret, iBufferList->AddL(NULL, 0, iCurrentEncoding, NW_FALSE, urlStr));
cssBufferIndex = iBufferList->GetSize() - 1;
if (ret == KErrNoMemory)
{
status = KBrsrOutOfMemory;
}
if (freeNeeded)
{
NW_Str_Delete(urlStr);
}
if (status == KBrsrSuccess)
{
iBufferList->UpdatePriority(iCurrentBufferIndex);
/* finally load the url */
status = NW_CSS_Processor_ProcessLoadRequest( iProcessor,
NW_TextOf(url),
NW_CSS_Processor_LoadCSS,
iProcessor->isSavedDeck,
iProcessor->isHistLoad,
iProcessor->isReLoad,
cssBufferIndex);
}
else
{
status = KBrsrSuccess; /* ignore import */
}
NW_Object_Delete(url);
url = NULL;
return status;
}
// -----------------------------------------------------------------------------
// CCSSRuleList::GetCurrentEncoding
// Returns Returns the encoding of the document being parsed
// -----------------------------------------------------------------------------
//
TUint32 CCSSRuleList::GetCurrentEncoding()
{
return iCurrentEncoding;
}
// -----------------------------------------------------------------------------
// CCSSRuleList::SetCurrentEncoding
// Sets the encoding of the document being parsed
// -----------------------------------------------------------------------------
//
void CCSSRuleList::SetCurrentEncoding(TUint32 aEncoding)
{
iCurrentEncoding = aEncoding;
}
// -----------------------------------------------------------------------------
// CCSSRuleList::ApplyStyles
// Applies styles for a given element node in the document after pattern matching
// -----------------------------------------------------------------------------
//
void CCSSRuleList::ApplyStylesL(NW_DOM_ElementNode_t* aElementNode,
NW_LMgr_Box_t* aBox,
const NW_Evt_Event_t* aEvent,
NW_HED_DomHelper_t* aDomHelper)
{
TInt univRuleIndex = 0;
CCSSNodeData nodeData;
NW_ASSERT(aElementNode != NULL);
NW_ASSERT(aBox != NULL);
NW_ASSERT(aDomHelper != NULL);
TUint16 elementToken = NW_DOM_ElementNode_getTagToken(aElementNode);
/* apply styles for the given elementToken */
CCSSRules* elementRules = GetElementRules(elementToken);
CCSSRules* universalRules = GetElementRules(0);
if (elementRules!= NULL)
{
TInt elementRulesSize = elementRules->Count();
for(TInt index =0;index < elementRulesSize;index++)
{
CCSSSelector* entry1 = elementRules->At(index);
TInt priority1 = iBufferList->GetPriority(entry1->iBufferIndex);
if (universalRules)
{
/* first apply any universal rule which has a higher weight than this rule */
TInt universalRulesSize = universalRules->Count();
while (univRuleIndex < universalRulesSize)
{
CCSSSelector *entry2 = universalRules->At(univRuleIndex);
TInt priority2 = iBufferList->GetPriority(entry2->iBufferIndex);
if ((entry2->iSpecificity < entry1->iSpecificity) ||
((entry2->iSpecificity == entry1->iSpecificity) && (priority2 < priority1)))
break;
ApplyEntryStylesL(aElementNode, entry2, aBox, aEvent, aDomHelper, &nodeData);
univRuleIndex++;
} // while
}// if
ApplyEntryStylesL(aElementNode, entry1, aBox, aEvent, aDomHelper, &nodeData);
}// for
}// if (elementRules!= NULL)
/* apply any remaining universal rules */
if (universalRules)
{
TInt universalRulesSize = universalRules->Count();
while(univRuleIndex < universalRulesSize)
{
CCSSSelector *entry = universalRules->At(univRuleIndex);
ApplyEntryStylesL(aElementNode, entry, aBox, aEvent, aDomHelper, &nodeData);
univRuleIndex++;
}// while
}// if
}
// -----------------------------------------------------------------------------
// CCSSRuleList::ApplyEntryStyles
// Applies styles for a given rule list entry after pattern matching
// -----------------------------------------------------------------------------
//
void CCSSRuleList::ApplyEntryStylesL(NW_DOM_ElementNode_t* aElementNode,
CCSSSelector* aEntry,
NW_LMgr_Box_t* aBox,
const NW_Evt_Event_t* aEvent,
NW_HED_DomHelper_t* aDomHelper,
CCSSNodeData* aNodeData)
{
TText8* declarationList;
TCSSParser parser;
NW_ASSERT(aElementNode != NULL);
NW_ASSERT(aEntry != NULL);
NW_ASSERT(aBox != NULL);
NW_ASSERT(aDomHelper != NULL);
/* first match pattern */
iBufferList->InitializeParser(&parser, aEntry->iBufferIndex);
CCSSPatternMatcher patternMatcher(iDictionary, iProcessor->owner, aDomHelper);
TBool matched = patternMatcher.MatchPatternL(aElementNode, aEntry, aEvent, aNodeData);
if (matched)
{
// initialize the parser to beginning of selector
parser.SetPosition(aEntry->iSelector);
TCSSReader* reader = parser.GetReader();
// go to beginning of declaration list
reader->GoToToken(LEFT_BRACE, &declarationList);
/* pattern matches - apply styles */
TCSSDeclListHandle::ApplyStylesL(aBox, &parser, iProcessor, aEntry->iBufferIndex );
}
}