/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: smlparse.cpp,v 1.2.12.3 2005/01/26 20:09:29 ehyche Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>

#include "hxtypes.h"
#include "hxresult.h"
#include "hxcom.h"
#include "hxcomm.h"
#include "ihxpckts.h"
#include "hxfiles.h"
#include "hxformt.h"
#include "hxengin.h"
#include "hxplugn.h"
#include "hxpends.h"
#include "hxasm.h"
#include "hxprefs.h"
#include "hxupgrd.h"
#include "hxassert.h"
#include "chxpckts.h"
#include "upgrdcol.h"
#include "nptime.h"
#include "smpte.h"
#include "debug.h"
#include "hxstrutl.h"
#include "hxstring.h"
#include "cbqueue.h"
#include "hxslist.h"
#include "hxurl.h"
#include "hxmap.h"
#include "hxstack.h"
#include "hxwintyp.h"
#include "chxxtype.h"
#include "hxparse.h"

#include "hxxml.h"
#include "xmlreslt.h"

#include "looseprs.h"
#include "hxxmlprs.h" //HXXMLParser

#include "sm1elem.h"
#include "sm1time.h"
#include "sm1error.h"
#include "sm1parse.h"
#include "safestring.h"

#include "hxmon.h"

#include "debugout.h"
#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE		
static const char HX_THIS_FILE[] = __FILE__;
#endif

static const UINT32 MAX_DRIVER_PACKET_SIZE = 1024;
static const UINT32 INITIAL_STREAM1_TIMESTAMP = 1;
static const UINT32 INITIAL_STREAM0_TIMESTAMP = 0;
static const UINT32 MAX_ERROR_LEN = 1024;

static const char* const RN_PREFIX = "rn";
static const char* const RN_TAG_RENDERER_LIST = "rn:renderer-list";
static const char* const RN_TAG_RENDERER = "rn:renderer";
static const char* const SYSTEM_COMPONENT_NAMESPACE = "http://features.real.com/systemComponent";
static const char* const SYSTEM_COMPONENT = "systemComponent";

static const struct smil1ColorTable
{
    char* m_pColorName;
    UINT8 m_ucRed;
    UINT8 m_ucGreen;
    UINT8 m_ucBlue;
} Smil1ColorTable[] =
{
    {"black", 	0x00, 0x00, 0x00},
    {"silver", 	0xc0, 0xc0, 0xc0},
    {"gray",  	0x80, 0x80, 0x80},
    {"white", 	0xff, 0xff, 0xff},
    {"maroon",	0x80, 0x00, 0x00},
    {"red",   	0xff, 0x00, 0x00},
    {"purple",	0x80, 0x00, 0x80},
    {"fuchsia", 0xff, 0x00, 0xff},
    {"green",	0x00, 0x80, 0x00},
    {"lime",	0x00, 0xff, 0x00},
    {"olive",	0x80, 0x80, 0x00},
    {"yellow",	0xff, 0xff, 0x00},
    {"navy",	0x00, 0x00, 0x80},
    {"blue",	0x00, 0x00, 0xff},
    {"teal",	0x00, 0x80, 0x80},
    {"aqua",	0x00, 0xff, 0xff},
    {0,		0x00, 0x00, 0x00}
};

static const struct smil1TagTable
{
    SMIL1NodeTag m_tag;
    const char* m_name;
} smil1TagTable[] =
{
    {SMILSmil,		    "smil"},
    {SMILMeta,		    "meta"},
    {SMILHead,		    "head"},
    {SMILBody,		    "body"},
    {SMILBasicLayout,	    "layout"},
    {SMILRootLayout,	    "root-layout"},
    {SMILRegion,	    "region"},
    {SMILSwitch,	    "switch"},
    {SMILText,		    "text"},
    {SMILImg,		    "img"},
    {SMILRef,		    "ref"},
    {SMILAudio,		    "audio"},
    {SMILVideo,		    "video"},
    {SMILAnimation,	    "animation"},
    {SMILTextstream,	    "textstream"},
    {SMILAnchor,	    "anchor"},
    {SMILAAnchor,	    "a"},
    {SMILPar,		    "par"},
    {SMILSeq,		    "seq"},
    {SMILRNRendererList,    RN_TAG_RENDERER_LIST},
    {SMILRendererPreFetch,  RN_TAG_RENDERER},
    {SMILUnknown,	    "unknown"}
};


CSmil1Parser::CSmil1Parser(IUnknown* pContext):
    m_pContext(pContext),
    m_pClassFactory(NULL),
    m_pISystemRequired(NULL),
    m_pNodeList(0),
    m_pNodeListStack(0),
    m_pPacketQueue(0),
    m_pIDMap(0),
    m_pAddGroupMap(0),
    m_pSourceUpdateList(0),
    m_pRequireTagsMap(0),
    m_bNoNamespaces(FALSE),
    m_bRNNamespace(FALSE),
    m_bIgnoreUnrecognizedElements(TRUE),
    m_bSMILRootLayoutAlreadyFound(FALSE),
    m_bSMIL10FullCompliance(FALSE),
    m_pActiveNamespaceMap(NULL),
    m_pNSConflictList(NULL),
    m_bTimestampsResolved(FALSE),
    m_pCurNode(0),
    m_pNodeDependencies(0),
    m_pCurrentDependentNode(0),
    m_pAnchorStack(0),
    m_pCurrentAnchor(0),
    m_pEndLayout(0),
    m_ulBandwidthPreference(0),
    m_ulScreenHeightPreference(0),
    m_ulScreenWidthPreference(0),
    m_ulScreenDepthPreference(0),
    m_pLanguagePreferenceList(0),
    m_bCaptionsPreference(FALSE),
    m_pOverdubOrCaptionPreference(0),
    m_pBasePath(0),
    m_pTagAttributeMap(0),
    m_bContainsSource(FALSE),
    m_pEncoding(0),
    m_pTrackHintList(0),
    m_pParser(NULL),
    m_pResponse(NULL),
    m_ulErrorLineNumber(0),
    m_ulErrorColumnNumber(0),
    m_pErrorText(NULL)
    , m_bStoreErrors(FALSE)
    , m_pErrors(NULL)
    , m_ulPersistentComponentID(0)
    , m_elementWithinTag(WithinUnknown)
    , m_pVarName(NULL)
    , m_ulNextVar(0)
    , m_pTimelineElementManager(NULL)
{
    if(m_pContext)
    {
	m_pContext->AddRef();
	m_pContext->QueryInterface(IID_IHXCommonClassFactory, (void**)&m_pClassFactory);
    }

    initRequireTags();
    initTagAttributes();
    getPreferences();
    m_pVarName = new char [256];
    m_pTimelineElementManager = new CSmil1TimelineElementManager;
}

CSmil1Parser::~CSmil1Parser()
{
    deleteTagAttributes();
    HX_DELETE(m_pRequireTagsMap);

    if (m_pErrors)
    {
	int size = m_pErrors->GetSize();
	for (int i =0; i < size; ++i)
	{
	    IHXBuffer* pBuf = (IHXBuffer*)(*m_pErrors)[i];
	    HX_RELEASE(pBuf);
	    (*m_pErrors)[i] = NULL;
	}
	HX_DELETE(m_pErrors);
    }
    
    if (m_pActiveNamespaceMap != NULL)
    {
	CHXMapStringToOb::Iterator ndxBuffer = m_pActiveNamespaceMap->Begin();
	for (; ndxBuffer != m_pActiveNamespaceMap->End(); ++ndxBuffer)
	{
	    IHXBuffer* pBuffer = (IHXBuffer*)(*ndxBuffer);
	    HX_RELEASE(pBuffer);
	}
	HX_DELETE(m_pActiveNamespaceMap);
    }

    if (m_pNSConflictList != NULL)
    {
	CHXSimpleList::Iterator ndx = m_pNSConflictList->Begin();
	for (; ndx != m_pNSConflictList->End(); ++ndx)
	{
	    SMIL1Namespace* pNS = (SMIL1Namespace*)(*ndx);
	    HX_DELETE(pNS);
	}
	HX_DELETE(m_pNSConflictList);
    }

    HX_DELETE(m_pNodeDependencies);
    HX_DELETE(m_pAnchorStack);
    HX_VECTOR_DELETE(m_pEncoding);
    if(m_pLanguagePreferenceList)
    {
	CHXSimpleList::Iterator i = m_pLanguagePreferenceList->Begin();
	for(; i != m_pLanguagePreferenceList->End(); ++i)
	{
	    char* pLang = (char*)(*i);
	    delete[] pLang;
	}
	HX_DELETE(m_pLanguagePreferenceList);
    }
    HX_DELETE(m_pOverdubOrCaptionPreference);
    HX_DELETE(m_pBasePath);

    close();

    HX_RELEASE(m_pClassFactory);
    HX_RELEASE(m_pContext);
    HX_VECTOR_DELETE(m_pVarName);
    HX_DELETE(m_pTimelineElementManager);
}

void
CSmil1Parser::initRequireTags()
{
    //XXXBAB - add required tags here
#if 0
    m_pRequireTagsMap = new CHXMapStringToOb;
    (*m_pRequireTagsMap)["foo-require"] = 0;
    (*m_pRequireTagsMap)["boo-require"] = 0;
#endif

}

void
CSmil1Parser::getPreferences()
{
    IHXPreferences*	pPrefs = 0;
    IHXRegistry*	pRegistry = NULL;

    m_pContext->QueryInterface(IID_IHXRegistry, (void**)&pRegistry);

    if(HXR_OK == m_pContext->QueryInterface(
	IID_IHXPreferences, (void**)&pPrefs))
    {
	IHXBuffer* pBuf = 0;
	CHXString strTemp;
	strTemp.Format("%s.%s",HXREGISTRY_PREFPROPNAME,"Language");

	if(pRegistry && HXR_OK == pRegistry->GetStrByName(strTemp, pBuf))
	{
	    // language preference can be a comma-separated list

	    const char* pLang = (const char*)pBuf->GetBuffer();
	    // gonna call strtok, so copy the string...
	    char* pLangCopy = new_string(pLang);
	    m_pLanguagePreferenceList = new CHXSimpleList;
	    char* pTok = strtok(pLangCopy, ",");
	    while(pTok)
	    {
		char* pLangString = new_string(pTok);
		m_pLanguagePreferenceList->AddTail(pLangString);
		pTok = strtok(NULL, ",");
	    }
	    delete[] pLangCopy;
	    HX_RELEASE(pBuf);
	}
	if(HXR_OK == pPrefs->ReadPref("bandwidth", pBuf)  ||
		// /Fixes PR 84098 (SMIL 1.0 version) on Mac whose player
		// registry is case-sensitive and the registry value is
		// Bandwidth with a capital B:
		HXR_OK == pPrefs->ReadPref("Bandwidth", pBuf))
	{
	    m_ulBandwidthPreference = 
		(UINT32)atol((const char*)pBuf->GetBuffer());
	    HX_RELEASE(pBuf);
	}
	if(HXR_OK == pPrefs->ReadPref("screen_depth", pBuf))
	{
	    m_ulScreenDepthPreference =
		(UINT32)atol((const char*)pBuf->GetBuffer());
	    HX_RELEASE(pBuf);
	}
	if(HXR_OK == pPrefs->ReadPref("screen_height", pBuf))
	{
	    m_ulScreenHeightPreference =
		(UINT32)atol((const char*)pBuf->GetBuffer());
	    HX_RELEASE(pBuf);
	}
	if(HXR_OK == pPrefs->ReadPref("screen_width", pBuf))
	{
	    m_ulScreenWidthPreference =
		(UINT32)atol((const char*)pBuf->GetBuffer());
	    HX_RELEASE(pBuf);
	}
	if(HXR_OK == pPrefs->ReadPref("caption_switch", pBuf))
	{
	    m_bCaptionsPreference = 
		(UINT32)atol((const char*)pBuf->GetBuffer());
	    HX_RELEASE(pBuf);
	}
	if(HXR_OK == pPrefs->ReadPref("overdub_or_caption", pBuf))
	{
	    const char* pStr = (const char*)pBuf->GetBuffer();
	    m_pOverdubOrCaptionPreference = new_string(pStr);
	    HX_RELEASE(pBuf);
	}

	HX_RELEASE(pPrefs);
    }

    HX_RELEASE(pRegistry);
}

void
CSmil1Parser::close()
{
    HX_DELETE(m_pPacketQueue);
    HX_DELETE(m_pEndLayout);
    HX_DELETE(m_pTrackHintList);
    HX_RELEASE(m_pResponse);
    HX_RELEASE(m_pErrorText);
    if (m_pParser)
    {
	m_pParser->Close();
	HX_RELEASE(m_pParser);
    }
    HX_RELEASE(m_pISystemRequired);

    if(m_pIDMap)
    {
	CHXMapStringToOb::Iterator i = m_pIDMap->Begin();
	for(; i != m_pIDMap->End(); ++i)
	{
	    SMIL1Node* pNode = (SMIL1Node*)(*i);
	    HX_DELETE(pNode->m_pElement);
	}
	HX_DELETE(m_pIDMap);
    }

    if(m_pAddGroupMap)
    {
	CHXMapLongToObj::Iterator i = m_pAddGroupMap->Begin();
	for(; i != m_pAddGroupMap->End(); ++i)
	{
	    CSmil1AddGroup* pAddGroup = (CSmil1AddGroup*)(*i);
	    delete pAddGroup;
	}
	HX_DELETE(m_pAddGroupMap);
    }

    if(m_pSourceUpdateList)
    {
	CHXSimpleList::Iterator i = m_pSourceUpdateList->Begin();
	for(; i != m_pSourceUpdateList->End(); ++i)
	{
	    CSmil1SourceUpdate* pUpdate = (CSmil1SourceUpdate*)(*i);
	    delete pUpdate;
	}
	HX_DELETE(m_pSourceUpdateList);
    }

    if (m_pActiveNamespaceMap)
    {
	CHXMapStringToOb::Iterator ndxBuffer = m_pActiveNamespaceMap->Begin();
	for (; ndxBuffer != m_pActiveNamespaceMap->End(); ++ndxBuffer)
	{
	    IHXBuffer* pBuffer = (IHXBuffer*)(*ndxBuffer);
	    HX_RELEASE(pBuffer);
	}
	HX_DELETE(m_pActiveNamespaceMap);
    }

    if (m_pNSConflictList != NULL)
    {
	CHXSimpleList::Iterator ndx = m_pNSConflictList->Begin();
	for (; ndx != m_pNSConflictList->End(); ++ndx)
	{
	    SMIL1Namespace* pNS = (SMIL1Namespace*)(*ndx);
	    HX_DELETE(pNS);
	}
	HX_DELETE(m_pNSConflictList);
    }

    delete m_pNodeListStack;
    if(m_pNodeList)
    {
	delete m_pNodeList->m_pParentNode;
    }
}

HX_RESULT
CSmil1Parser::init(BOOL bStoreErrors)
{
    HX_RESULT rc = HXR_OK;

    close();
    m_pNodeListStack = new CHXStack;
    m_pPacketQueue = new CHXSimpleList;
    m_pIDMap = new CHXMapStringToOb;
    m_pAddGroupMap = new CHXMapLongToObj;
    m_bStoreErrors = bStoreErrors;
    if (m_bStoreErrors)
    {
	// XXXJHUG  error stuff.
	// In the future if there was any reason, we could 
	// store the errors in the nodes that the errors occurred in.
	// for now when we get an error notification, we will
	// just call the storeError function which will add
	// a new IHXBuffer to this array..  This will also be
	// called when problems are found with tags...
	// this will save having to walk the tree when it 
	// is time to dump the errors.
	m_pErrors = new CHXPtrArray;
    }

    SMIL1Node* pRootNode = new SMIL1Node;
    pRootNode->m_id = "root";
    pRootNode->m_name = "root";
    m_pNodeList = new SMIL1NodeList;
    pRootNode->m_pNodeList = m_pNodeList;
    m_pNodeList->m_pParentNode = pRootNode;
    m_pNodeListStack->Push(pRootNode);

#ifdef USE_EXPAT_FOR_SMIL
    rc = m_pClassFactory->CreateInstance(CLSID_IHXXMLParser, (void**)&m_pParser);
    if (FAILED(rc))
    {
	// they don't have the parser...  use old one?
	// Don't QI core for IID_IHXXMLParser; use our own instance.
	m_pParser = new HXXMLParser;
	if (m_pParser)
	{
	    rc = HXR_OK;
	    m_pParser->AddRef();
	}
	else
	{
	    rc = HXR_OUTOFMEMORY;
	}
    }
    HX_RELEASE(pFact);
    if (SUCCEEDED(rc))
    {
	m_pResponse = new CSmil1ParserResponse(this);
	m_pResponse->AddRef();
	// Expat is created off the CCF.
	// In strict mode it requires 100% compliant XML.   We will
	// create a "loose" version that allows &'s in attribute values.
	// (this parser is still MUCH stricter than the original XML parser)
	rc = m_pParser->Init(m_pResponse, "iso-8859-1", FALSE);	
    }

#else
    // Don't QI core for IID_IHXXMLParser; use our own instance.
    HXXMLParser* parser = new HXXMLParser();

    if (parser)
    {
	parser->AddRef();
    }
    else
    {
	rc = HXR_OUTOFMEMORY;
    }
    
    if (SUCCEEDED(rc))
    {
	m_pResponse = new CSmil1ParserResponse(this);
	m_pResponse->AddRef();
	rc = parser->Init(m_pResponse, NULL, TRUE);	// strict parser
    }
    if (m_bStoreErrors && parser)
    {
	parser->InitErrorNotifier(m_pResponse);
    }
    m_pParser = (IHXXMLParser*)parser;


#endif	
    
    return rc;
}

HX_RESULT
CSmil1Parser::parse(IHXBuffer* pBuffer, BOOL bIsFinal)
{
    HX_RESULT rc = HXR_OK;

    rc = m_pParser->Parse(pBuffer, bIsFinal);
    if(HXR_OK != rc)
    {
	m_pParser->GetCurrentLineNumber(m_ulErrorLineNumber);
	m_pParser->GetCurrentColumnNumber(m_ulErrorColumnNumber);
	HX_RELEASE(m_pErrorText);
	m_pParser->GetCurrentErrorText(m_pErrorText);
    }
    return rc;
}

HX_RESULT
CSmil1Parser::durationResolved(const char* pID, UINT32 ulDuration)
{
    SMIL1Node*	pNode = NULL;
    IHXBuffer* pBuf = NULL;

    if(m_pIDMap->Lookup(pID, (void*&)pNode))
    {	
	if (pNode->m_pElement->m_bIndefiniteDuration)
	{
	    goto cleanup;
	}
	
	// add duration to parent element
	if(pNode &&
	   pNode->m_pElement &&
	   pNode->m_pElement->m_pTimelineElement)
	{
	    pNode->m_pElement->m_pTimelineElement->setDuration(ulDuration);
	}
    }

cleanup:

    HX_RELEASE(pBuf);

    return HXR_OK;
}

BOOL AncestorEventsAreResolved(SMIL1Node* pNode)
{
    if (!pNode  ||  !pNode->m_pElement  ||
	    !pNode->m_pElement->m_pTimelineElement  ||  pNode->m_tag == SMILBody)
    {
	return TRUE;
    }
    //Now, look to see if its duration and delay events, if any, are
    // resolved:
    if ( ( (pNode->m_pElement->m_pTimelineElement->durationEvent()  &&
	!pNode->m_pElement->m_pTimelineElement->durationSet())  ||
	(pNode->m_pElement->m_pTimelineElement->delayEvent()  &&
	!pNode->m_pElement->m_pTimelineElement->initialDelaySet()) )  &&
	//[SMIL 1.0 compliance] Helps fix PR 32578:
	//However, if we have a duration event and it's based on a child's
	// timing (as can happen via endsync="id(child)", then we want to
	// avoid this element waiting for its parent to be resolved while
	// the parent is waiting for this element to be resolved:
	(!pNode->m_pElement->m_pTimelineElement->durationEvent()  ||
	SMILEventSourceID != pNode->m_pElement->m_nEndsyncEventSourceTag) )
    {
	return FALSE; //We still need to await event resolution.
    }
    //pNode is ok but its dependency ancestors may still be unresolved and
    // thus pNode may still have timing constraints from its dependency
    // ancestors due to their unresolved event(s):
    return AncestorEventsAreResolved(pNode->m_pParent);
}

void
CSmil1Parser::insertTimelineElement(const char* pID, UINT32 ulDelay)
{
    SMIL1Node* pNode = 0;
    if(m_pIDMap->Lookup(pID, (void*&)pNode))
    {
	if(pNode &&
	    pNode->m_pElement &&
	    !pNode->m_pElement->m_bInsertedIntoTimeline  &&
	    //[SMIL 1.0 compliance] Helps fix PR 16629:
	    // We don't want to insert a node into the timeline if
	    // its begin or end is dependent on another (not-yet-
	    // resolved) element:
	    ( (!pNode->m_pElement->m_pTimelineElement->durationEvent()  ||
	    pNode->m_pElement->m_pTimelineElement->durationSet())  &&
	    (!pNode->m_pElement->m_pTimelineElement->delayEvent()  ||
	    pNode->m_pElement->m_pTimelineElement->initialDelaySet()) )
	    //[SMIL 1.0 compliance] Helps fix 14420:
	    // First, we need to look all the way up the tree of ancestors
	    // to see if any of them have event-based delays or durations
	    // and to make sure the appropriate time(s) are resolved.  If
	    // not, we'll have to await those event resolutions before
	    // inserting this element into the timeline:
	    &&  AncestorEventsAreResolved(pNode)
	    )
	{
	    // /[SMIL 1.0 Compliance] Fixes PR 27644:
	    // if our begin offset is same or greater than our parent's
	    // end offset, then we should be ignored:
	    if ( pNode->m_pParent  &&  pNode->m_pParent->m_pElement  &&
		    pNode->m_pElement->m_ulBeginOffset != ((UINT32)-1)  &&
		    pNode->m_pParent->m_pElement->m_ulEndOffset != 
		    ((UINT32)-1)  &&
		    (pNode->m_pElement->m_ulBeginOffset >
		    pNode->m_pParent->m_pElement->m_ulEndOffset) )
	    {
		return; //Don't insert this because it can't ever play.
	    }

	    // skip the element if its duration == 0
	    if (0 == pNode->m_pElement->m_ulDuration)
	    {
		durationResolved(pNode->m_id, 0);
	    }
	    else
	    {
		pNode->m_pElement->m_ulDelay = ulDelay;
		pNode->m_pElement->m_ulTimestamp = INITIAL_STREAM1_TIMESTAMP;
		pNode->m_pElement->m_bInsertedIntoTimeline = TRUE;
		insertElementByTimestamp(pNode->m_pElement);
	    }
	}
    }
}

void
CSmil1Parser::resetTimelineElementDuration(const char* pID, 
					  UINT32 ulDuration)
{
    SMIL1Node* pNode = NULL;
    if(m_pIDMap->Lookup(pID, (void*&)pNode))
    {
	CSmil1SourceUpdate* pUpdate = new CSmil1SourceUpdate;
	pUpdate->m_ulTimestamp = INITIAL_STREAM1_TIMESTAMP;
	pUpdate->m_srcID = pID;
	pUpdate->m_ulUpdatedDuration = ulDuration;

	if(!m_pSourceUpdateList)
	{
	    m_pSourceUpdateList = new CHXSimpleList;
	}
	m_pSourceUpdateList->AddTail(pUpdate);
	insertElementByTimestamp(pUpdate);
    }
}

CSmil1Element*
CSmil1Parser::findElement(const char* pID)
{
    SMIL1Node* pNode = NULL;
    if(m_pIDMap->Lookup(pID, (void*&)pNode))
    {
	return pNode->m_pElement;
    }
    return NULL;
}

const char*
CSmil1Parser::assignID(const char* pPrefix)
{
    SafeSprintf(m_pVarName, 256, "%s_%ld", pPrefix, GetUniqueNumber());
    return m_pVarName;
}

UINT16
CSmil1Parser::getFragmentGroup(const char* pFragment)
{
    if(pFragment)
    {
	SMIL1Node* pNode = 0;
	if(m_pIDMap->Lookup(pFragment, (void*&)pNode))
	{
	    if(!pNode->m_bDelete)
	    {
		if(pNode->m_tag == SMILAAnchor ||
		    pNode->m_tag == SMILSwitch)
		{
		    SMIL1Node* pChildNode = getTimelineDescendent(pNode, NULL);
		    while(pChildNode)
		    {
			if(!pChildNode->m_bDelete)
			{
			    return pChildNode->m_nGroup;
			}
			pChildNode = getTimelineDescendent(pNode, pChildNode);
		    }
		}
		else if(pNode->m_tag == SMILAnchor)
		{
		    SMIL1Node* pParentNode = pNode->m_pParent;
		    if(pParentNode &&
			!pParentNode->m_bDelete)
		    {
			return pParentNode->m_nGroup;
		    }
		}
		else
		{
		    return pNode->m_nGroup;
		}
	    }
	}
    }
    return 0;
}

UINT32
CSmil1Parser::getFragmentOffset(const char* pFragment,
	//This BOOL will be set to FALSE if the fragment
	// does not exist or does not yet have a resolved
	// begin time.  This was necessary to fix PR 22655:
	BOOL& bFragFoundAndResolved)
{
    bFragFoundAndResolved = FALSE;
    if(pFragment)
    {
	UINT32 ulAnchorBegin = 0;
	SMIL1Node* pNode = NULL;
	CSmil1Element* pElement = NULL;
	CSmil1Element* pActualElement = NULL;
	if(m_pIDMap->Lookup(pFragment, (void*&)pNode) &&
	    pNode->m_pElement)
	{
	    pElement = pNode->m_pElement;
	    if(pNode->m_tag == SMILSwitch ||
		pNode->m_tag == SMILAAnchor)
	    {
		SMIL1Node* pChildNode = getTimelineDescendent(
		    pNode, NULL);
		while(pChildNode)
		{
		    if(!pChildNode->m_bDelete)
		    {
			pActualElement = pChildNode->m_pElement;
			break;
		    }
		    pChildNode = getTimelineDescendent(
			pNode, pChildNode);
		}
	    }
	    else if(pNode->m_tag == SMILAnchor)
	    {
		if(pElement->m_ulBeginOffset != (UINT32)-1)
		{
		    ulAnchorBegin = pElement->m_ulBeginOffset;
		}
		SMIL1Node* pParent = pNode->m_pParent;
		if(pParent)
		{
		    pActualElement = pParent->m_pElement;
		}
	    }
	    else
	    {
		pActualElement = pElement;
	    }

	    if(pActualElement)
	    {
		//[SMIL 1.0 Compliance] Fixes PR 26464:
		// Use delay (which already includes begin offset)
		// if it's a valid value, else use begin offset
		// without delay added (see comment below):
		if(pActualElement->m_ulDelay != (UINT32)-1)
		{
		    bFragFoundAndResolved = TRUE;
		    return pActualElement->m_ulDelay +
			ulAnchorBegin;
		}
		else if(pActualElement->m_ulBeginOffset != (UINT32)-1)
		{ 
		    bFragFoundAndResolved = TRUE; 
		    //Changed this while fixing PR 26464:
		    // This used to return pActualElement->m_ulDelay +
		    // pActualElement->m_ulBeginOffset + ulAnchorBegin but
		    // the delay can already account for the begin if both
		    // are set so we'd end up seeking past where we were
		    // supposed to go by the amount of the begin offset. 
		    // Also, we weren't even checking to see
		    // if delay was valid before using it (and now we're
		    // sure it is invalid per check above): 
		    return pActualElement->m_ulBeginOffset +
			ulAnchorBegin; 
		} 
		else
		{
		    return 0;
		}
	    }
	}
    }
    return 0;
}

SMIL1Node*
CSmil1Parser::findFirstNode(SMIL1NodeList* pNodeList, SMIL1NodeTag tag)
{
    if(!pNodeList)
    {
	return 0;
    }

    SMIL1Node* pFoundNode = 0;
    CHXSimpleList::Iterator i;
    for(i=pNodeList->Begin();i!=pNodeList->End();++i)
    {
	SMIL1Node* pNode = (SMIL1Node*)(*i);
	if(pNode->m_tag == tag)
	{
	    pFoundNode = pNode;
	}
	else
	{
	    pFoundNode = findFirstNode(pNode->m_pNodeList, tag);
	}
	if(pFoundNode)
	{
	    break;
	}
    }
    return pFoundNode;
}

SMIL1Node*
CSmil1Parser::findFirstNode(SMIL1NodeTag tag)
{
    return findFirstNode(m_pNodeList, tag);
}

SMIL1Node*
CSmil1Parser::getFirstNodeChild(SMIL1Node* pNode)
{
    m_pCurNode = pNode;
    if(!m_pCurNode)
    {
	return 0;
    }
   return m_pCurNode->getFirstChild();
}

SMIL1Node*
CSmil1Parser::getNextNodeChild()
{
    if(!m_pCurNode)
    {
	return 0;
    }
    return m_pCurNode->getNextChild();
}

HX_RESULT
CSmil1Parser::parseClockValue(const char* pValue, UINT32& ulTimeValue)
{
    // try npt
    char* pPtr = (char *)strstr(pValue, "npt=");
    if(pPtr)
    {
	pPtr += 4;  // point to beginning of clock value
	//[SMIL 1.0 compliance] fixes PR 26445: if "npt=4h" is specified,
	// we need to convert to "14400s" otherwise the 4 is treated as
	// seconds:
	char* pHourChar = strchr(pPtr, 'h');
	if (pHourChar  &&  !strchr(pPtr, ':')) //then it's hours without ':'
	{
	    IHXBuffer* pBuf = new CHXBuffer;
	    if (pBuf)
	    {
		pBuf->AddRef();

		*pHourChar = '\0'; //get rid of the 'h' in pPtr.
		pBuf->Set((const unsigned char *)pPtr,
		    strlen(pPtr) + strlen(":00:00") + 1);
		char* pTmp = (char*)pBuf->GetBuffer();
		strcat(pTmp, ":00:00"); /* Flawfinder: ignore */
		NPTime clockTime((const char*)pTmp);
		ulTimeValue = (UINT32)clockTime;
		pBuf->Release();
	    }
	    else
	    {
		return HXR_OUTOFMEMORY;
	    }
	}
	//END fix for PR 26445.
	else
	{
	    NPTime clockTime(pPtr);
	    ulTimeValue = (UINT32)clockTime;
	}
	return HXR_OK;
    }
    // try smpte
    pPtr = (char *)strstr(pValue, "smpte=");
    if(pPtr)
    {
	pPtr += 6;  // point to beginning of clock value
	SMPTETimeCode clockTime(pPtr);
	ulTimeValue = (UINT32)clockTime;
	return HXR_OK;
    }
    pPtr = (char *)strstr(pValue, "smpte-30-drop=");
    if(pPtr)
    {
	pPtr += 14;  // point to beginning of clock value
	SMPTETimeCode clockTime(pPtr);
	ulTimeValue = (UINT32)clockTime;
	return HXR_OK;
    }
    pPtr = (char *)strstr(pValue, "smpte-25=");
    if(pPtr)
    {
	pPtr += 9;  // point to beginning of clock value
	SMPTETimeCode clockTime;
        clockTime.m_framesPerSec = SMPTETimeCode::FPS_25;
        clockTime.fromString(pPtr);
	ulTimeValue = (UINT32)clockTime;
	return HXR_OK;
    }
    else if(strchr(pValue, ':'))     // try just hh:mm:ss with no prefix/suffix
    {
	NPTime clockTime(pValue);
	ulTimeValue = (UINT32)clockTime;
	return HXR_OK;
    }

    // ok, try h/min/s/ms

    char* pEndPtr = 0;
    double dVal = strtod(pValue, &pEndPtr);
    if(strcmp(pEndPtr, "h") == 0)
    {
	ulTimeValue = (UINT32)(dVal * 60.0 * 60.0 * 1000.0);
	return HXR_OK;
    }
    else if(strcmp(pEndPtr, "min") == 0)
    {
	ulTimeValue = (UINT32)(dVal * 60.0 * 1000.0);
	return HXR_OK;
    }
    else if(strcmp(pEndPtr, "s") == 0  ||
	    //[SMIL 1.0 compliance] Fixes PR 22673: the SMIL doc says that we
	    // need to default to seconds if no unit-type is given:
	    //     Timecount-val         ::= Timecount ("." Fraction)?
	    //              ("h" | "min" | "s" | "ms")? ; default is "s"
	    (!strlen(pEndPtr)) )
    {
	ulTimeValue = (UINT32)(dVal * 1000.0);
	return HXR_OK;
    }
    else if(strcmp(pEndPtr, "ms") == 0)
    {
	ulTimeValue = (UINT32)(dVal);
	return HXR_OK;
    }
    //else something other than "h", "min", "s", "", or "ms" was specified:
    else
    {
	return HXR_FAIL;
    }
}

HX_RESULT
CSmil1Parser::parseAnchorCoords(const char* pCoords, CSmil1AnchorElement* pAnchor)
{
    HX_RESULT rc = HXR_OK;
    double coordArray[4];
    BOOL percentArray[4];

    int i = 0;
    for(i=0; i<4; ++i)
    {
	coordArray[i] = 0.0;
	percentArray[i] = FALSE;
    }

    char* pCoordCopy = new_string(pCoords);
    char* pTok = strtok(pCoordCopy, ",");
    for(i=0;i<4,pTok;++i)
    {
	char* pEndPtr = 0;
	double dVal = strtod(pTok, &pEndPtr);
	coordArray[i] = dVal;
	percentArray[i] = (*pEndPtr == '%') ? TRUE: FALSE;

	pTok = strtok(NULL, ",");
    }
    delete[] pCoordCopy;

    pAnchor->m_ulOriginalLeftX = pAnchor->m_ulLeftX = 
	(UINT32)coordArray[0];
    pAnchor->m_bLeftXIsPercent = percentArray[0];
    pAnchor->m_ulOriginalTopY = pAnchor->m_ulTopY = 
	(UINT32)coordArray[1];
    pAnchor->m_bTopYIsPercent = percentArray[1];
    pAnchor->m_ulOriginalRightX = pAnchor->m_ulRightX = 
	(UINT32)coordArray[2];
    pAnchor->m_bRightXIsPercent = percentArray[2];
    pAnchor->m_ulOriginalBottomY = pAnchor->m_ulBottomY = 
	(UINT32)coordArray[3];
    pAnchor->m_bBottomYIsPercent = percentArray[3];
    
    pAnchor->m_bCoordsSet = TRUE;
    return rc;
}


HX_RESULT
CSmil1Parser::parseDuration(const char* pDuration, CSmil1Element* pElement,
    SMILSyncAttributeTag nTag)
{
    HX_RESULT rc = HXR_OK;

    if(!pDuration)
    {
	return HXR_FAIL;
    }

    const char* pCh = pDuration;

    // check for event-source
    // syntax is: id(a)(4s)
    if(strncmp(pCh, "id(", 3) == 0)
    {
	BOOL bParseError = FALSE;
	BOOL bHasEvent = TRUE;

	UINT32 clockValue = 0;
	char* pIdTag = new char[strlen(pDuration)+1];
	char* pEvent = new char[strlen(pDuration)+1];
	pIdTag[0] = 0;
	pEvent[0] = 0;

	pCh += 3;	// skip over 'id('

	int i = 0;
	while(*pCh && (*pCh != ')'))
	{
	    pIdTag[i++] = *pCh++;
	}
	if(*pCh == ')')
	{
	    pIdTag[i] = 0;

	    // lookup ID to see if it references an existing entity,
	    // otherwise it is an error
	    void* pDummy = NULL;
	    if(!m_pIDMap->Lookup(pIdTag, pDummy))
	    {
		rc = HXR_FAIL;
		CSmil1SMILSyntaxErrorHandler errHandler(m_pContext);
		errHandler.ReportError(SMILErrorBadDuration, pDuration, 
		    pElement->m_pNode->m_ulTagStartLine);
		bParseError = TRUE;
	    }
	    else
	    {
		switch(nTag)
		{
		    case SMILSyncAttrBegin:
		    {
			pElement->m_BeginEventSourceID = pIdTag;
		    }
		    break;

		    case SMILSyncAttrEnd:
		    {
			pElement->m_EndEventSourceID = pIdTag;
		    }
		    break;

		    case SMILSyncAttrEndsync:
		    {
			pElement->m_EndsyncEventSourceID = pIdTag;
		    }
		    break;

		    default:
		    break;
		}
	    }

	    delete[] pIdTag;

	    if(strlen(pCh) > 2)
	    {
		if(nTag != SMILSyncAttrEndsync)
		{
		    pCh++;	// skip over ')'
		    pCh++;  // skip over '('
		    i = 0;
		    while(*pCh && (*pCh != ')'))
		    {
			pEvent[i++] = *pCh++;
		    }
		    if(*pCh == ')')
		    {
			pEvent[i] = 0;
		    }
		    else
		    {
			bParseError = TRUE;
		    }
		}
		else
		{
		    bParseError = TRUE;
		}
	    }
	    else
	    {
		if(nTag == SMILSyncAttrEndsync)
		{
		    pElement->m_nEndsyncEventSourceTag = SMILEventSourceID;
		    bHasEvent = FALSE;
		}
		else
		{
		    bParseError = TRUE;
		}
	    }
	}
	else
	{
	    bParseError = TRUE;
	}
	if(bParseError)
	{
	    rc = HXR_FAIL;
	    CSmil1SMILSyntaxErrorHandler errHandler(m_pContext);
	    errHandler.ReportError(SMILErrorBadDuration, pDuration, 
		pElement->m_pNode->m_ulTagStartLine);
	}
	else if(bHasEvent)
	{
	    SMILEventSourceTag eSourceTag = SMILEventSourceNone;
	    UINT32 ulEventClockValue = 0;
	    if(strcmp(pEvent, "begin") == 0)
	    {
		eSourceTag = SMILEventSourceBegin;
	    }
	    else if(strcmp(pEvent, "end") == 0)
	    {
		eSourceTag = SMILEventSourceEnd;
	    }
	    else
	    {
		if(HXR_OK == parseClockValue(pEvent, ulEventClockValue))
		{
		    eSourceTag = SMILEventSourceClock;
		}
		else
		{
		    rc = HXR_FAIL;
		    CSmil1SMILSyntaxErrorHandler errHandler(m_pContext);
		    errHandler.ReportError(SMILErrorBadDuration, pDuration, 
			pElement->m_pNode->m_ulTagStartLine);
		}
	    }

	    switch(nTag)
	    {
		case SMILSyncAttrBegin:
		{
		    pElement->m_nBeginEventSourceTag = eSourceTag;
		    pElement->m_ulBeginEventClockValue = ulEventClockValue;
		}
		break;

		case SMILSyncAttrEnd:
		{
		    pElement->m_nEndEventSourceTag = eSourceTag;
		    pElement->m_ulEndEventClockValue = ulEventClockValue;
		}
		break;

		case SMILSyncAttrEndsync:
		{
		    pElement->m_nEndsyncEventSourceTag = eSourceTag;
		}
		break;

		default:
		break;
	    }
	}
	delete[] pEvent;
    }
    else if(strcmp(pCh, "first") == 0)
    {
	if(nTag == SMILSyncAttrEndsync)
	{
	    pElement->m_nEndsyncEventSourceTag = SMILEventSourceFirst;
	}
    }
    else if(strcmp(pCh, "last") == 0)
    {
	if(nTag == SMILSyncAttrEndsync)
	{
	    pElement->m_nEndsyncEventSourceTag = SMILEventSourceLast;
	}
    }
    else if(strcmp(pCh, "indefinite") == 0)
    {
	if (pElement->m_pNode->m_tag == SMILSeq || 
	    pElement->m_pNode->m_tag == SMILPar)
	{
	    rc = HXR_FAIL;
	    CSmil1SMILSyntaxErrorHandler errHandler(m_pContext);
	    errHandler.ReportError(SMILErrorIndefiniteNotSupported, NULL, 
		pElement->m_pNode->m_ulTagStartLine);
	}
	else
	{
	    pElement->m_bIndefiniteDuration = TRUE;
	}
    }
    else
    {
	UINT32 ulClockValue = 0;
	if(HXR_OK == parseClockValue(pCh, ulClockValue))
	{
	    switch(nTag)
	    {
		case SMILSyncAttrBegin:
		{
		    pElement->m_ulBeginOffset = ulClockValue;
		}
		break;

		case SMILSyncAttrEnd:
		{
		    pElement->m_ulEndOffset = ulClockValue;
		}
		break;

		case SMILSyncAttrDur:
		{
		    pElement->m_ulDuration = ulClockValue;
		}
		break;

		case SMILSyncAttrEndsync:
		{
		    pElement->m_ulEndSync = ulClockValue;
		}
		break;

		case SMILSyncAttrClipBegin:
		{
		    pElement->m_ulClipBegin = ulClockValue;
		}
		break;

		case SMILSyncAttrClipEnd:
		{
		    pElement->m_ulClipEnd = ulClockValue;
		}
		break;
		    
		default:
		break;
	    }
	}
	else
	{
	    rc = HXR_FAIL;
	    CSmil1SMILSyntaxErrorHandler errHandler(m_pContext);
	    errHandler.ReportError(SMILErrorBadDuration, pCh, 
		pElement->m_pNode->m_ulTagStartLine);
	}
    }
    return rc;
}

HX_RESULT
CSmil1Parser::adjustDuration(CSmil1Element* pElement)
{
    HX_RESULT rc = HXR_OK;

    // check for duration errors
    if(pElement->m_ulEndOffset != (UINT32)-1)
    {
	if(pElement->m_ulBeginOffset != (UINT32)-1)
	{
	    if(pElement->m_ulEndOffset < 
		pElement->m_ulBeginOffset)
	    {
		pElement->m_ulDuration = 0;
		goto exit;
	    }
	    if(pElement->m_ulDuration != (UINT32)-1)
	    {
		if(pElement->m_ulDuration !=
		    pElement->m_ulEndOffset -
		    pElement->m_ulBeginOffset)
		{
		    // If the element has both an explicit dur and an explicit
		    // end, the desired end is the minimum of: the sum of the 
		    // desired begin and the explicit dur; and the explicit end. 
		    
		    // override "dur"
		    if ( pElement->m_ulBeginOffset + pElement->m_ulDuration > 
			pElement->m_ulEndOffset )
		    {
			// we want to override the duration, because it is 
			// greater than the end offset.
			pElement->m_ulDuration =
			    pElement->m_ulEndOffset -
			    pElement->m_ulBeginOffset;
		    }
		    else
		    {
			// else we want to use the current duration, 
			// and override the end offset.
			pElement->m_ulEndOffset = pElement->m_ulBeginOffset
			    + pElement->m_ulDuration;
		    }
			
		    goto exit;
		}
	    }
	}
	else
	{
	    if(pElement->m_ulDuration != (UINT32)-1)
	    {
		if(pElement->m_ulEndOffset >
		    pElement->m_ulDuration)
		{
		    pElement->m_ulDuration = pElement->m_ulEndOffset;
		    goto exit;
		}
	    }
	}
    }
   
    // adjust for begin/end/dur attributes
    if(pElement->m_ulDuration == (UINT32)-1) // duration not set
    {
	if(pElement->m_ulEndOffset != (UINT32)-1)
	{
	    // has an end but no duration
	    if(pElement->m_ulBeginOffset != (UINT32)-1)
	    {
		pElement->m_ulDuration = pElement->m_ulEndOffset -
					pElement->m_ulBeginOffset;
	    }
	    else
	    {
		pElement->m_ulDuration = pElement->m_ulEndOffset;
	    }
	}
    }
    else    // explicit duration set
    {
	if(pElement->m_ulEndOffset != (UINT32)-1)
	{
	    // has a duration and an end
	    UINT32 ulDur = 0;
	    if(pElement->m_ulBeginOffset != (UINT32)-1)
	    {
		ulDur = pElement->m_ulEndOffset - 
		         pElement->m_ulBeginOffset;
	    }
	    else
	    {
		ulDur = pElement->m_ulEndOffset;
	    }
	    pElement->m_ulDuration = ulDur;
	}
    }

exit:
    return rc;
}

UINT8
CSmil1Parser::getColorElement(const char* pColorFrag, int len)
{
    UINT8 ucValue = 0;

    char* pTmpBuf = new char[len+1];
    strncpy(pTmpBuf, pColorFrag, len); /* Flawfinder: ignore */
    pTmpBuf[len] = 0;

    ucValue = (UINT8)strtol(pTmpBuf, 0, 16);
    delete[] pTmpBuf;
    return ucValue;
}
    
HXxColor
CSmil1Parser::parseColor(const char* pColorString)
{
    HXxColor theColor = 0;
    UINT8 ucRed = 0;
    UINT8 ucGreen = 0;
    UINT8 ucBlue = 0;
    if(pColorString[0] == '#')
    {
	if(strlen(pColorString) == 4)
	{
	    /* #rgb, duplicate the numbers */
	    char tmpBuf[6]; /* Flawfinder: ignore */
	    tmpBuf[0] = tmpBuf[1] = pColorString[1];
	    tmpBuf[2] = tmpBuf[3] = pColorString[2];
	    tmpBuf[4] = tmpBuf[5] = pColorString[3];
	    ucRed = getColorElement(&tmpBuf[0], 2);
	    ucGreen = getColorElement(&tmpBuf[2], 2);
	    ucBlue = getColorElement(&tmpBuf[4], 2);
	}
	else if(strlen(pColorString) == 7)
	{
	    /* #rrggbb */
	    ucRed = getColorElement(&pColorString[1], 2);
	    ucGreen = getColorElement(&pColorString[3], 2);
	    ucBlue = getColorElement(&pColorString[5], 2);
	}
    }
    else
    {
	// string, try to get it from the color table
	int i = 0;
	const char* pColorName = Smil1ColorTable[i].m_pColorName;
	while(pColorName)
	{
	    if(strcmp(pColorName, pColorString) == 0)
	    {
		ucRed = Smil1ColorTable[i].m_ucRed;
		ucBlue = Smil1ColorTable[i].m_ucBlue;
		ucGreen = Smil1ColorTable[i].m_ucGreen;
		break;
	    }
	    pColorName = Smil1ColorTable[++i].m_pColorName;
	}
    }

#ifdef _WINDOWS
    theColor = (HXxColor)(RGB(ucRed, ucGreen, ucBlue));
#else
    theColor = (HXxColor)
	    (ucRed << 16 |
	    ucGreen << 8 |
	    ucBlue);
#endif
    return theColor;
}

void
CSmil1Parser::badAttributeError(SMIL1NodeTag tag, const char* pAttrName,
			       UINT32 ulLineNumber, BOOL bJustStore)
{
    const char* pTagName = "unknown";
    // get tag name from table
    int i = 0;
    SMIL1NodeTag thisTag = smil1TagTable[i].m_tag;
    while(thisTag != SMILUnknown)
    {
	if(thisTag == tag)
	{
	    pTagName = smil1TagTable[i].m_name;
	    break;
	}
	++i;
	thisTag = smil1TagTable[i].m_tag;
    }
    
    char tmpBuf[256]; /* Flawfinder: ignore */
    SafeSprintf(tmpBuf, 256, "<%s>: %s",
	pTagName, pAttrName);

    if (m_bStoreErrors)
    {
	storeError(SMILErrorUnrecognizedAttribute, tmpBuf, 0, 
	    ulLineNumber, 0, FALSE);
    }

    if (!bJustStore)
    {
	CSmil1SMILSyntaxErrorHandler errHandler(m_pContext);
	errHandler.ReportError(SMILErrorUnrecognizedAttribute, tmpBuf, ulLineNumber);
    }
}

CSmil1Meta*
CSmil1Parser::makeMeta(SMIL1Node* pNode)
{
    CSmil1Meta* pMeta = new CSmil1Meta(pNode);
    if(pNode->m_pValues)
    {
	const char* pName = 0;
	IHXBuffer* pBuf = 0;

	HX_RESULT rc = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);
	while(HXR_OK == rc)
	{
	    if(strcmp(pName, "name") == 0)
	    {
		pMeta->m_name = (const char*)pBuf->GetBuffer();
	    }
	    else if(strcmp(pName, "content") == 0)
	    {
		pMeta->m_content = (const char*)pBuf->GetBuffer();
	    }
	    HX_RELEASE(pBuf);
	    rc = pNode->m_pValues->GetNextPropertyCString(pName, pBuf);
	}
	HX_RELEASE(pBuf);

	// check for 'base'
	if(pMeta->m_name == "base")
	{
	    HX_DELETE(m_pBasePath);
	    m_pBasePath = new_string((const char*)pMeta->m_content);
	    HX_RELEASE(pBuf);
	}
    }
    return pMeta;
}

CSmil1RendererPreFetch*
CSmil1Parser::makeRendererPreFetch(SMIL1Node* pNode)
{
    CSmil1RendererPreFetch* pRenderer = 
	new CSmil1RendererPreFetch(pNode);
    if(pNode->m_pValues)
    {
	const char* pName = 0;
	IHXBuffer* pBuf = 0;
	HX_RESULT rc = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);
	while(HXR_OK == rc)
	{
	    if(strcmp(pName, "type") == 0)
	    {
		pRenderer->m_mimeType = (const char*)pBuf->GetBuffer();
	    }
	    rc = pNode->m_pValues->GetNextPropertyCString(pName, pBuf);
	}
    }
    return pRenderer;
}

CSmil1RootLayout*
CSmil1Parser::makeRootLayout(SMIL1Node* pNode)
{
    BOOL bHasHeight = FALSE;
    BOOL bHasWidth = FALSE;

    //[SMIL 1.0 compliance] Fixes PR 22674.  SMIL 1.0 documentation states:
    //  "If a document contains more than one "root-layout" element,
    //   this is an error, and the document should not be displayed."
    if (m_bSMILRootLayoutAlreadyFound  &&  m_bSMIL10FullCompliance)
    {
	CSmil1SMILSyntaxErrorHandler errHandler(m_pContext);
	errHandler.ReportError(SMILErrorUnexpectedTag,
		(const char*)pNode->m_name, pNode->m_ulTagStartLine);
	return NULL;
    }
    else if (m_bStoreErrors && m_bSMILRootLayoutAlreadyFound)
    {
	storeError(SMILErrorUnexpectedTag, pNode->m_name, 0, 
		pNode->m_ulTagStartLine, 0, FALSE);
    }


    m_bSMILRootLayoutAlreadyFound = TRUE;

    CSmil1RootLayout* pLayout = new CSmil1RootLayout(pNode);
    if(pNode->m_pValues)
    {
	const char* pName = 0;
	IHXBuffer* pBuf = 0;
	HX_RESULT rc = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);
	while(HXR_OK == rc)
	{
	    //[SMIL 1.0 compliance] Fixes PR 24628.  SMIL 1.0
	    // documentation states in section 3.3.1 that only "length"
	    // values are allowed in a root-layout height or width;
	    // percentages are not allowed:
	    // [SMIL 1.0 compliance] Also fixes PR 25694: don't allow
	    // ANYTHING other than numeric values with 'px' allowed as
	    // the only valid units string:
	    if (m_bSMIL10FullCompliance)
	    {
		if(!strcmp(pName, "height")  ||  !strcmp(pName, "width"))
		{
		    const char* pTmp = (const char*)pBuf->GetBuffer();
		    UINT32 ulBuflen = pBuf->GetSize();
		    UINT32 ulCounter = 0L;
		    BOOL bNumCharFound = FALSE;
		    BOOL bPxFoundAlready = FALSE;
		    while (pTmp  &&  *pTmp)
		    {
			//The SMIL 1.0 spec says to use a "length" value as
			// defined in the CSS2 spec, except that the "px"
			// units are optional.  The CSS2 spec can be found
			// here:
			//  http://www.w3.org/TR/REC-CSS2/syndata.html
			// with the specific definition found here in that
			// page:
			//    .../syndata.html#value-def-length
			// Look for: "[+|-](#*)[.][(#*)][px]"
			if (bPxFoundAlready  ||  
				( ('0' > *pTmp  ||  '9' < *pTmp)  &&
				'.' != *pTmp  &&
				(bNumCharFound  ||  ('-' != *pTmp  &&
				'+' != *pTmp)) ) )
			{
			    //If at least one number was found already
			    // and "px" follows, then this char (*pTmp)
			    // is not illegal here:
			    if (!bPxFoundAlready  &&
				    bNumCharFound  &&
				    'p' == *pTmp  &&
				    ulCounter < ulBuflen-1  &&
				    'x' == *(pTmp+1) )
			    {
				//the first "px" following a number is ok;
				bPxFoundAlready = TRUE;
				++pTmp; //jump past the 'x'.
			    }
			    else
			    {
				CSmil1SMILSyntaxErrorHandler errHandler(
				        m_pContext);
				errHandler.ReportError(SMILErrorBadAttribute,
				        (const char*)pBuf->GetBuffer(),
					pNode->m_ulTagStartLine);
				return NULL;
			    }
			}
			else if ('.' == *pTmp  ||  '-' == *pTmp  ||
				'+' == *pTmp)
			{
			    //Valid chars at start, so keep going.
			}
			else
			{
			    bNumCharFound = TRUE;
			}
			++pTmp;
			++ulCounter;
		    }
		}
	    }
	    if(strcmp(pName, "height") == 0)
	    {
		pLayout->m_ulHeight = atol((const char*)pBuf->GetBuffer());
		bHasHeight = TRUE;
	    }
	    else if(strcmp(pName, "width") == 0)
	    {
		pLayout->m_ulWidth = atol((const char*)pBuf->GetBuffer());
		bHasWidth = TRUE;
	    }
	    else if(strcmp(pName, "background-color") == 0)
	    {
                UINT32    ulColor = 0;
                HX_RESULT rv = HXParseColorUINT32((const char*) pBuf->GetBuffer(),
                                                  ulColor);
                if (SUCCEEDED(rv))
                {
                    pLayout->m_ulBgColor = ulColor;
                }
	    }
	    else if(strcmp(pName, "overflow") == 0)
	    {
		pLayout->m_overflow = (const char*)pBuf->GetBuffer();
	    }
	    else if(strcmp(pName, "title") == 0)
	    {
		pLayout->m_title = (const char*)pBuf->GetBuffer();
	    }
	    else if((strcmp(pName, "id") == 0) ||
		    (strcmp(pName, "skip-content") == 0))
	    {
		// dont' do anything with these attributes
	    }
	    
	    HX_RELEASE(pBuf);
	    rc = pNode->m_pValues->GetNextPropertyCString(pName, pBuf);
	}
    }

    //[SMIL 1.0 Compliance]: PR 24630:
    // height and width are not required in root layout, per the 1.0 spec:
    pLayout->m_bHeightUnspecified = !bHasHeight;
    pLayout->m_bWidthUnspecified = !bHasWidth;

    return pLayout;
}

CSmil1Region*
CSmil1Parser::makeRegion(SMIL1Node* pNode)
{
    CSmil1Region* pRegion = new CSmil1Region(pNode);

    if(pNode->m_pValues)
    {
	const char* pName = 0;
	IHXBuffer* pBuf = 0;
	HX_RESULT rc = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);
	while(HXR_OK == rc)
	{
	    //[SMIL 1.0 compliance] Fixes PR 25697.  SMIL 1.0
	    // documentation states in section 3.3.1 that only percentage and
	    // "length" values are allowed in a root-layout height or width;
	    if (m_bSMIL10FullCompliance)
	    {
		if(!strcmp(pName, "height")  ||  !strcmp(pName, "width")  ||
			!strcmp(pName, "top")  ||  !strcmp(pName, "left"))
		{
		    const char* pTmp = (const char*)pBuf->GetBuffer();
		    UINT32 ulBuflen = pBuf->GetSize();
		    UINT32 ulCounter = 0L;
		    BOOL bNumCharFound = FALSE;
		    BOOL bPxOrPercentFoundAlready = FALSE;
		    while (pTmp  &&  *pTmp)
		    {
			//The SMIL 1.0 spec says to use a percentage value
			// or a "length" value as defined in the CSS2 spec,
			// except that the "px" units are the only ones
			// allowed and are optional.
			// The CSS2 spec can be found here:
			//  http://www.w3.org/TR/REC-CSS2/syndata.html
			// with the specific definition found here in that
			// page:
			//    .../syndata.html#value-def-length
			// Look for: "[+|-](#*)[.][(#*)][px|%]"
			if (bPxOrPercentFoundAlready  ||
				( ('0' > *pTmp  ||  '9' < *pTmp)  &&
				'.' != *pTmp  &&
				(bNumCharFound  ||  ('-' != *pTmp  &&
				'+' != *pTmp)) ) )
			{
			    //If at least one number was found already
			    // and "px" or "%" follows, then this char
			    // (*pTmp) is not illegal here:
			    if (!bPxOrPercentFoundAlready  &&
				    bNumCharFound  &&
				    ( '%' == *pTmp  ||
				    ('p' == *pTmp  &&
				    ulCounter < ulBuflen-1  &&
				    'x' == *(pTmp+1))) )
			    {
				//The first "px" or "%" following a number
				// is ok;
				bPxOrPercentFoundAlready = TRUE;
				if ('p' == *pTmp)
				{
				    ++pTmp; //jump past the 'x'.
				}
			    }
			    else
			    {
				CSmil1SMILSyntaxErrorHandler errHandler(
				        m_pContext);
				errHandler.ReportError(SMILErrorBadAttribute,
				        (const char*)pBuf->GetBuffer(),
					pNode->m_ulTagStartLine);
				return NULL;
			    }
			}
			else if ('.' == *pTmp  ||  '-' == *pTmp  ||
				'+' == *pTmp)
			{
			    //Valid chars at start, so keep going.
			}
			else
			{
			    bNumCharFound = TRUE;
			}
			++pTmp;
			++ulCounter;
		    }
		}
	    }
	    if(strcmp(pName, "left") == 0)
	    {
		pRegion->m_left = (const char*)pBuf->GetBuffer();
	    }
	    else if(strcmp(pName, "top") == 0)
	    {
		pRegion->m_top = (const char*)pBuf->GetBuffer();
	    }
	    else if(strcmp(pName, "height") == 0)
	    {
		pRegion->m_height = (const char*)pBuf->GetBuffer();
	    }
	    else if(strcmp(pName, "width") == 0)
	    {
		pRegion->m_width = (const char*)pBuf->GetBuffer();
	    }
	    else if(strcmp(pName, "fit") == 0)
	    {
		pRegion->m_fit = (const char*)pBuf->GetBuffer();
	    }
	    else if(strcmp(pName, "z-index") == 0)
	    {
		pRegion->m_zIndex = atol((const char*)pBuf->GetBuffer());
	    }
	    else if(strcmp(pName, "background-color") == 0)
	    {
		const char* pActualColor = (const char*)pBuf->GetBuffer();
		if(strcmp(pActualColor, "transparent") == 0)
		{
		    pRegion->m_bBgColorSet = FALSE;
		}
		else
		{
                    UINT32 ulColor = 0;
                    HX_RESULT rv = HXParseColorUINT32((const char*) pBuf->GetBuffer(),
                                                      ulColor);
                    if (SUCCEEDED(rv))
                    {
                        pRegion->m_ulBgColor   = ulColor;
                        pRegion->m_bBgColorSet = TRUE;
                    }
		}
	    }
	    
	    pBuf->Release();
	    rc = pNode->m_pValues->GetNextPropertyCString(pName, pBuf);
	}
    }
    return pRegion;
}

CSmil1AAnchorElement*
CSmil1Parser::makeAAnchorElement(SMIL1Node* pNode)
{
    CSmil1AAnchorElement* pAnchor = 
	new CSmil1AAnchorElement(pNode);
    if(pNode->m_pValues)
    {
	const char* pName = 0;
	IHXBuffer* pBuf = 0;
	HX_RESULT rc = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);
	while(HXR_OK == rc)
	{
	    if(strcmp(pName, "href") == 0)
	    {
		pAnchor->m_href = (const char*)pBuf->GetBuffer();
	    }
	    else if(strcmp(pName, "show") == 0)
	    {
		pAnchor->m_show = (const char*)pBuf->GetBuffer();
	    }

	    pBuf->Release();
	    rc = pNode->m_pValues->GetNextPropertyCString(pName, pBuf);
	}
    }
    return pAnchor;
}

CSmil1AnchorElement*
CSmil1Parser::makeAnchorElement(SMIL1Node* pNode)
{
    CSmil1AnchorElement* pAnchor = new CSmil1AnchorElement(pNode);
    if(pNode->m_pValues)
    {
	const char* pName = 0;
	IHXBuffer* pBuf = 0;
	HX_RESULT rc = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);
	while(HXR_OK == rc)
	{
	    if(strcmp(pName, "href") == 0)
	    {
		pAnchor->m_href = (const char*)pBuf->GetBuffer();
	    }
	    else if(strcmp(pName, "show") == 0)
	    {
		pAnchor->m_show = (const char*)pBuf->GetBuffer();
	    }
	    else if(strcmp(pName, "coords") == 0)
	    {
		rc = parseAnchorCoords((const char*)pBuf->GetBuffer(),
		    pAnchor);
	    }
	    else if(strcmp(pName, "fragment-id") == 0)
	    {
		pAnchor->m_fragmentID = (const char*)pBuf->GetBuffer();
	    }
	    else if(strcmp(pName, "z-index") == 0)
	    {
		pAnchor->m_zIndex = atol((const char*)pBuf->GetBuffer());
	    }
	    else if(strcmp(pName, "begin") == 0)
	    {
		rc = parseDuration((const char*)pBuf->GetBuffer(), pAnchor, 
		    SMILSyncAttrBegin);
		if(HXR_OK == rc)
		{
		    pAnchor->m_bTimeValueSet = TRUE;
		}
	    }
	    else if(strcmp(pName, "end") == 0)
	    {
		rc = parseDuration((const char*)pBuf->GetBuffer(), pAnchor,
		    SMILSyncAttrEnd);
		if(HXR_OK == rc)
		{
		    pAnchor->m_bTimeValueSet = TRUE;
		}
	    }

	    pBuf->Release();
	    rc = pNode->m_pValues->GetNextPropertyCString(pName, pBuf);
	}
    }
    return pAnchor;
}

CSmil1Source*
CSmil1Parser::makeSource(SMIL1Node* pNode)
{
    HX_RESULT rc = HXR_OK;
    CSmil1Source* pSource = new CSmil1Source(pNode);

    // assign to a group
    if(pNode->m_nGroup == (UINT16)-1)
    {
	SMIL1Node* pParent = pNode->m_pParent;
	while(pParent)
	{
	    if(pParent->m_nGroup != (UINT16)-1)
	    {
		pNode->m_nGroup = pParent->m_nGroup;
		break;
	    }
	    pParent = pParent->m_pParent;
	}
    }

    if(pNode->m_pValues)
    {
	const char* pName = 0;
	IHXBuffer* pBuf = 0;
	rc = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);
	while(HXR_OK == rc)
	{
	    if(strcmp(pName, "src") == 0)
	    {
		CHXString src = (const char*)pBuf->GetBuffer();
		
		// trim leading/trailing spaces
		src.TrimRight();
		src.TrimLeft();

		if(m_pBasePath &&
		    isRelativeURL(src))
		{
		    pSource->m_src = CHXString(m_pBasePath) + src;
		}
		else
		{
		    pSource->m_src = src;
		}
	    }
	    else if(strcmp(pName, "region") == 0)
	    {
		//char szPersistentComponentID[MAX_DISPLAY_NAME] = {0};
		//itoa(m_ulPersistentComponentID, szPersistentComponentID, 10);
   
		// append persistent ID to the end of region id
		// to make it unique in nested meta
		pSource->m_region = (const char*)pBuf->GetBuffer();
		//pSource->m_region += "_";
		//pSource->m_region += szPersistentComponentID;
	    }
	    else if(strcmp(pName, "begin") == 0)
	    {
		rc = parseDuration((const char*)pBuf->GetBuffer(), pSource, 
		    SMILSyncAttrBegin);
	    }
	    else if(strcmp(pName, "end") == 0)
	    {
		rc = parseDuration((const char*)pBuf->GetBuffer(), pSource,
		    SMILSyncAttrEnd);
	    }
	    else if(strcmp(pName, "clip-begin") == 0)
	    {
		rc = parseDuration((const char*)pBuf->GetBuffer(), pSource,
		    SMILSyncAttrClipBegin);
	    }
	    else if(strcmp(pName, "clip-end") == 0)
	    {
		rc = parseDuration((const char*)pBuf->GetBuffer(), pSource,
		    SMILSyncAttrClipEnd);
	    }
	    else if(strcmp(pName, "dur") == 0)
	    {
		rc = parseDuration((const char*)pBuf->GetBuffer(), pSource,
		    SMILSyncAttrDur);
	    }
	    else if(strcmp(pName, "fill") == 0)
	    {
		pSource->m_fill = (const char*)pBuf->GetBuffer();
	    }
	    else if(strcmp(pName, "title") == 0)
	    {
		pSource->m_title = (const char*)pBuf->GetBuffer();
	    }
	    else if(strcmp(pName, "repeat") == 0)
	    {
		const char* pRepeatCount = (const char*)pBuf->GetBuffer();

		if(strcmp(pRepeatCount, "indefinite") == 0)
		{
		    pSource->m_ulRepeatValue = MAX_UINT32;
		}
		else
		{
		    pSource->m_ulRepeatValue = atol(pRepeatCount);
		}
	    }


	    pBuf->Release();
	    if(HXR_OK != rc)
	    {
		goto exit;
	    }

	    rc = pNode->m_pValues->GetNextPropertyCString(pName, pBuf);
	}
    }

    rc = adjustDuration(pSource);

exit:
    if(HXR_OK != rc)
    {
	HX_DELETE(pSource);
    }

    return pSource;
}

CSmil1SeqElement*
CSmil1Parser::makeSeqElement(SMIL1Node* pNode)
{
    HX_RESULT rc = HXR_OK;

    CSmil1SeqElement* pElement = 
	new CSmil1SeqElement(pNode);
    if(pNode->m_pValues)
    {
	const char* pName = 0;
	IHXBuffer* pBuf = 0;
	rc = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);
	while(HXR_OK == rc)
	{
	    if(strcmp(pName, "dur") == 0)
	    {
		rc = parseDuration((const char*)pBuf->GetBuffer(), pElement,
		    SMILSyncAttrDur);
	    }
            else if(strcmp(pName, "begin") == 0)
            {
		rc = parseDuration((const char*)pBuf->GetBuffer(), pElement,
		    SMILSyncAttrBegin);
            }
            else if(strcmp(pName, "end") == 0)
            {
		rc = parseDuration((const char*)pBuf->GetBuffer(), pElement,
		    SMILSyncAttrEnd);
            }
	    else if(strcmp(pName, "title") == 0)
	    {
		pElement->m_title = (const char*)pBuf->GetBuffer();
	    }
	    else if(strcmp(pName, "repeat") == 0)
	    {
		pElement->m_ulRepeatValue = atol((const char*)pBuf->GetBuffer());
	    }

	    pBuf->Release();

	    if(HXR_OK != rc)
	    {
		goto exit;
	    }

	    rc = pNode->m_pValues->GetNextPropertyCString(pName, pBuf);
	}
    }

    rc = adjustDuration(pElement);

exit:
    if(HXR_OK != rc)
    {
	HX_DELETE(pElement);
    }

    return pElement;
}

CSmil1ParElement*
CSmil1Parser::makeParElement(SMIL1Node* pNode)
{
    HX_RESULT rc = HXR_OK;

    CSmil1ParElement* pElement = 
	new CSmil1ParElement(pNode);
    if(pNode->m_pValues)
    {
	const char* pName = 0;
	IHXBuffer* pBuf = 0;
	rc = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);
	while(HXR_OK == rc)
	{
	    if(strcmp(pName, "dur") == 0)
	    {
		rc = parseDuration((const char*)pBuf->GetBuffer(), pElement,
		    SMILSyncAttrDur);
	    }
            else if(strcmp(pName, "begin") == 0)
            {
		rc = parseDuration((const char*)pBuf->GetBuffer(), pElement,
		    SMILSyncAttrBegin);
            }
            else if(strcmp(pName, "end") == 0)
            {
		rc = parseDuration((const char*)pBuf->GetBuffer(), pElement,
		    SMILSyncAttrEnd);
            }
	    else if(strcmp(pName, "endsync") == 0)
	    {
		rc = parseDuration((const char*)pBuf->GetBuffer(), pElement,
		    SMILSyncAttrEndsync);
	    }
	    else if(strcmp(pName, "title") == 0)
	    {
		pElement->m_title = (const char*)pBuf->GetBuffer();
	    }
	    else if(strcmp(pName, "repeat") == 0)
	    {
		pElement->m_ulRepeatValue = atol((const char*)pBuf->GetBuffer());
	    }


	    pBuf->Release();

	    if(HXR_OK != rc)
	    {
		goto exit;
	    }

	    rc = pNode->m_pValues->GetNextPropertyCString(pName, pBuf);
	}
    }

    rc = adjustDuration(pElement);

exit:
    if(HXR_OK != rc)
    {
	HX_DELETE(pElement);
    }

    return pElement;
}

HX_RESULT
CSmil1Parser::insertElementByTimestamp(CSmil1Element* pPacket)
{
    LISTPOSITION lPos = m_pPacketQueue->GetHeadPosition();
    LISTPOSITION lPrev = lPos;

    while(lPos)
    {
	CSmil1Element* pPkt = (CSmil1Element*)m_pPacketQueue->GetNext(lPos);
	if(pPkt->m_ulTimestamp > pPacket->m_ulTimestamp)
	{
	    m_pPacketQueue->InsertBefore(lPrev, pPacket);
	    return HXR_OK;
	}
	lPrev = lPos;
    }
    m_pPacketQueue->AddTail(pPacket);

    return HXR_OK;
}

HX_RESULT
CSmil1Parser::mapID(SMIL1Node* pNode, BOOL bOverWrite)
{
    HX_RESULT rc = HXR_OK;

    void* pTmp = 0;
    if(!bOverWrite && m_pIDMap->Lookup((const char*)pNode->m_id, (void*&)pTmp))
    {
	rc = HXR_FAIL;
	CSmil1SMILSyntaxErrorHandler errHandler(m_pContext);
	errHandler.ReportError(SMILErrorDuplicateID, pNode->m_id, 
				pNode->m_ulTagStartLine);
    }
    else
    {
	(*m_pIDMap)[(const char*)pNode->m_id] = pNode;
    }
    return rc;
}

//This function is needed to fix PR 13319.  If you have a repeat of greater
// than 1 in a <seq>, we need a way to map the IDs of the children created
// when the additional <seq>(s) are created
HX_RESULT
CSmil1Parser::mapChildrenIDs(SMIL1NodeList* pNodeList, BOOL bOverWrite)
{
    HX_RESULT rc = HXR_OK;

    if (!pNodeList)
    {
	return rc;
    }

    CHXSimpleList::Iterator i;
    for(i=pNodeList->Begin();rc == HXR_OK && i!=pNodeList->End();++i)
    {
	SMIL1Node* pNode = (SMIL1Node*)(*i);
	rc = mapID(pNode, bOverWrite);

	HX_ASSERT(rc == HXR_OK);

	if(pNode->m_pNodeList)
	{
	    rc = mapChildrenIDs(pNode->m_pNodeList, bOverWrite);
	}
    }

    return rc;
}

HX_RESULT
CSmil1Parser::markRepeatReplica(SMIL1NodeList* pNodeList, RepeatTag repeatTag)
{
    HX_RESULT rc = HXR_OK;

    if(!pNodeList)
    {
	return rc;
    }

    CHXSimpleList::Iterator i;
    for(i=pNodeList->Begin();rc == HXR_OK && i!=pNodeList->End();++i)
    {
	SMIL1Node* pNode = (SMIL1Node*)(*i);
	pNode->m_repeatTag = repeatTag;

	if(pNode->m_pNodeList)
	{
	    rc = markRepeatReplica(pNode->m_pNodeList, repeatTag);
	}
    }

    return rc;
}

HX_RESULT
CSmil1Parser::createElements()
{
    HX_RESULT rc = HXR_OK;
    
    SMIL1Node* pSMIL1Node = findFirstNode(SMILSmil);
    if(!pSMIL1Node)  // dangerwillrobinson!!!
    {
	rc = HXR_FAIL;
	CSmil1SMILSyntaxErrorHandler errHandler(m_pContext);
	errHandler.ReportError(SMILErrorNotSMIL, NULL, 0);
	return rc;
    }

    rc = addToNamespaceScope(pSMIL1Node);
    if (SUCCEEDED(rc))
    {
	// XXXJEFFA Hardcode "cv" namespace prefix for Redstone alpha 7
	rc = addGlobalNamespace((const char*) SYSTEM_COMPONENT_NAMESPACE, "cv");
    }
    
    if (FAILED(rc))
    {
	HX_ASSERT(FALSE);
	return rc;
    }
    
    SMIL1Node* pHeadNode = findFirstNode(SMILHead);
    if(pHeadNode)
    {
	rc = markTestAttributeNodes(pHeadNode->m_pNodeList);
	if (SUCCEEDED(rc))
	{
	    rc = addToNamespaceScope(pHeadNode);
	}
	if(SUCCEEDED(rc))
	{
	    rc = createHeadElements(pHeadNode->m_pNodeList);
	}
	if (SUCCEEDED(rc))
	{
	    rc = removeFromNamespaceScope(pHeadNode);
	}
    }

    if(rc == HXR_OK)
    {
	SMIL1Node* pBodyNode = findFirstNode(SMILBody);
	if(pBodyNode && 
	    pBodyNode->m_pNodeList)
	{
 	    rc = addToNamespaceScope(pBodyNode);
 	    if (SUCCEEDED(rc))
  	    {
		SMIL1Node* pTopNode = getTimelineDescendent(pBodyNode, NULL);
		if(pTopNode &&
		    pTopNode->m_tag != SMILSeq)
		{
		    createSeqWrapper(pBodyNode->m_pNodeList);
		}
		//[SMIL 1.0 compliance] fix for PR 23050:
		// if first descendent of body is a <switch> and its first child
		// is a <seq> that did not get chosen in the switch, then we
		// would be tricked into thinking we already had a valid outer
		// seq, but we wouldn't.  Let's put one there in this case,
		// noting that we don't yet know if that seq is valid or not.
		// However, it doesn't matter either way:
		if(pTopNode  &&  SMILSeq == pTopNode->m_tag  &&
			pTopNode->m_pParent  &&
			pTopNode->m_pParent->m_tag == SMILSwitch)
		{
		    createSeqWrapper(pBodyNode->m_pNodeList);
		}

		
		if(HXR_OK != markTestAttributeNodes(pBodyNode->m_pNodeList) ||
		   HXR_OK != expandRepeatElements(pBodyNode->m_pNodeList)	||
		   HXR_OK != createBodyElements(pBodyNode->m_pNodeList) ||
		   HXR_OK != assignGroupIndexes(pBodyNode->m_pNodeList) ||
		   HXR_OK != constructTimelineElements(pBodyNode->m_pNodeList) ||
		   HXR_OK != setInitialDelays(pBodyNode->m_pNodeList) ||
		   // HXR_OK != updateEventElements(pBodyNode->m_pNodeList) ||
	#ifdef _DEBUG
		   HXR_OK != printBodyElements(pBodyNode->m_pNodeList) ||
	#endif
		   HXR_OK != insertGroups())
		{
		    rc = HXR_FAIL;
		}
	    }

	    if (SUCCEEDED(rc))
  	    {
		rc = removeFromNamespaceScope(pBodyNode);
  	    }
	}
	if(rc == HXR_OK && !m_bContainsSource)
	{
	    rc = HXR_OK;
	    //[SMIL 1.0 compliance] fixes PR 27672:
	    // don't call SMILErrorNoBodyElements error as it is not one
	    // per the SMIL 1.0 spec.
	}
    }

    return rc;
}

HX_RESULT
CSmil1Parser::createSeqWrapper(SMIL1NodeList* pNodeList)
{
    // create a default <seq></seq> wrapper within the <body></body>
    HX_RESULT rc = HXR_OK;
    SMIL1Node* pSeqNode = new SMIL1Node;
    pSeqNode->m_name = "seq";
    pSeqNode->m_pParent = pNodeList->m_pParentNode;
    pSeqNode->m_id = assignID("seq");
    pSeqNode->m_tag = SMILSeq;
    pSeqNode->m_pNodeList = new SMIL1NodeList;
    // Must put the node in the id map - otherwise
    // we leak the node
    mapID(pSeqNode, TRUE);

    // move children from bodyNode list to seq list
    int count = pNodeList->GetCount();
    SMIL1Node* pEndBodyNode = NULL;
    while( count > 0)	// don't reparent </body> tag 
    {
	SMIL1Node* pChildNode = (SMIL1Node*)pNodeList->RemoveHead();
	//XXXJHUG don't reparent the </body> tag...
	if ( pChildNode->m_id != "CLOSE-body" )
	{
	    pChildNode->m_pParent = pSeqNode;
	    pSeqNode->m_pNodeList->AddTail(pChildNode);
	}
	else
	{
	    pEndBodyNode = pChildNode;
	}
	--count;
    }

    SMIL1Node* pEndSeqNode = new SMIL1Node;
    pEndSeqNode->m_name = "seq";
    pEndSeqNode->m_id = "CLOSE-seq";
    pEndSeqNode->m_pParent = pSeqNode;
    pEndSeqNode->m_tag = SMILEndSeq;
    pSeqNode->m_pNodeList->AddTail(pEndSeqNode);

    // now add it to the <body> parent...
    pNodeList->AddHead(pSeqNode);
    pNodeList->AddTail(pEndBodyNode);

    return rc;
}

#if 0
//XXXBAB - useful for debugging
void
CSmil1Parser::PrintNode(SMIL1Node* pNode, FILE* fp, int level)
{
    for(int tab=0;tab<level;++tab)
    {
	fprintf(fp, "\t");
    }
    fprintf(fp, "%s\n", (const char*)pNode->m_id);
    if(pNode->m_pNodeList)
    {
	CHXSimpleList::Iterator i = pNode->m_pNodeList->Begin();
	for(; i != pNode->m_pNodeList->End(); ++i)
	{
	    SMIL1Node* pChildNode = (SMIL1Node*)(*i);
	    PrintNode(pChildNode, fp, level+1);
	}
    }
}

void
CSmil1Parser::PrintNodes()
{
    FILE* fp = fopen("nodes.txt", "w");
    SMIL1Node* pNode = (SMIL1Node*)m_pNodeList->GetHead();
    PrintNode(pNode, fp, 0);
    fclose(fp);
}
#endif


HX_RESULT
CSmil1Parser::printBodyElements(SMIL1NodeList* pNodeList)
{
    // debugging code...
    HX_RESULT rc = HXR_OK;

//#define XXXEH_FOR_DEBUGGING_ONLY
#if defined(XXXEH_FOR_DEBUGGING_ONLY)
    static int level = 0;

    if(!pNodeList)
    {
	return rc;
    }

    CHXSimpleList::Iterator i;
    for(i=pNodeList->Begin();i!=pNodeList->End();++i)
    {
	if(HXR_OK != rc)
	{
	    return rc;
	}

	SMIL1Node* pNode = (SMIL1Node*)(*i);

	FILE* fp=fopen("c:\\temp\\smil.txt", "a+");
	if (!fp)
	{
	    break;
	}

	for(int i=0;i<level;++i)
	{
	    fprintf(fp, "\t");
	}
	UINT32 ulDelay = (UINT32)-1;
	if(pNode->m_pElement &&
	    pNode->m_pElement->m_pTimelineElement)
	{
	    ulDelay = pNode->m_pElement->m_pTimelineElement->getDelay();
	}
	fprintf(fp, "%s tag %d group %d repeatid %s delay %d deleted %d\n", (const char*)pNode->m_id,
	    pNode->m_tag, pNode->m_nGroup, (const char*)pNode->m_repeatid, ulDelay, pNode->m_bDelete);
	fclose(fp);

	if(pNode->m_pNodeList)
	{
	    ++level;
	    rc = printBodyElements(pNode->m_pNodeList);
	    --level;
	}
    }
#endif
    return rc;
}

HX_RESULT
CSmil1Parser::insertGroups()
{
    HX_RESULT rc = HXR_OK;

    if(!m_pAddGroupMap)
    {
	rc = HXR_UNEXPECTED;
    }
    else
    {
	UINT32 ulGroupNo = 0;
	CSmil1AddGroup* pAddGroup = 0;
	BOOL bGroupInserted = FALSE;
	while(m_pAddGroupMap->Lookup(ulGroupNo++, (void*&)pAddGroup))
	{
	    bGroupInserted = TRUE;
	    pAddGroup->m_ulDelay = 0;
	    pAddGroup->m_ulTimestamp = INITIAL_STREAM0_TIMESTAMP;
	    insertElementByTimestamp(pAddGroup);
	}
    }
    return rc;
}

BOOL
CSmil1Parser::inLanguagePreference(const char* pLang)
{
    // return FALSE if language preference is not in m_pLanguagePreferenceList

    BOOL rc = FALSE;

    // pLang can be comma separated lists,
    // with '*' accepting all languages

    // copy the string so strtok doesn't modify something bad...
    char* pLangCopy = new_string(pLang);
    char* pLangFrag = strtok(pLangCopy, ",");
    while(pLangFrag)
    {
	if(*pLangFrag == '*')	// wildcard
	{
	    rc = TRUE;
	    break;
	}

	// copy the primary tag part of the language (up to '-')
	char* pPrimaryTag = new char[strlen(pLang)+1];
	char* pTagPtr = pPrimaryTag;
	char* pFragPtr = pLangFrag;
	while(*pFragPtr && *pFragPtr != '-')
	{
	    *pTagPtr++ = *pFragPtr++;
	}
	*pTagPtr = '\0';

	CHXSimpleList::Iterator i = m_pLanguagePreferenceList->Begin();
	for(; i != m_pLanguagePreferenceList->End(); ++i)
	{
	    char* pPrefLang = (char*)(*i);
	    if(*pPrefLang == '*')	// wildcard
	    {
		rc = TRUE;
	    }
	    else if(strcmp(pPrefLang, pLangFrag) == 0)
	    {
		rc = TRUE;
	    }
	    else if(strncmp(pPrefLang, pPrimaryTag, strlen(pPrimaryTag)) == 0)
	    {
		rc = TRUE;
	    }
	    if(rc)
	    {
		break;
	    }
	}

	delete[] pPrimaryTag;

	if(rc)
	{
	    break;
	}
	pLangFrag = strtok(NULL, ",");
    }
    delete[] pLangCopy;

    return rc;
}

BOOL
CSmil1Parser::systemComponentFailed(IHXBuffer* pRequiredValue)
{
    BOOL bFailed = TRUE;
    IHXUpgradeCollection* pUpgradeCollection = new HXUpgradeCollection;
    pUpgradeCollection->AddRef();

    pUpgradeCollection->Add(eUT_Required, pRequiredValue, 0, 0);
    if (m_pISystemRequired == NULL)
    {
	m_pContext->QueryInterface(IID_IHXSystemRequired, 
	    (void**)&m_pISystemRequired);
    }

    // there may not be a system required interface
    if (m_pISystemRequired != NULL)
    {
	bFailed = FAILED(m_pISystemRequired->HasFeatures(pUpgradeCollection));
    }

    HX_RELEASE(pUpgradeCollection);

    return bFailed;
}

BOOL
CSmil1Parser::testAttributeFailed(SMIL1Node* pNode)
{
    HX_RESULT rc = HXR_OK;
    BOOL bFailed = FALSE;
    IHXBuffer* pBuf = 0;

    if(pNode->m_pValues)
    {
	rc = pNode->m_pValues->GetPropertyCString("system-required", pBuf);
	if(HXR_OK == rc)
	{
	    const char* pActualString = (const char*)pBuf->GetBuffer();
	    // Check with the core first
	    
	    if(m_pRequireTagsMap)
	    {
		void* pTag = 0;
		if(!m_pRequireTagsMap->Lookup(pActualString, 
		    (void*&) pTag))
		{
		    bFailed = TRUE;
		}
	    }
	    else	// no required list, can't parse it
	    {
		bFailed = TRUE;
	    }

	    pBuf->Release();
	    if(bFailed)
	    {
		goto exit;
	    }
	}

	rc = pNode->m_pValues->GetPropertyCString("system-bitrate", pBuf);
	if(HXR_OK == rc)
	{
	    UINT32 ulBandwidth = atol((const char*)pBuf->GetBuffer());

	    if(m_ulBandwidthPreference < ulBandwidth)
	    {
		bFailed = TRUE;
	    }
	    pBuf->Release();
	    if(bFailed)
	    {
		goto exit;
	    }
	}

	rc = pNode->m_pValues->GetPropertyCString("system-language", pBuf);
	if(HXR_OK == rc)
	{
	    if(m_pLanguagePreferenceList)
	    {
		const char* pLang = (const char*)pBuf->GetBuffer();
		if(!inLanguagePreference(pLang))
		{
		    bFailed = TRUE;
		}
	    }
	    else    // no preference, can't do it...
	    {
		bFailed = TRUE;
	    }
	    pBuf->Release();
	    if(bFailed)
	    {
		goto exit;
	    }
	}

	rc = pNode->m_pValues->GetPropertyCString("system-captions", pBuf);
	if(HXR_OK == rc)
	{
	    const char* pActualValue = (const char*)pBuf->GetBuffer();
	    if(strcmp(pActualValue, "on") == 0)
	    {
		if(!m_bCaptionsPreference)
		{
		    bFailed = TRUE;
		}
	    }
	    else
	    {
		if(m_bCaptionsPreference)
		{
		    bFailed = TRUE;
		}
	    }
	    pBuf->Release();
	    if(bFailed)
	    {
		goto exit;
	    }
	}

	if(HXR_OK == pNode->m_pValues->GetPropertyCString(
	    "system-overdub-or-caption", pBuf))
	{
	    if(m_pOverdubOrCaptionPreference)
	    {
		const char* pActualValue = (const char*)pBuf->GetBuffer();
		if(strcmp(pActualValue, "caption") == 0)
		{
		    if(strcmp(m_pOverdubOrCaptionPreference, "caption") != 0)
		    {
			bFailed = TRUE;
		    }
		}
		else if(strcmp(pActualValue, "overdub") == 0)
		{
		    if(strcmp(m_pOverdubOrCaptionPreference, "overdub") != 0)
		    {
			bFailed = TRUE;
		    }
		}
	    }
	    else
	    {
		bFailed = TRUE;
	    }
	    pBuf->Release();
	    if(bFailed)
	    {
		goto exit;
	    }
	}

	rc = pNode->m_pValues->GetPropertyCString(
	    "system-screen-size", pBuf);
	if(HXR_OK == rc)
	{
	    UINT32 ulScreenHeight = 0;
	    UINT32 ulScreenWidth = 0;

	    const char* pScreenSize = (const char*)pBuf->GetBuffer();
	    // format is screen-height "X" screen-width
	    char tmp[256]; /* Flawfinder: ignore */
	    SafeStrCpy(tmp, pScreenSize, 256);
	    char* pScreenHeight = strtok(tmp, "X");
	    if(pScreenHeight)
	    {
		ulScreenHeight = atol(pScreenHeight);
		char* pScreenWidth = strtok(NULL, "");
		if(pScreenWidth)
		{
		    ulScreenWidth = atol(pScreenWidth);
		}
	    }
	    if((m_ulScreenHeightPreference < ulScreenHeight) ||
	       (m_ulScreenWidthPreference < ulScreenWidth))
	    {
		bFailed = TRUE;
	    }
	    pBuf->Release();
	    if(bFailed)
	    {
		goto exit;
	    }
	}

	rc = pNode->m_pValues->GetPropertyCString(
	    "system-screen-depth", pBuf);
	if(HXR_OK == rc)
	{
	    UINT32 ulScreenDepth = atol((const char*)pBuf->GetBuffer());
	    if(m_ulScreenDepthPreference < ulScreenDepth)
	    {
		bFailed = TRUE;
	    }
	    pBuf->Release();
	    if(bFailed)
	    {
		goto exit;
	    }
	}

	if (m_pActiveNamespaceMap)
	{
	    // look through name spaces for systemComponent name space.  If it's there
	    // append the prefix and look for systemComponent test attribute
	    CHXMapStringToOb::Iterator ndxBuffer = m_pActiveNamespaceMap->Begin();
	    for (; ndxBuffer != m_pActiveNamespaceMap->End(); ++ndxBuffer)
	    {
		IHXBuffer* pBuffer = (IHXBuffer*)(*ndxBuffer);
		if (strcmp((const char*) SYSTEM_COMPONENT_NAMESPACE, (const char*)pBuffer->GetBuffer()) == 0)
		{
		    const char* pPrefix = (const char*)ndxBuffer.get_key();
		    // +1 for the : in the namespace and 1 for the \0
		    char* pAttrName = new char[strlen(pPrefix) + strlen((const char*) SYSTEM_COMPONENT) + 2];
		    
		    pAttrName[0] = '\0';
		    strcat(pAttrName, pPrefix); /* Flawfinder: ignore */
		    strcat(pAttrName, ":"); /* Flawfinder: ignore */
		    strcat(pAttrName, (const char*) SYSTEM_COMPONENT); /* Flawfinder: ignore */

		    rc = pNode->m_pValues->GetPropertyCString(
			pAttrName, pBuf);

		    HX_VECTOR_DELETE(pAttrName);
		    
		    if(HXR_OK == rc)
		    {
			bFailed = systemComponentFailed(pBuf);
			pBuf->Release();
			
			// If I get in here, I found the right prefix
			if (bFailed)
			{
			    goto exit;
			}
		    }
		}
	    }
	}
    }

exit:
    return bFailed;
}

HX_RESULT
CSmil1Parser::selectSwitchNodes(SMIL1Node* pSwitchNode)
{
    HX_RESULT rc = HXR_OK;

    SMIL1NodeList* pNodeList = pSwitchNode->m_pNodeList;
    if(!pNodeList)
    {
	return rc;
    }

    SMIL1Node* pSelectedNode = 0;
    CHXSimpleList* pRejectedNodeList = new CHXSimpleList;

    CHXSimpleList::Iterator i;
    for(i=pNodeList->Begin();i!=pNodeList->End();++i)
    {
	SMIL1Node* pNode = (SMIL1Node*)(*i);

	if(!pNode->m_bDelete)
	{
	    if(testAttributeFailed(pNode))
	    {
		pRejectedNodeList->AddTail(pNode);
	    }
	    else
	    {
		pSelectedNode = pNode; 
		//[SMIL 1.0 Compliance] Fixes PR 26732:
		//Now, update selected node's id to that of the switch so
		// that anchors pointing to the switch id will reference
		// the node of the switch that was selected.  Note that
		// we can't fix this the other way around, namely to rename
		// all anchors' id strings to that of the selected node
		// since not all of them have been parsed/set yet:
		HX_ASSERT(pSelectedNode->m_id.GetLength() > 0  && 
		    pSwitchNode->m_id.GetLength() > 0);
		if (pSelectedNode->m_id.GetLength() > 0 &&  
		    pSwitchNode->m_id.GetLength() > 0)
		{
		    pSelectedNode->m_id = pSwitchNode->m_id;
		    (*m_pIDMap)[(const char*)pSelectedNode->m_id] =
			    pSelectedNode;
		    pSwitchNode->m_id = assignID("switch");
		    (*m_pIDMap)[(const char*)pSwitchNode->m_id] =
			    pSwitchNode;
		}
		break;
	    }
	}
    }

    HX_DELETE(pRejectedNodeList);

    // delete non-selected nodes
    for(i=pNodeList->Begin();i!=pNodeList->End();++i)
    {
	SMIL1Node* pNode = (SMIL1Node*)(*i);
	if(pNode != pSelectedNode)
	{
	    pNode->m_bDelete = TRUE;
	}
    }

    return rc;
}

HX_RESULT
CSmil1Parser::markTestAttributeNodes(SMIL1NodeList* pNodeList)
{
    HX_RESULT rc = HXR_OK;

    if(!pNodeList)
    {
	return rc;
    }

    CHXSimpleList::Iterator i;
    for(i=pNodeList->Begin();i!=pNodeList->End() && SUCCEEDED(rc);++i)
    {
	SMIL1Node* pNode = (SMIL1Node*)(*i);

	rc = addToNamespaceScope(pNode);
	if (FAILED(rc))
	{
	    return rc;
	}

	if(pNode->m_tag == SMILSwitch)
	{
	    selectSwitchNodes(pNode);
	}
	else
	{
	    if(testAttributeFailed(pNode))
	    {
		pNode->m_bDelete = TRUE;
	    }
	}
	//Fix for PR 21303: we need to keep drilling down
	// through the tree even if we've just called
	// selectSwitchNodes() to delete all but one
	// child of the switch; the descendants of that
	// child may have attributes as well that need
	// to be checked:
	//(For efficiency, only do the following code if
	// the node is not already slated for deletion
	// because a deletable node's children will be
	// deleted anyway):
	if (!pNode->m_bDelete)
	{
	    rc = markTestAttributeNodes(pNode->m_pNodeList);
	}
	if (SUCCEEDED(rc))
	{
	    rc = removeFromNamespaceScope(pNode);
	}
    }
    return rc;
}

BOOL
CSmil1Parser::firstDependentChild(SMIL1Node* pNode)
{
    if(getFirstNodeChild(pNode->m_pDependency) == pNode)
    {
	return TRUE;
    }
    return FALSE;
}

BOOL
CSmil1Parser::hasParParent(SMIL1Node* pNode)
{
    // walk up parent chain until we find a PAR
    BOOL rc = FALSE;
    while(pNode)
    {
	pNode = pNode->m_pParent;
	if(pNode && pNode->m_tag == SMILPar)
	{
	    rc = TRUE;
	    break;
	}
    }
    return rc;
}

SMIL1NodeTag
CSmil1Parser::getSyncTag(SMIL1Node* pNode)
{
    // walk up parent chain until we either find
    // a PAR or a SEQ
    SMIL1NodeTag tag = SMILSeq;	// assume a SEQ
    while(pNode)
    {
	pNode = pNode->m_pParent;
	if(pNode && pNode->m_tag == SMILPar)
	{
	    tag = SMILPar;
	    break;
	}
	else if(pNode && pNode->m_tag == SMILSeq)
	{
	    tag = SMILSeq;
	    break;
	}
    }
    return tag;
}

SMIL1Node*
CSmil1Parser::getSyncParent(SMIL1Node* pNode)
{
    SMIL1Node* pReturnNode = NULL;
    while(pNode)
    {
	pNode = pNode->m_pParent;
	if(pNode && 
	    (pNode->m_tag == SMILPar ||
	     pNode->m_tag == SMILSeq))
	{
	    pReturnNode = pNode;
	    break;
	}
    }
    return pReturnNode;
}

BOOL
CSmil1Parser::inSeq(SMIL1Node* pNode)
{
    SMIL1NodeTag tag = getSyncTag(pNode);
    return tag == SMILSeq;
}

HX_RESULT
CSmil1Parser::createHeadElements(SMIL1NodeList* pNodeList)
{
    HX_RESULT rc = HXR_OK;

    if(!pNodeList)
    {
	return rc;
    }

    CHXSimpleList::Iterator i;
    for(i=pNodeList->Begin();i!=pNodeList->End();++i)
    {
	if(HXR_OK != rc)
	{
	    return rc;
	}

	SMIL1Node* pNode = (SMIL1Node*)(*i);

	if(pNode->m_bDelete)	// skip this puppy
	{
	    continue;
	}

	rc = addToNamespaceScope(pNode);
	if (FAILED(rc))
	{
	    return rc;
	}

	switch(pNode->m_tag)
	{
	    case SMILRegion:
	    {
		//[SMIL 1.0 compliance] Fixes PR 25798: a region element may
		// only be contained inside a layout element:
		if (m_bSMIL10FullCompliance  &&  pNode->m_pParent  &&
			pNode->m_pParent->m_name.GetLength()  &&
			strcmp((const char*)pNode->m_pParent->m_name,
			"layout"))
		{
		    CSmil1SMILSyntaxErrorHandler errHandler(m_pContext);
		    errHandler.ReportError(SMILErrorUnexpectedTag,
			    (const char*)pNode->m_name,
			    pNode->m_ulTagStartLine);
		    return HXR_FAIL;
		}
	    }
	    break;

	    case SMILMeta:
	    {
		CSmil1Meta* pMeta = makeMeta(pNode);
		if(pMeta)
		{
		    pNode->m_pElement = pMeta;
		    pMeta->m_ulDelay = 0;
		    pMeta->m_ulTimestamp = INITIAL_STREAM0_TIMESTAMP;
		    insertElementByTimestamp(pMeta);
		}
		else
		{
		    return HXR_FAIL;
		}
	    }
	    break;

	    case SMILBasicLayout:
	    {
		const char* pLayoutName = (const char*)pNode->m_id;
		BOOL bHasRegions = FALSE;
		SMIL1Node* pLayoutNode = pNode->getFirstChild();
		while(pLayoutNode && !pLayoutNode->m_bDelete)
		{
		    switch(pLayoutNode->m_tag)
		    {
			case SMILRootLayout:
			{
			    CSmil1RootLayout* pRootLayout = 
				makeRootLayout(pLayoutNode);
			    if(pRootLayout)
			    {
				pLayoutNode->m_pElement = pRootLayout;
				pRootLayout->m_ulDelay = 0;
				pRootLayout->m_ulTimestamp =
				    INITIAL_STREAM0_TIMESTAMP;
				insertElementByTimestamp(pRootLayout);
			    }
			    else
			    {
				return HXR_FAIL;
			    }
			}
			break;

			case SMILRegion:
			{
			    CSmil1Region* pRegion = 
				makeRegion(pLayoutNode);
			    if(pRegion)
			    {
				pLayoutNode->m_pElement = pRegion;
				bHasRegions = TRUE;
				pRegion->m_ulDelay = 0;
				pRegion->m_ulTimestamp = 
				    INITIAL_STREAM0_TIMESTAMP;
				insertElementByTimestamp(pRegion);
			    }
			    else
			    {
				return HXR_FAIL;
			    }
			}
			break;

			case SMILMeta:
			{
			    CSmil1Meta* pMeta = makeMeta(pLayoutNode);
			    if(pMeta)
			    {
				pNode->m_pElement = pMeta;
				pMeta->m_ulDelay = 0;
				pMeta->m_ulTimestamp = 
				    INITIAL_STREAM0_TIMESTAMP;
				insertElementByTimestamp(pMeta);
			    }
			    else
			    {
				return HXR_FAIL;
			    }
			}
			break;

			default:
			break;
		    }
		    pLayoutNode = pNode->getNextChild();
		}
		if(bHasRegions)
		{
		    // send end-layout packet at TS 0
		    m_pEndLayout = new CSmil1EndLayout;
		    m_pEndLayout->m_ulDelay = 0;
		    m_pEndLayout->m_ulTimestamp = INITIAL_STREAM0_TIMESTAMP;
		    insertElementByTimestamp(m_pEndLayout);
		}
	    }
	    break;
	    	
	    case SMILRNRendererList:
	    {
		SMIL1Node* pRendererNode = pNode->getFirstChild();
		while(pRendererNode && 
		    pRendererNode->m_tag == SMILRendererPreFetch)
		{
		    CSmil1RendererPreFetch* pRenderer = 
			makeRendererPreFetch(pRendererNode);
		    pRendererNode->m_pElement = pRenderer;
		    pRenderer->m_ulDelay = 0;
		    pRenderer->m_ulTimestamp = INITIAL_STREAM0_TIMESTAMP;
		    insertElementByTimestamp(pRenderer);

		    pRendererNode = pNode->getNextChild();
		}
	    }
	    break;

	    default:
	    break;
	}
	rc = createHeadElements(pNode->m_pNodeList);

	if (SUCCEEDED(rc))
	{
	    rc = removeFromNamespaceScope(pNode);
	}
    }
    return rc;
}

HX_RESULT
CSmil1Parser::setInitialDelays(SMIL1NodeList* pNodeList)
{
    HX_RESULT rc = HXR_OK;

    UINT16 nGroup = (UINT16)-1;

    if(!pNodeList)
    {
	return rc;
    }

    CHXSimpleList::Iterator i = pNodeList->Begin();
    for(;i!=pNodeList->End();++i)
    {
	if(HXR_OK != rc)
	{
	    return rc;
	}

	SMIL1Node* pNode = (SMIL1Node*)(*i);

	if(pNode->m_bDelete)	// skip this puppy
	{
	    continue;
	}
	else if(pNode->m_tag == SMILAAnchor ||
	        pNode->m_tag == SMILSwitch)
	{
	    pNode = getTimelineDescendent(pNode);
	    if(!pNode)
	    {
		continue;
	    }
	}

	setInitialDelay(pNode);
	switch(pNode->m_tag)
	{
	    case SMILSeq:
	    {
		setInitialDelayOnSeq(pNode);
	    }
	    break;
	    case SMILPar:
	    {
		SMIL1Node* pThisNode =
		    getTimelineDescendent(pNode, NULL);
		while(pThisNode)
		{
		    setInitialDelay(pThisNode);
		    pThisNode =
			getTimelineDescendent(pNode, pThisNode);
		}
	    }
	    break;
	}
    }
    return rc;
}

void
CSmil1Parser::setInitialDelay(SMIL1Node* pNode)
{
    if(pNode->m_pElement &&
	pNode->m_pElement->m_pTimelineElement &&
	!pNode->m_pElement->m_pTimelineElement->initialDelaySet())
    {
	pNode->m_pElement->m_pTimelineElement->setDelay(0);
    }

    if(pNode->m_pNodeList)
    {
	if(pNode->m_tag == SMILSeq ||
	    pNode->m_tag == SMILPar)
	{
	    if(pNode->m_pElement &&
		pNode->m_pElement->m_pTimelineElement &&
		pNode->m_pElement->m_ulDuration != (UINT32)-1)
	    {
		pNode->m_pElement->m_pTimelineElement->setDuration(
		    pNode->m_pElement->m_ulDuration);
	    }
	}

	if(pNode->m_tag == SMILSeq)
	{
	    setInitialDelayOnSeq(pNode);
	}
	//If the element doesn't exist or it does but the timelineElement
	// doesn't exist, or it does but there is no delay event, or there is
	// one but the delay is already set, then it's ok to set call
	// setInitialDelay():
	else if ((!pNode->m_pElement  ||
		!pNode->m_pElement->m_pTimelineElement)  ||
		//[SMIL 1.0 Compliance] Helps fix PR 14420:
		//Don't call this if pNode has an unresolved delay event:
		(!pNode->m_pElement->m_pTimelineElement->delayEvent()  ||
		pNode->m_pElement->m_pTimelineElement->initialDelaySet() ) )
	{
	    SMIL1Node* pThisNode = 
		getTimelineDescendent(pNode, NULL);
	    while(pThisNode)
	    {
		setInitialDelay(pThisNode);
		pThisNode = getTimelineDescendent(pNode, pThisNode);
	    }
	}
    }
}

void 
CSmil1Parser::setInitialDelayOnSeq(SMIL1Node* pNode)
{
    UINT16 nGroup = (UINT16)-1;
    
    SMIL1Node* pThisNode = getTimelineDescendent(pNode, NULL);
    while(pThisNode)
    {
	if(pThisNode->m_nGroup != nGroup)
	{
	    nGroup = pThisNode->m_nGroup;
	    setInitialDelay(pThisNode);
	}
	pThisNode = getTimelineDescendent(pNode, pThisNode);
    }
}

HX_RESULT
CSmil1Parser::assignGroupIndexes(SMIL1NodeList* pNodeList)
{
    HX_RESULT rc = HXR_OK;

    if(!pNodeList)
    {
	return rc;
    }

    UINT16 nGroup = 0;
    CHXSimpleList::Iterator i = pNodeList->Begin();
    for(; i != pNodeList->End(); ++i)
    {
	if(HXR_OK != rc)
	{
	    return rc;
	}

	SMIL1Node* pNode = (SMIL1Node*)(*i);

	if(pNode->m_bDelete)	// skip this puppy
	{
	    continue;
	}
	else if(pNode->m_tag == SMILAAnchor ||
	        pNode->m_tag == SMILSwitch)
	{
	    pNode = getTimelineDescendent(pNode);
	    if(!pNode)
	    {
		continue;
	    }
	}

	switch(pNode->m_tag)
	{
	    case SMILSeq:
	    {
		SMIL1Node* pThisNode =
		    getTimelineDescendent(pNode, NULL);
		while(pThisNode)
		{
		    rc = assignGroupIndexOnSeq(pThisNode, nGroup);
		    if(HXR_OK == rc)
		    {
			pThisNode =
			    getTimelineDescendent(pNode, pThisNode);
			nGroup++;
		    }
		    else
		    {
			break;
		    }
		}
	    }
	    break;

	    case SMILPar:
	    {
		// top-level par, each child has group 0
		SMIL1Node* pThisNode =
		    getTimelineDescendent(pNode, NULL);
		while(pThisNode)
		{
		    rc = assignGroupIndexOnPar(pThisNode, nGroup);
		    if(HXR_OK == rc)
		    {
			pThisNode =
			    getTimelineDescendent(pNode, pThisNode);
		    }
		    else
		    {
			break;
		    }
		}
	    }
	    break;

	    default:
	    break;
	}
    }

    return rc;
}

HX_RESULT
CSmil1Parser::assignGroupIndexOnPar(SMIL1Node* pNode,
				   UINT16 nGroup)
{
    HX_RESULT rc = HXR_OK;

    if(pNode->m_bDelete)
    {
	return rc;
    }

    pNode->m_nGroup = nGroup;
    pNode->m_repeatid.AppendULONG(nGroup);

    if (isMediaObject(pNode))
    {
	addGroup(pNode);
    }
    else if (SMILSeq == pNode->m_tag ||
	     SMILPar == pNode->m_tag)
    {
	// top-level par, each child has group 0
	SMIL1Node* pThisNode =
	    getTimelineDescendent(pNode, NULL);
	while(pThisNode)
	{
	    rc = assignGroupIndexOnPar(pThisNode, nGroup);
	    if(HXR_OK == rc)
	    {
		pThisNode =
		    getTimelineDescendent(pNode, pThisNode);
	    }
	    else
	    {
		break;
	    }
	}
    }

    return rc;
}

HX_RESULT
CSmil1Parser::assignGroupIndexOnSeq(SMIL1Node* pNode,
				   UINT16& nGroup)
{
    HX_RESULT rc = HXR_OK;

    if(pNode->m_bDelete)
    {
	return rc;
    }

    pNode->m_nGroup = nGroup;
    pNode->m_repeatid.AppendULONG(nGroup);

    if (isMediaObject(pNode))
    {
	addGroup(pNode);
    }
    else if (SMILSeq == pNode->m_tag)
    {
	SMIL1Node* pThisNode =
	    getTimelineDescendent(pNode, NULL);
	while(pThisNode)
	{
	    rc = assignGroupIndexOnSeq(pThisNode, nGroup);
	    if(HXR_OK == rc)
	    {
		pThisNode = getTimelineDescendent(pNode, pThisNode);
		if (pThisNode)
		{
		    if (isMediaObject(pThisNode)    ||
			SMILSeq == pThisNode->m_tag ||
			SMILPar == pThisNode->m_tag)
		    {
			nGroup++;
		    }
		}
	    }
	    else
	    {
		break;
	    }
	}
    }
    else if (SMILPar == pNode->m_tag)
    {
	SMIL1Node* pThisNode =
	    getTimelineDescendent(pNode, NULL);
	while(pThisNode)
	{
	    rc = assignGroupIndexOnPar(pThisNode, nGroup);
	    if(HXR_OK == rc)
	    {
		pThisNode =
		    getTimelineDescendent(pNode, pThisNode);
	    }
	    else
	    {
		break;
	    }
	}
    }

    return rc;
}

BOOL 
CSmil1Parser::isMediaObject(SMIL1Node* pNode)
{
    BOOL bResult = FALSE;

    if (NULL == pNode || pNode->m_bDelete)
    {
	goto cleanup;
    }

    switch(pNode->m_tag)
    {
	case SMILRef:
	case SMILText:
	case SMILImg:
	case SMILAudio:
	case SMILVideo:
	case SMILAnimation:
	case SMILTextstream:
	{
	    bResult = TRUE;
	}
	break;
	
	default:
	break;
    }

cleanup:

    return bResult;
}

HX_RESULT
CSmil1Parser::addGroup(SMIL1Node* pNode)
{
    HX_RESULT rc = HXR_OK;

    // set group info information
    CSmil1AddGroup* pAddGroup = 0;
    if(m_pAddGroupMap->Lookup(pNode->m_nGroup, (void*&)pAddGroup))
    {
	// we don't count self-replicated elements as they
	// share the same track ID
	if (pNode->m_repeatTag == RepeatUnknown)
	{
	    pAddGroup->m_nTotalTracks++;
	    if(pNode->m_pElement &&
	       pNode->m_pElement->m_ulDelay == 0)
	    {
		pAddGroup->m_nInitTracks++;
	    }
	}
    }
    else
    {
	CSmil1AddGroup* pAddGroup = new CSmil1AddGroup;
	pAddGroup->m_nGroup = pNode->m_nGroup;
	
	// reference PAR properties in group
	if(pNode->m_pDependency &&
	   pNode->m_pDependency->m_tag == SMILPar &&
	   hasParParent(pNode))
	{
	    IHXValues* pValues = pNode->m_pDependency->m_pValues;
	    if(pValues)
	    {
		pAddGroup->m_pValues = pValues;
		pAddGroup->m_pValues->AddRef();
	    }
	    pAddGroup->m_ulDuration = 
		pNode->m_pDependency->m_pElement->m_ulDuration;
	}

	// XXXJHUG  -- If the group is from a Seq, we still want
	// to pass along the Title, author, copyright & abstract from
	// the dependant parrent sequence group.
	if (pNode->m_pDependency &&
	    pNode->m_pDependency->m_tag == SMILSeq)
	{
	    IHXValues* pValues = pNode->m_pDependency->m_pValues;
	    if(pValues)
	    {
		// XXXJH should use CCF
		pAddGroup->m_pValues = new CHXHeader();
		IHXBuffer* pBuf = NULL;
		const char* name = NULL;
		pAddGroup->m_pValues->AddRef();
		if (SUCCEEDED(pValues->GetFirstPropertyCString(name, pBuf)))
		{
		    do
		    {
			if (strcmp("title",name)==0 ||
			    strcmp("author",name)==0 ||
			    strcmp("abstract",name)==0 ||
			    strcmp("copyright",name)==0)
			{
			    pAddGroup->m_pValues->SetPropertyCString(name, pBuf);
			}
			HX_RELEASE(pBuf);
		    }
		    while(SUCCEEDED(pValues->GetNextPropertyCString(name, pBuf)));
		}
	    }
	    // the duration for a sequence is dependant on the elements
	    // within the sequence.
	}

	(*m_pAddGroupMap)[pNode->m_nGroup] = pAddGroup;
	pAddGroup->m_nTotalTracks = 1;
	if(pNode->m_pElement &&
	    pNode->m_pElement->m_ulDelay == 0)
	{
	    pAddGroup->m_nInitTracks = 1;
	}
    }
    return rc;
}

HX_RESULT
CSmil1Parser::createBodyElements(SMIL1NodeList* pNodeList)
{
    HX_RESULT rc = HXR_OK;

    if(!pNodeList)
    {
	return rc;
    }

    if(!m_pNodeDependencies)
    {
	m_pNodeDependencies = new CHXStack;
    }

    if(!m_pAnchorStack)
    {
	m_pAnchorStack = new CHXStack;
    }

    if(!m_pTrackHintList)
    {
	m_pTrackHintList = new CHXSimpleList;
    }

    UINT32 ulTrackHint = 1;

    CHXSimpleList::Iterator i;
    for(i=pNodeList->Begin();i!=pNodeList->End();++i)
    {
	if(HXR_OK != rc)
	{
	    return rc;
	}

	SMIL1Node* pNode = (SMIL1Node*)(*i);

	if(pNode->m_bDelete)	// skip this puppy
	{
	    continue;
	}

	rc = addToNamespaceScope(pNode);
	if (FAILED(rc))
	{
	    return rc;
	}

	switch(pNode->m_tag)
	{
	    case SMILSeq:
	    {
		CSmil1SeqElement* pElement = makeSeqElement(pNode);
		if(!pElement)
		{
		    return HXR_FAIL;
		}
		pNode->m_pElement = pElement;

		CSmil1TimelineSeq* pTimelineSeq =
		    new CSmil1TimelineSeq(pElement, this);

		pElement->m_pTimelineElement = pTimelineSeq;

		if(!m_pCurrentDependentNode)	// at the top
		{
		    //pNode->m_nGroup = (UINT16)-1;   // top level
		}
		else
		{
		    pNode->m_pDependency = m_pCurrentDependentNode;

		    if(!hasParParent(pNode))
		    {
			//pNode->m_nGroup = 
			//    m_pCurrentDependentNode->m_nGroup + 1;
		    }
		    else
		    {
			//pNode->m_nGroup = 
			//    m_pCurrentDependentNode->m_nGroup;
		    }
		}
		m_pCurrentDependentNode = pNode;
		m_pNodeDependencies->Push(m_pCurrentDependentNode);

		ulTrackHint = 1;
		m_pTrackHintList->AddTail((void*)ulTrackHint);
	    }
	    break;

	    case SMILPar:
	    {
		CSmil1ParElement* pElement = makeParElement(pNode);
		if(!pElement)
		{
		    return HXR_FAIL;
		}
		pNode->m_pElement = pElement;
		CSmil1TimelinePar* pTimelinePar = 
		    new CSmil1TimelinePar(pElement, this);
		pElement->m_pTimelineElement = pTimelinePar;

		if(!m_pCurrentDependentNode)	// at the top
		{
		    // pNode->m_nGroup = 0;
		}
		else
		{
		    pNode->m_pDependency = m_pCurrentDependentNode;
		    if(!hasParParent(pNode))
		    {
			//pNode->m_nGroup = 
			//    m_pCurrentDependentNode->m_nGroup + 1;
		    }
		    else
		    {
			//pNode->m_nGroup = m_pCurrentDependentNode->m_nGroup;
		    }
		}
		if(firstDependentChild(pNode) || m_pCurrentDependentNode == 0)
		{
		    ulTrackHint = 1;
		    m_pTrackHintList->AddTail((void*)ulTrackHint);
		}
		else
		{
		    if(m_pTrackHintList->GetCount() > 0)
		    {
			ulTrackHint = (UINT32)m_pTrackHintList->RemoveTail();
		    }
		    m_pTrackHintList->AddTail((void*)++ulTrackHint);
		}

		m_pCurrentDependentNode = pNode;
		m_pNodeDependencies->Push(m_pCurrentDependentNode);

	    }
	    break;

	    case SMILEndSeq:
	    {
		m_pCurrentDependentNode = 
		    (SMIL1Node*)m_pNodeDependencies->Pop();

		if(m_pTrackHintList->GetCount() > 0)
		{
		    m_pTrackHintList->RemoveTail();
		}
	    }
	    break;

	    case SMILEndPar:
	    {
		m_pCurrentDependentNode = 
		    (SMIL1Node*)m_pNodeDependencies->Pop();

		if(m_pTrackHintList->GetCount() > 0)
		{
		    m_pTrackHintList->RemoveTail();
		}
	    }
	    break;

	    case SMILAAnchor:
	    {
		CSmil1AAnchorElement* pElement = makeAAnchorElement(pNode);
		if(pElement)
		{
		    pNode->m_pElement = pElement;
		    m_pAnchorStack->Push(m_pCurrentAnchor);
		    m_pCurrentAnchor = pElement;
		}
	    }
	    break;

	    case SMILEndAAnchor:
	    {
		m_pCurrentAnchor = (CSmil1AAnchorElement*)m_pAnchorStack->Pop();
	    }
	    break;

	    case SMILAnchor:
	    {
		CSmil1AnchorElement* pElement = makeAnchorElement(pNode);
		if(pElement)
		{
		    pNode->m_pElement = pElement;

		    //[SMIL 1.0 compliance] Helps fix PR 26471:
		    // In order to get a notification that we have a resolved
		    // event-based begin time, we need to be a
		    // timeline element:
		    CSmil1TimelineAnchor* pTimelineAnchor = 
			new CSmil1TimelineAnchor(pElement, this);
		    pElement->m_pTimelineElement = pTimelineAnchor;

		    // add link to parent
		    SMIL1Node* pParent = pNode->m_pParent;
		    if(pParent->m_pElement)
		    {
			pParent->m_pElement->m_pHyperlinks->AddTail(pElement);
		    }

		    if(!m_pCurrentDependentNode)
		    { 
			//anchor should NEVER be top element:
			HX_ASSERT(m_pCurrentDependentNode);
		    } 
		    else 
		    { 
			pNode->m_pDependency = m_pCurrentDependentNode;
		    } 
		    if(firstDependentChild(pNode) || m_pCurrentDependentNode == 0)
		    { 
			ulTrackHint = 1;
			m_pTrackHintList->AddTail((void*)ulTrackHint);
		    } 
		    else 
		    { 
			if(m_pTrackHintList->GetCount() > 0)
			{
			    ulTrackHint = (UINT32)m_pTrackHintList->RemoveTail();
			}
			m_pTrackHintList->AddTail((void*)++ulTrackHint);
		    } 
		    m_pCurrentDependentNode = pNode;
		    m_pNodeDependencies->Push(m_pCurrentDependentNode);
		    //end of part of fix for PR 26471.
		}
	    }
	    break;

	    case SMILRef:
	    case SMILText:
	    case SMILImg:
	    case SMILAudio:
	    case SMILVideo:
	    case SMILAnimation:
	    case SMILTextstream:
	    {
		CSmil1Source* pSource = makeSource(pNode);
		if(!pSource)
		{
		    return HXR_FAIL;
		}
		m_bContainsSource = TRUE;
		pNode->m_pElement = pSource;

		CSmil1TimelineElement* pTimelineElement = 
		    new CSmil1TimelineElement(pSource, this);

		pNode->m_pElement->m_pTimelineElement = pTimelineElement;

		// attach any links
		if(m_pCurrentAnchor)
		{
		    pSource->m_pHyperlinks->AddTail(m_pCurrentAnchor);
		}

		if(!m_pCurrentDependentNode)
		{
		    // make it behave like it's in a SEQ
		    pNode->m_pDependency = 0;
		    //pNode->m_nGroup = 0;
		    m_pCurrentDependentNode = pNode;
		    ulTrackHint = 1;
		    m_pTrackHintList->AddTail((void*)ulTrackHint);
		}
		else
		{
		    pNode->m_pDependency = m_pCurrentDependentNode;

		    if(inSeq(pNode))
		    {
			if(firstDependentChild(pNode))
			{
			    ulTrackHint = 1;
			    m_pTrackHintList->AddTail((void*)ulTrackHint);
			}
			else
			{
			    if(m_pTrackHintList->GetCount() > 0)
			    {
				ulTrackHint = 
				    (UINT32)m_pTrackHintList->RemoveTail();
			    }
			    m_pTrackHintList->AddTail((void*)++ulTrackHint);
			}
		    }
		    else
		    {
			if(firstDependentChild(pNode))
			{
			    ulTrackHint = 1;
			    m_pTrackHintList->AddTail((void*)ulTrackHint);
			}
		    }

		    if(inSeq(pNode) && !hasParParent(pNode))
		    {
			if(firstDependentChild(pNode))
			{
			    //pNode->m_nGroup = m_pCurrentDependentNode->m_nGroup;
			}
			else
			{
			    //pNode->m_nGroup = 
				//m_pCurrentDependentNode->m_nGroup + 1;
			}
			m_pCurrentDependentNode = pNode;
		    }
		    else if(inSeq(pNode))
		    {
			//pNode->m_nGroup = m_pCurrentDependentNode->m_nGroup;
			m_pCurrentDependentNode = pNode;
		    }
		    else	// in PAR
		    {
			// pNode->m_nGroup = m_pCurrentDependentNode->m_nGroup;
		    }
		}

		// set track hint info
		BOOL bFirstHint = TRUE;
		CHXSimpleList::Iterator hintIter = m_pTrackHintList->Begin();
		for(; hintIter != m_pTrackHintList->End(); ++hintIter)
		{
		    char tmpBuf[20]; /* Flawfinder: ignore */

		    UINT32 ulTrackHint = (UINT32)(*hintIter);
		    if(bFirstHint)
		    {
			sprintf(tmpBuf, "%d", ulTrackHint); /* Flawfinder: ignore */
			bFirstHint = FALSE;
		    }
		    else
		    {
			sprintf(tmpBuf, ".%d", ulTrackHint); /* Flawfinder: ignore */
		    }
		    pNode->m_trackHint += tmpBuf;
		}
	    }
	    break;

	    default:
	    break;
	}
	rc = createBodyElements(pNode->m_pNodeList);
	
	if (SUCCEEDED(rc))
	{
	    rc = removeFromNamespaceScope(pNode);
	}
    }
    return rc;
}

HX_RESULT
CSmil1Parser::expandRepeatElements(SMIL1NodeList* pNodeList)
{
    // walk through tree and expand any "repeat" attributes
    
    HX_RESULT rc = HXR_OK;
    if(!pNodeList)
    {
	return rc;
    }

    LISTPOSITION lPos = pNodeList->GetHeadPosition();
    while (lPos && HXR_OK == rc)
    {
	SMIL1Node* pNode = (SMIL1Node*)pNodeList->GetAt(lPos);

	if(pNode->m_bDelete)	// skip this puppy
	{
	    pNodeList->GetNext(lPos);
	    continue;
	}

	if(pNode->m_pValues)
	{
	    const char* pName = 0;
	    IHXBuffer* pBuf = 0;

	    if (!pNode->m_bRepeatHandled &&
		HXR_OK == pNode->m_pValues->GetPropertyCString("repeat", pBuf))
	    {
		BOOL	    bRepeatIndefinite = FALSE;
		const char* pRepeatCount = (const char*)pBuf->GetBuffer();
		if(pRepeatCount)
		{
		    INT32 lRepeatCount = 0;

		    if(strcmp(pRepeatCount, "indefinite") == 0)
		    {
			bRepeatIndefinite = TRUE;
			lRepeatCount = 2;
		    }
		    else
		    {
			lRepeatCount = atol(pRepeatCount);
		    }

		    if(lRepeatCount == 0)
		    {
			pNode->m_bDelete = TRUE;
		    }
		    else if(lRepeatCount > 1)
		    {
			// build a <seq>/</seq> around the repeated element...
			SMIL1Node* pSeqNode = NULL;
			SMIL1Node* pSeqEndNode = NULL;
			
			createParent(pNode, SMILSeq, pSeqNode, pSeqEndNode);

			SMIL1Node* pNodeCopy = NULL;
			BOOL	  bOverWrite = TRUE;
			for(INT32 lCount = 0; lCount < lRepeatCount;
			    ++lCount)
			{
			    // we want to keep the original ids for the
			    // 1st repeat so that we don't break any links
			    if (0 == lCount)
			    {
				pNodeCopy = new SMIL1Node(*pNode, TRUE, this);
				bOverWrite = TRUE;
			    }
			    else
			    {
				pNodeCopy = new SMIL1Node(*pNode, FALSE, this);
				bOverWrite = FALSE;
			    }
				
			    pNodeCopy->m_pParent = pSeqNode;
			    pNodeCopy->m_pNodeList->m_pParentNode = pNodeCopy;
			    pNodeCopy->m_bRepeatHandled = TRUE;

			    // after the 1st repeat, mark the rest of repeats
			    // as replica(used in counting tracks etc.)
			    if (lCount > 0)
			    {
				if (bRepeatIndefinite)
				{
				    if (pNodeCopy->m_tag == SMILSeq ||
					pNodeCopy->m_tag == SMILPar)
				    {
					// set indefinite repeat tag on seq
					// will be used in constructing timeline elements
					pSeqNode->m_repeatTag = RepeatIndefiniteOnMe;
					pNodeCopy->m_repeatTag = RepeatIndefiniteOnGroup;
					markRepeatReplica(pNodeCopy->m_pNodeList, RepeatIndefiniteOnGroup);
				    }
				    else
				    {
					pNodeCopy->m_repeatTag = RepeatIndefiniteOnMe;
				    }
				}
				else
				{
				    pNodeCopy->m_repeatTag = RepeatReplica;
				    
				    if (pNodeCopy->m_tag == SMILSeq ||
					pNodeCopy->m_tag == SMILPar)
				    {
					markRepeatReplica(pNodeCopy->m_pNodeList, RepeatReplica);
				    }
				}
			    }

			    mapID(pNodeCopy, bOverWrite);
			    //Fix for PR 13119: internal elements (source
			    // elements, especially) of a repeated <seq> or
			    // <par> tag were not getting mapped and thus
			    // never played, so we need to map the IDs of
			    // all in the m_pNodeList of the pNodeCopy:
			    mapChildrenIDs(pNodeCopy->m_pNodeList, bOverWrite);

			    pSeqNode->m_pNodeList->AddTail(pNodeCopy);
			}
			pSeqNode->m_pNodeList->AddTail(pSeqEndNode);

			// special case to ensure all repeated elements
			// are within the same group(share the same timeline)
			if (!hasParParent(pSeqNode))
			{
			    SMIL1Node* pParNode = NULL;
			    SMIL1Node* pParEndNode = NULL;

			    createParent(pSeqNode, SMILPar, pParNode, pParEndNode);

			    pParNode->m_repeatTag = pSeqNode->m_repeatTag;
			    pParNode->m_pNodeList->AddTail(pSeqNode);			    
			    pParNode->m_pNodeList->AddTail(pParEndNode);

			    // now add the list to the parent...
			    pNodeList->InsertBefore(lPos, pParNode);
			    pNode->m_bDelete = TRUE;	// delete original node
			    pNode = pParNode;
			}
			else
			{
			    // now add the list to the parent...
			    pNodeList->InsertBefore(lPos, pSeqNode);
			    pNode->m_bDelete = TRUE;	// delete original node
			    pNode = pSeqNode;
			}
		    }
		}
	    }
	}
	// breadth first
	rc = expandRepeatElements(pNode->m_pNodeList);
	pNodeList->GetNext(lPos);
    }
    return rc;
}

HX_RESULT
CSmil1Parser::createParent (SMIL1Node*	pChildNode, 
			   SMIL1NodeTag	tag,
			   SMIL1Node*&	pParent,
			   SMIL1Node*&	pParentEnd)
{
    HX_RESULT	hr = HXR_OK;

    pParent = NULL;
    pParentEnd = NULL;

    if (!pChildNode)
    {
	hr = HXR_FAILED;
	goto cleanup;
    }

    pParent = new SMIL1Node;			    
    pParent->m_pParent = pChildNode->m_pParent;
    pParent->m_tag = tag;
    
    pParent->m_pNodeList = new SMIL1NodeList;
    pParent->m_pNodeList->m_pParentNode = pParent;
    
    pParentEnd = new SMIL1Node;
    pParentEnd->m_pParent = pParent;

    if (SMILPar == tag)
    {
	pParent->m_name = "par";
	pParentEnd->m_name = "par";
	pParentEnd->m_id = assignID("CLOSE-par");	
	pParentEnd->m_tag = SMILEndPar;
    }
    else if (SMILSeq == tag)
    {
	pParent->m_name = "seq";
	pParentEnd->m_name = "seq";
	pParentEnd->m_id = assignID("CLOSE-seq");
	pParentEnd->m_tag = SMILEndSeq;
    }
    else
    {
	HX_ASSERT(FALSE);
	hr = HXR_FAILED;
	goto cleanup;
    }
    
    mapID(pParent, TRUE);
    mapID(pParentEnd, TRUE);

cleanup:

    return hr;
}

void
CSmil1Parser::resetTimeline()
{
    if (m_pTimelineElementManager)
    {
        m_pTimelineElementManager->resetTimeline();
    }
}

SMIL1Node*
CSmil1Parser::getTimelineDescendent(SMIL1Node* pParentNode)
{
    SMIL1Node* pDescendentNode = NULL;

    CHXSimpleList* pNodeList = pParentNode->m_pNodeList;
    if(!pNodeList)
    {
	return pDescendentNode;
    }

    CHXSimpleList::Iterator i;
    for(i=pNodeList->Begin();i!=pNodeList->End();++i)
    {
	SMIL1Node* pNode = (SMIL1Node*)(*i);
	if(pNode->m_bDelete)
	{
	    continue;
	}
	if(pNode->m_tag == SMILAAnchor ||
	   pNode->m_tag == SMILSwitch)
	{
	    pNode = getTimelineDescendent(pNode);
	    if(pNode)
	    {
		pDescendentNode = pNode;
		break;
	    }
	}
	else
	{
	    if(pNode->m_tag == SMILSeq ||
		pNode->m_tag == SMILPar ||
		pNode->m_tag == SMILRef ||
		pNode->m_tag == SMILText ||
		pNode->m_tag == SMILImg ||
		pNode->m_tag == SMILAudio ||
		pNode->m_tag == SMILVideo ||
		pNode->m_tag == SMILAnimation ||
		pNode->m_tag == SMILTextstream ||
		pNode->m_tag == SMILAnchor)
	    {
		pDescendentNode = pNode;
		break;
	    }
	}
    }
    return pDescendentNode;
}

SMIL1Node*
CSmil1Parser::getTimelineDescendent(SMIL1Node* pParentNode, SMIL1Node* pSiblingNode)
{
    SMIL1Node* pFirstDescendent = getTimelineDescendent(pParentNode);
    if(!pSiblingNode)
    {
	return pFirstDescendent;
    }

    // first, find parent of sibling
    SMIL1Node* pSiblingParent = pSiblingNode->m_pParent;
    while(pSiblingParent &&
	  pSiblingParent->m_tag != SMILSeq &&
	  pSiblingParent->m_tag != SMILPar)
    {
	pSiblingParent = pSiblingParent->m_pParent;
    }

    if(!pSiblingParent)
    {
	return NULL;
    }

    // now find the sibling, then next sibling will be 
    // returned. Brain damaged, but what are ya gonna do?
    BOOL bFoundSibling = FALSE;
    SMIL1Node* pNewSibling = NULL;
    CHXSimpleList::Iterator i = pSiblingParent->m_pNodeList->Begin();
    for(; i != pSiblingParent->m_pNodeList->End() && !pNewSibling; ++i)
    {
	SMIL1Node* pThisNode = (SMIL1Node*)(*i);

	if(pThisNode->m_bDelete)
	{
	    continue;
	}

	if(pThisNode->m_tag == SMILAAnchor ||
	    pThisNode->m_tag == SMILSwitch)
	{
	    CHXSimpleList::Iterator j = pThisNode->m_pNodeList->Begin();

	    for(; j != pThisNode->m_pNodeList->End(); ++j)
	    {
		SMIL1Node* pChildNode = (SMIL1Node*)(*j);
		if(pChildNode->m_bDelete)
		{
		    continue;
		}
		if(pChildNode == pSiblingNode)
		{
		    bFoundSibling = TRUE;
		    break;
		}
		else if(bFoundSibling)
		{
		    pNewSibling = pChildNode;
		    break;
		}
	    }
	}
	else
	{
	    if(pThisNode == pSiblingNode)
	    {
		bFoundSibling = TRUE;
	    }
	    else if(bFoundSibling)
	    {
		pNewSibling = pThisNode;
		break;
	    }
	}
    }

    return pNewSibling;
}

HX_RESULT
CSmil1Parser::constructTimelineElements(SMIL1NodeList* pNodeList)
{
    HX_RESULT rc = HXR_OK;

    if(!pNodeList)
    {
	return rc;
    }

    CHXSimpleList::Iterator i;
    for(i=pNodeList->Begin();i!=pNodeList->End();++i)
    {
	if(HXR_OK != rc)
	{
	    return rc;
	}

	SMIL1Node* pNode = (SMIL1Node*)(*i);

	if(pNode->m_bDelete)	// skip this puppy
	{
	    continue;
	}

	switch(pNode->m_tag)
	{
	    case SMILSeq:
	    {
		SMIL1Node* pChildNode = getTimelineDescendent(pNode, NULL);
		CSmil1TimelineElement* pPrevElement = NULL;
		UINT16 uPrevGroup = (UINT16)-1;
		while(pChildNode)
		{
		    if (pChildNode->m_pElement &&
			pChildNode->m_pElement->m_pTimelineElement)
		    {			
			pNode->m_pElement->m_pTimelineElement->addChild(
				pChildNode->m_pElement->m_pTimelineElement);

			if(pPrevElement &&
			   uPrevGroup == pChildNode->m_nGroup)
			{
			    pPrevElement->setDependent(pChildNode->m_pElement->m_pTimelineElement);
			}
			
			pPrevElement = pChildNode->m_pElement->m_pTimelineElement;
			uPrevGroup = pChildNode->m_nGroup;
		    }

		    // there is no dependent on pChildNode which would repeat 
		    // itself indefinitely within the same seq
		    if (pChildNode->m_repeatTag == RepeatIndefiniteOnMe)
		    {
			break;
		    }
		    
		    pChildNode = getTimelineDescendent(pNode, pChildNode);
		}
	    }
	    break;

	    case SMILPar:
	    {
		SMIL1Node* pChildNode = getTimelineDescendent(pNode, NULL);
		while(pChildNode)
		{
		    if(pChildNode->m_pElement &&
		       pChildNode->m_pElement->m_pTimelineElement)
		    {
			pNode->m_pElement->m_pTimelineElement->addChild(
			    pChildNode->m_pElement->m_pTimelineElement);
		    }

		    pChildNode = getTimelineDescendent(pNode, pChildNode);
		}
	    }
	    break;

	    default:
	    break;
	}
	rc = constructTimelineElements(pNode->m_pNodeList);
    }
    return rc;
}

HX_RESULT
CSmil1Parser::handleNextElement(CSmil1ElementHandler* pHandler)
{
    HX_RESULT rc = HXR_OK;
    if(m_pPacketQueue->GetCount() > 0)
    {
	CSmil1Element* pElement = (CSmil1Element*)m_pPacketQueue->RemoveHead();
	pElement->m_pHandler = pHandler;
	rc = pElement->handleElement();
    }
    else if(m_bTimestampsResolved)
    {
	rc = HXR_STREAM_DONE;
    }
    else
    {
	rc = HXR_NO_DATA;
    }

    return rc;
}

void
CSmil1Parser::initTagAttributes()
{
    if(!m_pTagAttributeMap)
    {
	m_pTagAttributeMap = new CHXMapLongToObj;
    }
    CHXMapStringToOb* pStringMap = 0;

    // SMILSmil
    pStringMap = new CHXMapStringToOb;
    (*pStringMap)["id"] = 0;
    (*m_pTagAttributeMap)[SMILSmil] = pStringMap;

    // SMILHead
    pStringMap = new CHXMapStringToOb;
    (*pStringMap)["id"] = 0;
    (*m_pTagAttributeMap)[SMILHead] = pStringMap;

    // SMILBasicLayout
    pStringMap = new CHXMapStringToOb;
    (*pStringMap)["id"] = 0;
    (*pStringMap)["type"] = 0;
    (*m_pTagAttributeMap)[SMILBasicLayout] = pStringMap;

    // SMILRegion
    pStringMap = new CHXMapStringToOb;
    (*pStringMap)["id"] = 0;
    (*pStringMap)["title"] = 0;
    (*pStringMap)["height"] = 0;
    (*pStringMap)["width"] = 0;
    (*pStringMap)["background-color"] = 0;
    (*pStringMap)["left"] = 0;
    (*pStringMap)["top"] = 0;
    (*pStringMap)["z-index"] = 0;
    (*pStringMap)["fit"] = 0;
    (*pStringMap)["skip-content"] = 0;
    (*m_pTagAttributeMap)[SMILRegion] = pStringMap;

    // SMILRootLayout
    pStringMap = new CHXMapStringToOb;
    (*pStringMap)["id"] = 0;
    (*pStringMap)["title"] = 0;
    (*pStringMap)["height"] = 0;
    (*pStringMap)["width"] = 0;
    (*pStringMap)["background-color"] = 0;
    (*pStringMap)["overflow"] = 0;
    (*pStringMap)["skip-content"] = 0;
    (*m_pTagAttributeMap)[SMILRootLayout] = pStringMap;

    // SMILMeta
    pStringMap = new CHXMapStringToOb;
    (*pStringMap)["name"] = 0;
    (*pStringMap)["content"] = 0;
    (*pStringMap)["skip-content"] = 0;
    (*m_pTagAttributeMap)[SMILMeta] = pStringMap;

    // SMILBody
    pStringMap = new CHXMapStringToOb;
    (*pStringMap)["id"] = 0;
    (*m_pTagAttributeMap)[SMILBody] = pStringMap;

    // SMILPar
    pStringMap = new CHXMapStringToOb;
    (*pStringMap)["id"] = 0;
    (*pStringMap)["title"] = 0;
    (*pStringMap)["abstract"] = 0;
    (*pStringMap)["author"] = 0;
    (*pStringMap)["copyright"] = 0;
    (*pStringMap)["endsync"] = 0;
    (*pStringMap)["dur"] = 0;
    (*pStringMap)["repeat"] = 0;
    (*pStringMap)["region"] = 0;
    (*pStringMap)["begin"] = 0;
    (*pStringMap)["end"] = 0;
    (*pStringMap)["system-bitrate"] = 0;
    (*pStringMap)["system-language"] = 0;
    (*pStringMap)["system-required"] = 0;
    (*pStringMap)["system-screen-size"] = 0;
    (*pStringMap)["system-screen-depth"] = 0;
    (*pStringMap)["system-captions"] = 0;
    (*pStringMap)["system-overdub-or-caption"] = 0;
    (*m_pTagAttributeMap)[SMILPar] = pStringMap;

    // SMILSeq
    pStringMap = new CHXMapStringToOb;
    (*pStringMap)["id"] = 0;
    (*pStringMap)["title"] = 0;
    (*pStringMap)["abstract"] = 0;
    (*pStringMap)["author"] = 0;
    (*pStringMap)["copyright"] = 0;
    (*pStringMap)["dur"] = 0;
    (*pStringMap)["repeat"] = 0;
    (*pStringMap)["region"] = 0;
    (*pStringMap)["begin"] = 0;
    (*pStringMap)["end"] = 0;
    (*pStringMap)["system-bitrate"] = 0;
    (*pStringMap)["system-language"] = 0;
    (*pStringMap)["system-required"] = 0;
    (*pStringMap)["system-screen-size"] = 0;
    (*pStringMap)["system-screen-depth"] = 0;
    (*pStringMap)["system-captions"] = 0;
    (*pStringMap)["system-overdub-or-caption"] = 0;
    (*m_pTagAttributeMap)[SMILSeq] = pStringMap;

    // SMILSwitch
    pStringMap = new CHXMapStringToOb;
    (*pStringMap)["id"] = 0;
    (*pStringMap)["title"] = 0;
    (*m_pTagAttributeMap)[SMILSwitch] = pStringMap;

    // SMILRef
    pStringMap = new CHXMapStringToOb;
    (*pStringMap)["id"] = 0;
    (*pStringMap)["title"] = 0;
    (*pStringMap)["abstract"] = 0;
    (*pStringMap)["author"] = 0;
    (*pStringMap)["copyright"] = 0;
    (*pStringMap)["region"] = 0;
    (*pStringMap)["alt"] = 0;
    (*pStringMap)["longdesc"] = 0;
    (*pStringMap)["src"] = 0;
    (*pStringMap)["type"] = 0;
    (*pStringMap)["dur"] = 0;
    (*pStringMap)["repeat"] = 0;
    (*pStringMap)["fill"] = 0;
    (*pStringMap)["begin"] = 0;
    (*pStringMap)["end"] = 0;
    (*pStringMap)["clip-begin"] = 0;
    (*pStringMap)["clip-end"] = 0;
    (*pStringMap)["skip-content"] = 0;
    (*pStringMap)["system-bitrate"] = 0;
    (*pStringMap)["system-language"] = 0;
    (*pStringMap)["system-required"] = 0;
    (*pStringMap)["system-screen-size"] = 0;
    (*pStringMap)["system-screen-depth"] = 0;
    (*pStringMap)["system-captions"] = 0;
    (*pStringMap)["system-overdub-or-caption"] = 0;
    (*m_pTagAttributeMap)[SMILRef] = pStringMap;

    // SMILAAnchor
    pStringMap = new CHXMapStringToOb;
    (*pStringMap)["id"] = 0;
    (*pStringMap)["title"] = 0;
    (*pStringMap)["href"] = 0;
    (*pStringMap)["show"] = 0;
    (*m_pTagAttributeMap)[SMILAAnchor] = pStringMap;

    // SMILAnchor
    pStringMap = new CHXMapStringToOb;
    (*pStringMap)["id"] = 0;
    (*pStringMap)["title"] = 0;
    (*pStringMap)["href"] = 0;
    (*pStringMap)["show"] = 0;
    (*pStringMap)["begin"] = 0;
    (*pStringMap)["end"] = 0;
    (*pStringMap)["coords"] = 0;
    (*pStringMap)["fragment-id"] = 0;
    (*pStringMap)["skip-content"] = 0;
    (*pStringMap)["z-index"] = 0;
    (*m_pTagAttributeMap)[SMILAnchor] = pStringMap;

    // SMILRendererPreFetch
    pStringMap = new CHXMapStringToOb;
    (*pStringMap)["type"] = 0;
    (*m_pTagAttributeMap)[SMILRendererPreFetch] = pStringMap;
}

void
CSmil1Parser::deleteTagAttributes()
{
    if(m_pTagAttributeMap)
    {
        CHXMapLongToObj::Iterator i = m_pTagAttributeMap->Begin();
	for(; i != m_pTagAttributeMap->End(); ++i)
	{
	    CHXMapStringToOb* pStringMap = (CHXMapStringToOb*)(*i);
	    delete pStringMap;
	}
    }
    HX_DELETE(m_pTagAttributeMap);
}

BOOL
CSmil1Parser::isLegalAttribute(SMIL1NodeTag tag, const char* pAttName)
{
    // use SMILRef for all media object checks
    if(tag == SMILText ||
       tag == SMILImg ||
       tag == SMILAudio ||
       tag == SMILVideo ||
       tag == SMILAnimation ||
       tag == SMILTextstream)
    {
	tag = SMILRef;
    }
    CHXMapStringToOb* pStringMap = 0;
    if(m_pTagAttributeMap->Lookup(tag, (void*&)pStringMap))
    {
	void* pVoid = 0;
	if(pStringMap->Lookup(pAttName, pVoid))
	{
	    return TRUE;
	}
	else if (strcmp(pAttName, "xmlns") == 0 || 
	    strncmp(pAttName, "xmlns:", 6) == 0)
	{
	    // accept any namespace declarations.
	    return TRUE;
	}
	else
	{
	    BOOL bValidAttribute = FALSE;
	    char* pColon = (char *)strchr(pAttName, ':');
	    if(pColon)
	    {
		//check for valid namespace tag we can
		// ignore

		char* pTmpNam = new_string(pAttName);
		char* pNamespace = strtok(pTmpNam, ":");
		if(pNamespace)
		{
		    UINT32 ulTemp = 0;
		    if(m_pActiveNamespaceMap &&
		      (m_pActiveNamespaceMap->Lookup(pNamespace,
		      (void*&)ulTemp)))
		    {
			// OK, we can ignore it...
			bValidAttribute = TRUE;
		    }
		}
		delete[] pTmpNam;
	    }
	    if(bValidAttribute)
	    {
		return TRUE;
	    }
	}
    }

    return FALSE;
}

BOOL
CSmil1Parser::isRelativeURL(const char* pURL)
{
    BOOL rc = TRUE;

    CHXURL urlObj(pURL);
    IHXValues* pHeader = urlObj.GetProperties();
    if(pHeader)
    {
        IHXBuffer* pBuffer = NULL;

	if(HXR_OK == pHeader->GetPropertyBuffer(PROPERTY_SCHEME, pBuffer))
	{
	    // fully qualified URL
	    rc = FALSE;
	    HX_RELEASE(pBuffer);
	}
    }

    HX_RELEASE(pHeader);
    return rc;
}


HX_RESULT
CSmil1Parser::storeNamespaces(SMIL1Node* pNode)
{
    HX_RESULT rc = HXR_OK;

    if (pNode->m_pValues)
    {
	const char* pName = NULL;
	IHXBuffer* pBuffer = NULL;
	HX_RESULT res = pNode->m_pValues->GetFirstPropertyCString(pName, pBuffer);
	while (SUCCEEDED(rc) && SUCCEEDED(res))
	{
	    if (strcmp(pName, "xmlns") == 0)
	    {
		if (!pNode->m_pNamespaceList)
		{
		    pNode->m_pNamespaceList = new CHXSimpleList;
		    if (!pNode->m_pNamespaceList)
		    {
			rc = HXR_OUTOFMEMORY;
			break;
		    }
		}
		
		// duplicate atributes should allready be caught...
		SMIL1Namespace* pNS = new SMIL1Namespace("", pBuffer);
		pNode->m_pNamespaceList->AddHead(pNS);
	    }
	    else if (strncmp(pName, "xmlns:", 6) == 0)
	    {
		if (!pNode->m_pNamespaceList)
		{
		    pNode->m_pNamespaceList = new CHXSimpleList;
		    if (!pNode->m_pNamespaceList)
		    {
			rc = HXR_OUTOFMEMORY;
			break;
		    }
		}

		char* nsPrefix = (char *)strchr(pName, ':');
		++nsPrefix;

		SMIL1Namespace* pNS = new SMIL1Namespace(nsPrefix, pBuffer);
		pNode->m_pNamespaceList->AddHead(pNS);
	    }

	    HX_RELEASE(pBuffer);
	    res = pNode->m_pValues->GetNextPropertyCString(pName, pBuffer);
	}
    }
    return rc;
}

HX_RESULT
CSmil1Parser::addToNamespaceScope(SMIL1Node* pNode)
{
    HX_RESULT rc = HXR_OK;

    if (!m_pActiveNamespaceMap)
    {
	m_pActiveNamespaceMap = new CHXMapStringToOb;
	if (!m_pActiveNamespaceMap)
	{
	    return HXR_OUTOFMEMORY;
	}
    }

    if (pNode &&
	pNode->m_pNamespaceList)
    {
	for (CHXSimpleList::Iterator pIt = pNode->m_pNamespaceList->Begin();
	     pIt != pNode->m_pNamespaceList->End(); ++pIt)
	{
	    SMIL1Namespace* pNS = (SMIL1Namespace*)(*pIt);
	    IHXBuffer* pBuf = (IHXBuffer*)(*m_pActiveNamespaceMap)[pNS->m_name];

	    if (pBuf)
	    {
		if (!m_pNSConflictList)
		{
		    m_pNSConflictList = new CHXSimpleList;
		    if (!m_pNSConflictList)
		    {
			rc = HXR_OUTOFMEMORY;
			break;
		    }
		}

		SMIL1Namespace* pConflict = new SMIL1Namespace(pNS);
		if (!pConflict)
		{
		    rc = HXR_OUTOFMEMORY;
		    break;
		}

		m_pNSConflictList->AddHead(pConflict);
		
		HX_RELEASE(pBuf);

		(*m_pActiveNamespaceMap)[pNS->m_name] = pNS->m_pValue;
		pNS->m_pValue->AddRef();
	    }
	    else
	    {
		(*m_pActiveNamespaceMap)[pNS->m_name] = pNS->m_pValue;
		pNS->m_pValue->AddRef();
	    }
	}
    }
    return rc;
}

HX_RESULT
CSmil1Parser::removeFromNamespaceScope(SMIL1Node* pNode)
{
    HX_RESULT rc = HXR_OK;
    
    if (pNode->m_pNamespaceList)
    {
	for (CHXSimpleList::Iterator pIt = pNode->m_pNamespaceList->Begin();
	     pIt != pNode->m_pNamespaceList->End(); ++pIt)
	{
	    SMIL1Namespace* pNS = (SMIL1Namespace*)(*pIt);

	    HX_ASSERT((*m_pActiveNamespaceMap)[pNS->m_name]);
	    IHXBuffer* pBuf = (IHXBuffer*)(*m_pActiveNamespaceMap)[pNS->m_name];

	    if (pBuf != NULL)
	    {
		HX_RELEASE(pBuf);
		m_pActiveNamespaceMap->RemoveKey(pNS->m_name);

		// check for conficts.
		if (m_pNSConflictList)
		{
		    LISTPOSITION pos = m_pNSConflictList->GetHeadPosition();
		    while (pos)
		    {
			SMIL1Namespace* pCon = 
			    (SMIL1Namespace*)m_pNSConflictList->GetAt(pos);

			if (strcmp(pCon->m_name, pNS->m_name) == 0)
			{
			    (*m_pActiveNamespaceMap)[pCon->m_name] = pCon->m_pValue;
			    pCon->m_pValue->AddRef();

			    HX_DELETE(pCon);

			    m_pNSConflictList->RemoveAt(pos);
			    // BREAK at the first found match.
			    break;
			}

			m_pNSConflictList->GetNext(pos);
		    }
		}
	    }
	}
    }
    return rc;
}


HX_RESULT
CSmil1Parser::addGlobalNamespace(const char* pNamespace,
			  const char* pPrefix)
{
    HX_RESULT rc = HXR_OK;

    if(m_bNoNamespaces)
    {
	rc = HXR_FAIL;
	CSmil1SMILSyntaxErrorHandler errHandler(m_pContext);
	errHandler.ReportError(SMILErrorSMIL10Document, NULL, 0);
    }
    else if (pNamespace)
    {
	if(!m_pActiveNamespaceMap)
	{
	    m_pActiveNamespaceMap = new CHXMapStringToOb;
	}

	if (!m_pRequireTagsMap)
	{
	    m_pRequireTagsMap = new CHXMapStringToOb;
	}

	if(pPrefix)
	{
	    // get an IHXBuffer
	    //XXXJEFFA should use CF for this
	    IHXBuffer* pBuffer = (IHXBuffer*) new CHXBuffer;
	    pBuffer->AddRef();
	    pBuffer->Set((UINT8*)pNamespace, strlen(pNamespace) + 1);

	    (*m_pActiveNamespaceMap)[pPrefix] = pBuffer;
	    
	    // add the prefix to the require map so system 
	    // required checks work
	    (*m_pRequireTagsMap)[pPrefix] = 0;

	    if(strcmp(pPrefix, (const char*) RN_PREFIX) == 0)
	    {
		m_bRNNamespace = TRUE;
	    }
	}
	else
	{
	    // empty prefix, 
	    // don't ignore unrecognized elements...
	    m_bIgnoreUnrecognizedElements = FALSE;
	}
    }

    return rc;
}


HX_RESULT
CSmil1Parser::storeError(HX_RESULT errCode, const char* pErrorString, 
			 const char* pFrameString, UINT32 ulLineNumber, 
			 UINT32 ulLinePosition, BOOL bXml)
{
    // XXXJHUG - this is what the SMIL error hanndler uses 1024....
    char errorString[1024]; /* Flawfinder: ignore */
    if (bXml)
    {
	CSmil1XMLSyntaxErrorHandler errHandler(m_pContext);
    
	errHandler.GetReportString(errCode, errorString);
    }
    else
    {
	CSmil1SMILSyntaxErrorHandler errHandler(m_pContext);
    
	errHandler.GetReportString((SMILErrorTag)errCode, errorString);
    }

    IHXBuffer* pBuf;
    m_pClassFactory->CreateInstance(CLSID_IHXBuffer, (void**)&pBuf);

    pBuf->SetSize(strlen(errorString) + strlen(pErrorString) + 10);
    char* buffer = (char*) pBuf->GetBuffer();
    sprintf(buffer, errorString, ulLineNumber, pErrorString); /* Flawfinder: ignore */

    m_pErrors->Add(pBuf);

    return HXR_OK;
}


HX_RESULT
CSmil1Parser::getErrors(CHXPtrArray** pErrs)
{
    *pErrs = m_pErrors;
    return HXR_OK;
}

ElementWithinTag
CSmil1Parser::GetElementWithin(const char* pID)
{
    ElementWithinTag	elementWithinTag = WithinUnknown;
    SMIL1Node*		pNode = NULL;
    SMIL1Node*		pParentNode = NULL;

    if(!m_pIDMap->Lookup(pID, (void*&)pNode))
    {	
	HX_ASSERT(FALSE);
	goto cleanup;
    }

    while (pNode->m_pParent)
    {	
	switch (pNode->m_pParent->m_tag)
	{
	case SMILPar:
	    if (elementWithinTag == WithinSeq)
	    {
		elementWithinTag = WithinSeqInPar;
	    }
	    else
	    {
		elementWithinTag = WithinPar;
	    }
	    goto cleanup;
	case SMILSeq:
	    elementWithinTag = WithinSeq;
	    break;
	default:
	    break;
	}

	pNode = pNode->m_pParent;
    }

cleanup:

    return elementWithinTag;
}

void
CSmil1Parser::InitPersistent(UINT32 ulPersistentComponentID, 
			    ElementWithinTag elementWithinTag)
{
    m_ulPersistentComponentID = ulPersistentComponentID;
    m_elementWithinTag = elementWithinTag;
}

/*
 * CSmil1ParserResponse methods
 */

CSmil1ParserResponse::CSmil1ParserResponse(CSmil1Parser* pParser)
:   m_pParser(pParser)
,   m_lRefCount(0)
{
}

CSmil1ParserResponse::~CSmil1ParserResponse()
{
}

STDMETHODIMP 
CSmil1ParserResponse::QueryInterface(REFIID riid, void** ppvObj)
{
    if (IsEqualIID(riid, IID_IUnknown))
    {
	AddRef();
	*ppvObj = this;
	return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXXMLParserResponse))
    {
	AddRef();
	*ppvObj = (IHXXMLParserResponse*)this;
	return HXR_OK;
    }

    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}

STDMETHODIMP_(ULONG32) 
CSmil1ParserResponse::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

STDMETHODIMP_(ULONG32) 
CSmil1ParserResponse::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}

STDMETHODIMP
CSmil1ParserResponse::HandleStartElement(const char* pName,
				 IHXValues* pAttributes,
				 UINT32 ulLineNumber,
				 UINT32 ulColumnNumber)
{
    HX_RESULT rc = HXR_OK;

    SMIL1Node* pParentNode = 
	(SMIL1Node*)m_pParser->m_pNodeListStack->TopOfStack();
    HX_ASSERT(pParentNode);

    SMIL1Node* pNode = new SMIL1Node;
    pNode->m_name = pName;
    pNode->m_pParent = pParentNode;

    pNode->m_pNodeList = new SMIL1NodeList;
    pNode->m_pNodeList->m_pParentNode = pNode;

    pNode->m_ulTagStartLine = ulLineNumber;
    pNode->m_ulTagStartColumn = ulColumnNumber;

    if(!pNode->m_pParent->m_bSkipContent)
    {
	IHXBuffer* pBuffer = NULL;
	if(HXR_OK == pAttributes->GetPropertyCString("id", pBuffer))
	{
	    const char* pID = (const char*)pBuffer->GetBuffer();
	    // XXXMEH - 04/27/99 - change this to still throw
	    // an error for isspace(), but not for isdigit()
	    // - not legal SMIL, but we still allowed
	    // it in previous players.
//	    if (isdigit(*pID) || isspace(*pID))
	    if (isspace(*pID))
	    {
		rc = HXR_XML_ILLEGALID;
		CSmil1XMLSyntaxErrorHandler errHandler(m_pParser->m_pContext);
		errHandler.ReportError(rc, pID, ulLineNumber);
		goto exit;
	    }
	    else if (m_pParser->m_bStoreErrors && isdigit(*pID))
	    {
		m_pParser->storeError(HXR_XML_ILLEGALID, pID, 0, 
		    ulLineNumber, ulColumnNumber);
	    }
	    else
	    {
		// append persistent ID to the end of region id
		// to make it unique in nested meta
		if (!strcmp(pNode->m_name, "region"))
		{
		    //char szPersistentComponentID[MAX_DISPLAY_NAME] = {0};
		    //itoa(m_pParser->m_ulPersistentComponentID, szPersistentComponentID, 10);
		
		    pNode->m_id = pID;
		    //pNode->m_id += "_";
		    //pNode->m_id += szPersistentComponentID;

		    pNode->m_repeatid = pNode->m_id;
		}
		else
		{
		    pNode->m_id = pID;
		    pNode->m_repeatid = pID;
		}
		HX_RELEASE(pBuffer);
	    }
	}
	else
	{
	    //[SMIL 1.0 Compliance] Fixes PR 24034: region tag must have
	    // an id attribute:
	    if (m_pParser->m_bSMIL10FullCompliance  &&
		    !strcmp(pNode->m_name, "region"))
	    {
		rc = HXR_XML_ILLEGALID;
		CSmil1XMLSyntaxErrorHandler errHandler(m_pParser->m_pContext);
		const char* pTmp = "region id=\"\"";
		errHandler.ReportError(rc, pTmp, ulLineNumber);
		goto exit;
	    }
	    else
	    {
		pNode->m_id = m_pParser->assignID(pName);
		pNode->m_repeatid = pNode->m_id;
	    }

	    if (m_pParser->m_bStoreErrors && !strcmp(pNode->m_name, "region"))
	    {
		const char* pTmp = "region id=\"\"";
		m_pParser->storeError(HXR_XML_ILLEGALID, pTmp, 0, 
		    ulLineNumber, ulColumnNumber);
	    }
	}
	
	if(HXR_OK != m_pParser->mapID(pNode))
	{
	    rc = HXR_XML_ILLEGALID;
	    CSmil1XMLSyntaxErrorHandler errHandler(m_pParser->m_pContext);
	    errHandler.ReportError(rc, pNode->m_id, ulLineNumber);
	    goto exit;
	}

	if(strcmp(pName, "smil") == 0)
	{
	    pNode->m_tag = SMILSmil;
	}
	else if(strcmp(pName, "layout") == 0)
	{
	    pNode->m_tag = SMILBasicLayout;
	    if(HXR_OK == pAttributes->GetPropertyCString("type", pBuffer))
	    {
		const char* pType = (const char*)pBuffer->GetBuffer();
		if(strcmp(pType, "text/smil-basic-layout") != 0)
		{
		    pNode->m_bDelete = TRUE;	// only basic layout supported
		}
		HX_RELEASE(pBuffer);
	    }
	}
	else if(strcmp(pName, "meta") == 0)
	{
	    pNode->m_tag = SMILMeta;
	    pNode->m_bSkipContent = TRUE;
	}
	else if(strcmp(pName, "head") == 0)
	{
	    pNode->m_id = "head";
	    pNode->m_tag = SMILHead;
	}
	else if(strcmp(pName, "body") == 0)
	{
	    pNode->m_id = "body";
	    pNode->m_tag = SMILBody;
	}
	else if(strcmp(pName, "region") == 0)
	{
	    pNode->m_tag = SMILRegion;
	    pNode->m_bSkipContent = TRUE;
	}
	else if(strcmp(pName, "root-layout") == 0)
	{
	    pNode->m_tag = SMILRootLayout;
	    pNode->m_bSkipContent = TRUE;
	}
	else if(strcmp(pName, "switch") == 0)
	{
	    pNode->m_tag = SMILSwitch;
	}
	else if(strcmp(pName, "text") == 0)
	{
	    pNode->m_tag = SMILText;
	}
	else if(strcmp(pName, "img") == 0)
	{
	    pNode->m_tag = SMILImg;
	}
	else if(strcmp(pName, "ref") == 0)
	{
	    pNode->m_tag = SMILRef;
	}
	else if(strcmp(pName, "audio") == 0)
	{
	    pNode->m_tag = SMILAudio;
	}
	else if(strcmp(pName, "video") == 0)
	{
	    pNode->m_tag = SMILVideo;
	}
	else if(strcmp(pName, "animation") == 0)
	{
	    pNode->m_tag = SMILAnimation;
	}
	else if(strcmp(pName, "textstream") == 0)
	{
	    pNode->m_tag = SMILTextstream;
	}
	else if(strcmp(pName, "a") == 0)
	{
	    pNode->m_tag = SMILAAnchor;
	}
	else if(strcmp(pName, "anchor") == 0)
	{
	    pNode->m_tag = SMILAnchor;
	    pNode->m_bSkipContent = TRUE;
	}
	else if(strcmp(pName, "par") == 0)
	{
	    pNode->m_tag = SMILPar;
	}
	else if(strcmp(pName, "seq") == 0)
	{
	    pNode->m_tag = SMILSeq;
	}
	else if(m_pParser->m_bRNNamespace && 
	    strcmp(pName, (const char*) RN_TAG_RENDERER_LIST) == 0)
	{
	    pNode->m_id = m_pParser->assignID((const char*) RN_TAG_RENDERER_LIST);
	    pNode->m_tag = SMILRNRendererList;
	}
	else if(strcmp(pName, (const char*) RN_TAG_RENDERER) == 0)
	{
	    pNode->m_tag = SMILRendererPreFetch;
	}
	else 
	{
	    BOOL bValidTag = FALSE;
	    char* pColon = (char *)strchr(pName, ':');
	    if(pColon)
	    {
		//check for valid namespace tag we can
		// ignore

		char* pTmpNam = new_string(pName);
		char* pNamespace = strtok(pTmpNam, ":");
		if(pNamespace)
		{
		    UINT32 ulTemp = 0;
		    if(m_pParser->m_pActiveNamespaceMap &&
		      (m_pParser->m_pActiveNamespaceMap->Lookup(pNamespace,
		      (void*&)ulTemp)))
		    {
			// OK, we can ignore it...
			bValidTag = TRUE;
		    }
		}
		delete[] pTmpNam;
	    }
	    if(!bValidTag && 
		!m_pParser->m_bIgnoreUnrecognizedElements)
	    {
		rc = HXR_FAIL;
		CSmil1SMILSyntaxErrorHandler errHandler(m_pParser->m_pContext);
		errHandler.ReportError(SMILErrorUnrecognizedTag, pName, ulLineNumber);
		goto exit;
	    }
	    else
	    {
		pNode->m_bDelete = TRUE;	// don't ask, don't tell...
	    }

	    if (!bValidTag && m_pParser->m_bStoreErrors)
	    {
		m_pParser->storeError(SMILErrorUnrecognizedTag, pName, 0, 
		    ulLineNumber, ulColumnNumber, FALSE);
	    }
	}

	pNode->m_pValues = pAttributes;
	pNode->m_pValues->AddRef();

	rc = m_pParser->storeNamespaces(pNode);
	if (SUCCEEDED(rc))
	{
	    rc = m_pParser->addToNamespaceScope(pNode);
	}

	const char* pName = NULL;
	HX_RESULT res = pAttributes->GetFirstPropertyCString(pName, pBuffer);
	while(HXR_OK == res && SUCCEEDED(rc))
	{
	    const char* pActualValue = (const char*)pBuffer->GetBuffer();

	    if(!m_pParser->isLegalAttribute(pNode->m_tag, pName) )
	    {
		if (!m_pParser->m_bIgnoreUnrecognizedElements)
		{
		    rc = HXR_FAIL;
		    m_pParser->badAttributeError(pNode->m_tag, pName,
			pNode->m_ulTagStartLine, FALSE);
		    break;
		}
		
		if (m_pParser->m_bStoreErrors)
		{
		    m_pParser->badAttributeError(pNode->m_tag, pName, 
			ulLineNumber, TRUE);
		}

	    }

	    if(strcmp(pName, "skip-content") == 0)
	    {
		if(strcmp(pActualValue, "true") == 0)
		{
		    pNode->m_bSkipContent = TRUE;
		}
		else
		{
		    pNode->m_bSkipContent = FALSE;
		}
	    }
	    HX_RELEASE(pBuffer);
	    res = pAttributes->GetNextPropertyCString(pName, pBuffer);
	}

	m_pParser->m_pNodeListStack->Push(pNode);
    }

exit:
    return rc;
}

STDMETHODIMP
CSmil1ParserResponse::HandleEndElement(const char* pName,
				      UINT32 ulLineNumber,
				      UINT32 ulColumnNumber)
{
    HX_RESULT rc = HXR_OK;

    SMIL1Node* pCurrentNode = (SMIL1Node*)m_pParser->m_pNodeListStack->Pop();
    SMIL1Node* pParentNode = (SMIL1Node*)m_pParser->m_pNodeListStack->TopOfStack();
    HX_ASSERT(pCurrentNode);
    HX_ASSERT(pParentNode);

    if (pParentNode)
    {
        pParentNode->m_pNodeList->AddTail(pCurrentNode);
    }

    SMIL1Node* pEndNode = new SMIL1Node;
    pEndNode->m_name = pName;
    pEndNode->m_id.Format("CLOSE-%s", pName);
    pEndNode->m_pParent = pParentNode;
    pEndNode->m_ulTagStartLine = ulLineNumber;
    pEndNode->m_ulTagStartColumn = ulColumnNumber;

    if(strcmp(pName, "seq") == 0)
    {
	pEndNode->m_tag = SMILEndSeq;
    }
    else if(strcmp(pName, "par") == 0)
    {
	pEndNode->m_tag = SMILEndPar;
    }
    else if(strcmp(pName, "a") == 0)
    {
	pEndNode->m_tag = SMILEndAAnchor;
    }

    pCurrentNode->m_pNodeList->AddTail(pEndNode);

    rc = m_pParser->removeFromNamespaceScope(pCurrentNode);

    return rc;
}

STDMETHODIMP
CSmil1ParserResponse::HandleCharacterData(IHXBuffer* pBuffer,
					 UINT32 ulLineNumber,
					 UINT32 ulColumnNumber)
{
    HX_RESULT rc = HXR_OK;

    return rc;
}

STDMETHODIMP
CSmil1ParserResponse::HandleProcessingInstruction(const char* pTarget,
					  IHXValues* pAttributes,
					  UINT32 ulLineNumber,
					  UINT32 ulColumnNumber)
{
    HX_RESULT rc = HXR_OK;

    if(strcmp(pTarget, "xml:namespace") == 0)
    {
	IHXBuffer* pNamespaceBuffer = NULL;
	IHXBuffer* pPrefixBuffer = NULL;
	const char* pNamespace = NULL;
	const char* pPrefix = NULL;
	if(HXR_OK == pAttributes->GetPropertyCString("ns", pNamespaceBuffer))
	{
	    pNamespace = (const char*)pNamespaceBuffer->GetBuffer();
	}
	if(HXR_OK == pAttributes->GetPropertyCString("prefix", pPrefixBuffer))
	{
	    pPrefix = (const char*)pPrefixBuffer->GetBuffer();
	}
	rc = m_pParser->addGlobalNamespace(pNamespace, pPrefix);
	HX_RELEASE(pNamespaceBuffer);
	HX_RELEASE(pPrefixBuffer);
    }
    return rc;
}

STDMETHODIMP
CSmil1ParserResponse::HandleUnparsedEntityDecl(const char* /*IN*/  pEntityName,
				      const char* /*IN*/  pSystemID,
				      const char* /*IN*/  pPublicID,
				      const char* /*IN*/  pNotationName,
				      UINT32 ulLineNumber,
				      UINT32 ulColumnNumber)
{
    HX_RESULT rc = HXR_OK;

    return rc;
}

STDMETHODIMP
CSmil1ParserResponse::HandleNotationDecl(const char* /*IN*/  pNotationName,
				const char* /*IN*/  pSystemID,
				const char* /*IN*/  pPublicID,
				UINT32 ulLineNumber,
				UINT32 ulColumnNumber)
{
    HX_RESULT rc = HXR_OK;

    return rc;
}

STDMETHODIMP
CSmil1ParserResponse::HandleUnparsedDoctypeDecl(const char*  /*IN*/ pName, 
					    const char*  /*IN*/ pSystemID,
					    const char*  /*IN*/ pPublicID, 
					    UINT32	 /*IN*/	ulLineNumber,
					    UINT32	 /*IN*/	ulColumNumber)
{
    HX_RESULT rc = HXR_OK;

    if(strcmp(pName, "smil") == 0 &&
       strcmp(pSystemID, "http://www.w3.org/TR/REC-smil/SMIL10.dtd") == 0 &&
       strcmp(pPublicID, "-//W3C//DTD SMIL 1.0//EN") == 0)
    {
	// only be strict, still allow namespaces...
	//m_pParser->m_bNoNamespaces = TRUE;
	m_pParser->m_bIgnoreUnrecognizedElements = FALSE;
    }

    return rc;
}

STDMETHODIMP
CSmil1ParserResponse::HandleComment(const char* /*IN*/  pComment,
				   UINT32 ulLineNumber,
				   UINT32 ulColumnNumber)
{
    HX_RESULT rc = HXR_OK;

    return rc;
}

STDMETHODIMP
CSmil1ParserResponse::HandleDefault(IHXBuffer*	/*IN*/	pBuffer,
				   UINT32 ulLineNumber,
				   UINT32 ulColumnNumber)
{
    HX_RESULT rc = HXR_OK;

    return rc;
}

HX_RESULT
CSmil1ParserResponse::ErrorInLastTag(HX_RESULT err, 
    const char* pErrorString, const char* pFrameString, 
    UINT32 ulLineNumber, UINT32 ulLinePosition)
{
    // XXXJHUG - we could add the error to the actual node...
    // SMIL1Node* pCurrentNode = (SMIL1Node*)m_pParser->m_pNodeListStack->Pop();
    // pCurrentNode->m_errs->Add
    //

    // If we were going to warn for errors I think the warnings should be
    // thrown from here.  (modify ReportError to have a warning level.)
    //
    // But for dumping the errors there is no benifit for this.
    // therefore we will just add the error to a ptr array of errors.
    // this array will also be added to when the SMIL attributes are bad.
    //
    // XXX to store in the parse tree you also have to make sure the last
    // call was a tag, and not a PI or a directive or something that was 
    // ignored.
    
    return m_pParser->storeError(err, pErrorString, pFrameString, 
	    ulLineNumber, ulLinePosition);
}

/*
 * SMIL1Node methods
 */

SMIL1Node::SMIL1Node():
    m_pNodeList(0),
    m_pParent(0),
    m_pDependency(0),
    m_tag(SMILUnknown),
    m_num(0),
    m_pValues(0),
    m_curPosition(0),
    m_pElement(0),
    m_nGroup((UINT16)-1),
    m_bLastInGroup(FALSE),
    m_bDelete(FALSE),
    m_bSkipContent(FALSE),
    m_pNamespaceList(NULL),
    m_bRepeatHandled(FALSE),
    m_repeatTag(RepeatUnknown)
{
}

SMIL1Node::SMIL1Node(const SMIL1Node& rhs, BOOL bKeepId, CSmil1Parser* pParser)
{
    if (bKeepId)
    {
	m_id = rhs.m_id;
    }
    else
    {
	// need a unique ID, so make one...
	char* pIDName = new char [256];
        if (pIDName && pParser)
        {
	    sprintf(pIDName, "node_copy_%ld", pParser->GetUniqueNumber()); /* Flawfinder: ignore */
	    m_id = pIDName;
        }
        HX_VECTOR_DELETE(pIDName);
    }

    m_repeatid = rhs.m_repeatid;
    m_pParent = rhs.m_pParent;
    m_pDependency = rhs.m_pDependency;
    m_tag = rhs.m_tag;
    m_num = rhs.m_num;
    m_curPosition = rhs.m_curPosition;
    m_pElement = rhs.m_pElement;
    m_nGroup = rhs.m_nGroup;
    m_bLastInGroup = rhs.m_bLastInGroup;
    m_bDelete = rhs.m_bDelete;
    m_bSkipContent = rhs.m_bSkipContent;
    m_bRepeatHandled = FALSE;
    m_repeatTag = rhs.m_repeatTag;

    if(rhs.m_pValues)
    {
	m_pValues = rhs.m_pValues;
	m_pValues->AddRef();
    }
    else
    {
	m_pValues = NULL;
    }
    if(rhs.m_pNodeList)
    {
	m_pNodeList = rhs.m_pNodeList->copy(this, bKeepId, pParser);
    }
    else
    {
	m_pNodeList = NULL;
    }
    if (rhs.m_pNamespaceList)
    {
    	m_pNamespaceList = new CHXSimpleList;
	for (CHXSimpleList::Iterator pIt = rhs.m_pNamespaceList->Begin();
	     pIt != rhs.m_pNamespaceList->End(); ++pIt)
	{
	    SMIL1Namespace* pNS = (SMIL1Namespace*)(*pIt);
	    SMIL1Namespace* pNewNS = new SMIL1Namespace(pNS);
	    m_pNamespaceList->AddHead(pNewNS);
	}
    }
    else
    {
	m_pNamespaceList = NULL;
    }
}

SMIL1Node::~SMIL1Node()
{
    HX_DELETE(m_pNodeList);
    HX_RELEASE(m_pValues);

    if (m_pNamespaceList) 
    {
	while (!m_pNamespaceList->IsEmpty())
	{
	    SMIL1Namespace* pNS = (SMIL1Namespace*)m_pNamespaceList->RemoveHead();
	    HX_DELETE(pNS);
	}
    }
    HX_DELETE(m_pNamespaceList);
}

SMIL1Node*
SMIL1Node::getFirstChild()
{
    if(!m_pNodeList)
    {
	return 0;
    }
    m_curPosition = m_pNodeList->GetHeadPosition();
    if(m_curPosition)
    {
	return (SMIL1Node*)m_pNodeList->GetNext(m_curPosition);
    }
    return 0;
}

SMIL1Node*
SMIL1Node::getNextChild()
{
    if(m_curPosition)
    {
	return (SMIL1Node*)m_pNodeList->GetNext(m_curPosition);
    }
    return 0;
}

SMIL1NodeList::SMIL1NodeList():
    m_pParentNode(0)
{
}

SMIL1NodeList::~SMIL1NodeList()
{
    CHXSimpleList::Iterator i;

    for(i = Begin(); i != End(); ++i)
    {
	SMIL1Node* pNode = (SMIL1Node*)(*i);
	HX_DELETE(pNode);
    }
}

SMIL1NodeList*
SMIL1NodeList::copy(SMIL1Node* pParent, BOOL bKeepId, CSmil1Parser* pParser)
{
    SMIL1NodeList* pNewList = new SMIL1NodeList;
    m_pParentNode = pParent;

    CHXSimpleList::Iterator i = Begin();
    for(; i != End(); ++i)
    {
	SMIL1Node* pNode = (SMIL1Node*)(*i);
	SMIL1Node* pNodeCopy = new SMIL1Node(*pNode, bKeepId, pParser);
	pNodeCopy->m_pParent = pParent;
	pNewList->AddTail(pNodeCopy);
    }
    return pNewList;
}
