Logo Search packages:      
Sourcecode: helix-player version File versions

smlparse.cpp

/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: smlparse.cpp,v 1.10.2.7 2004/07/13 23:02:47 ehodge 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 ***** */

// system
#include <time.h>
#include <math.h>
#if defined(_UNIX)  &&  (!(defined(_BEOS))) && (!(defined(_MAC_UNIX)))
/*for Display and Screen structs and X functions:*/
#include "hlxclib/string.h"
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <sys/types.h>
#endif

#if defined (_SYMBIAN)
#  ifdef _SYMBIAN7
#    include <hal_data.h> 
#  endif
#  include <hal.h>
#  include <coemain.h>
#  include <e32svr.h>
#  include <e32math.h>
#endif

// include
#include "hxtypes.h"
#include "hxwintyp.h"
#include "hxcom.h"
#include "hxcomm.h"
#include "hxprefs.h"
#include "hxxml.h"
#include "hxmon.h"
#include "smiltype.h"
#include "hxupgrd.h"
// pncont
#include "chxpckts.h"
#include "hxstring.h"
#include "hxslist.h"
#include "rtsputil.h"
#include "hxstack.h"
// pnmisc
#include "nptime.h"
#include "smpte.h"
#include "hxstrutl.h"
#include "hxurl.h"
#include "hxparse.h"
#include "hxwinver.h"
#include "dbcs.h" /* for HXGetNextChar() */
// rnxmllib
#include "xmlreslt.h"
#include "hxxmlprs.h"
// rmasmil
#include "smlelem.h"
#include "smltime.h"
#include "smlerror.h"
#include "smlprstime.h"
#include "animattr.h"
#include "binrymap.h"
#include "smlparse.h"
#include "parstabl.h"
#include "smlutil.h"
// pndebug
#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE
static const char HX_THIS_FILE[] = __FILE__;
#endif


#define TARGET_OF_LINK_DESTINATION_SOUND_LEVEL_STR "targetOfLinkDestnSndLevel"
#define TARGET_OF_LINK_SOURCE_SOUND_LEVEL_STR      "targetOfLinkSourceSndLevel"

#if defined(_DEBUG)
// /#define XXXEHODGE_DEBUG_EXCL_TRACK_REMOVAL
#if defined(XXXEHODGE_DEBUG_EXCL_TRACK_REMOVAL)
static BOOL bFirstExclTrackChangeDebugOut = TRUE;
#endif
#endif

#if defined(_DEBUG)
// /#define XXXEHODGE_DEBUG_RESETTIMELINEELEMENTDUR_AND_DELAY
#if defined(XXXEHODGE_DEBUG_RESETTIMELINEELEMENTDUR_AND_DELAY)
static BOOL bFirstResetTimelineElementDuration = TRUE;
#endif /*XXXEHODGE_DEBUG_RESETTIMELINEELEMENTDUR_AND_DELAY*/
#endif /*_DEBUG*/

// /This enables RealPlayer 8 (and prior) behavior when there is no explicit
// outer time container (seq, par, [or excl in a SMIL 2.0 doc]):
#define ALLOW_PLAYLIST_STYLE_SEQ  1
#define DONT_ALLOW_PLAYLIST_STYLE_SEQ_IF_EXPLICIT_OUTER_TIMECONTAINER  1

#define CHECKPENDING_TIME_VS_CURTIME_FUDGE_FACTOR   150 /* in milliseconds .*/

// /XXXEH- in order to get Interop Timing #25.6 working, we had to trick the
// core into staying alive by adding this track in slightly before the one
// that paused it has finished:
#define FUDGE_FACTOR_WHEN_DEFERRING_PAST_END_OF_GROUP     100 /*milliseconds*/
#define FUDGE_FACTOR_FOR_DEFERRING_PRIOR_TO_END_OF_GROUP  500 /*milliseconds*/

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";


// /These are all the SMIL Module namespaces recognized by the
// SMIL 2.0 Language:
// (From http://www.w3.org/AudioVideo/Group/Modules/
// smil-modules.html#smilModulesNSSMIL20ModuleIdentifiers)
const char* const CSmilParser::zm_pSupportedSMIL2ModuleNamespaces[
      NUM_SUPPORTED_SMIL_2_0_MODULE_NAMESPACES+1] =
{
    // /XXXEH- TODO: OPTIMIZATION: these all share the same base URL, so
    // we should just store the last part here and combine with the base
    // when comparing to a used namespace:
    "http://www.w3.org/2000/SMIL20/CR/NestedTimeContainers",
    "http://www.w3.org/2000/SMIL20/CR/DeprecatedFeatures",
    "http://www.w3.org/2000/SMIL20/CR/HostLanguage",
#if defined(HANDLE_CATCHALL_NS_FOR_ALL_SMIL_2_0_MODULES)
    "http://www.w3.org/2000/SMIL20/CR/",
#else
    "",
#endif
    "http://www.w3.org/2000/SMIL20/CR/Language",
    "http://www.w3.org/2000/SMIL20/CR/IntegrationSet",

    // /The rest are SMIL 2.0 Module namespaces:
    "http://www.w3.org/2000/SMIL20/CR/AccessKeyTiming",
    "http://www.w3.org/2000/SMIL20/CR/AudioLayout",
    "http://www.w3.org/2000/SMIL20/CR/BasicAnimation",
    "http://www.w3.org/2000/SMIL20/CR/BasicContentControl",
    "http://www.w3.org/2000/SMIL20/CR/BasicInlineTiming",
    "http://www.w3.org/2000/SMIL20/CR/BasicLayout",
    "http://www.w3.org/2000/SMIL20/CR/BasicLinking",
    "http://www.w3.org/2000/SMIL20/CR/BasicMedia",
    "http://www.w3.org/2000/SMIL20/CR/BasicTimeContainers",
    "http://www.w3.org/2000/SMIL20/CR/BasicTransistions",
    "http://www.w3.org/2000/SMIL20/CR/BrushMedia",
    "http://www.w3.org/2000/SMIL20/CR/CoordinatedTransitions",
    "http://www.w3.org/2000/SMIL20/CR/CustomTestAttributes",
    "http://www.w3.org/2000/SMIL20/CR/EventTiming",
    "http://www.w3.org/2000/SMIL20/CR/ExclTimeContainers",
    "http://www.w3.org/2000/SMIL20/CR/FillDefault",
    "http://www.w3.org/2000/SMIL20/CR/HierarchicalLayout",
#if defined(HANDLE_SMIL_2_0_INLINETRANSITIONS_MODULE)
    "http://www.w3.org/2000/SMIL20/CR/InlineTransitions",
#else
    "",
#endif
    "http://www.w3.org/2000/SMIL20/CR/LinkingAttributes",
    "http://www.w3.org/2000/SMIL20/CR/MediaAccessibility",
    "http://www.w3.org/2000/SMIL20/CR/MediaClipMarkers",
    "http://www.w3.org/2000/SMIL20/CR/MediaClipping",
    "http://www.w3.org/2000/SMIL20/CR/MediaDescription",
    "http://www.w3.org/2000/SMIL20/CR/MediaMarkerTiming",
    "http://www.w3.org/2000/SMIL20/CR/MediaParam",
    "http://www.w3.org/2000/SMIL20/CR/Metainformation",
    "http://www.w3.org/2000/SMIL20/CR/MinMaxTiming",
    "http://www.w3.org/2000/SMIL20/CR/MultiArcTiming",
    "http://www.w3.org/2000/SMIL20/CR/MultiWindowLayout",
    "http://www.w3.org/2000/SMIL20/CR/ObjectLinking",
    "http://www.w3.org/2000/SMIL20/CR/PrefetchControl",
    "http://www.w3.org/2000/SMIL20/CR/PrevTiming",
    "http://www.w3.org/2000/SMIL20/CR/RepeatTiming",
    "http://www.w3.org/2000/SMIL20/CR/RepeatValueTiming",
    "http://www.w3.org/2000/SMIL20/CR/RestartDefault",
    "http://www.w3.org/2000/SMIL20/CR/RestartTiming",
    "http://www.w3.org/2000/SMIL20/CR/SkipContentControl",
#if defined(HANDLE_SMIL_2_0_SPLINEANIMATION_MODULE)
    "http://www.w3.org/2000/SMIL20/CR/SplineAnimation",
#else
    "",
#endif
    "http://www.w3.org/2000/SMIL20/CR/Structure",
    "http://www.w3.org/2000/SMIL20/CR/SyncbaseTiming",
    "http://www.w3.org/2000/SMIL20/CR/SyncBehavior",
    "http://www.w3.org/2000/SMIL20/CR/SyncBehaviorDefault",
#if defined(HANDLE_SMIL_2_0_SYNCMASTER_MODULE)
    "http://www.w3.org/2000/SMIL20/CR/SyncMaster",
#else
    "",
#endif
    "http://www.w3.org/2000/SMIL20/CR/TimeContainerAttributes",
#if defined(HANDLE_SMIL_2_0_TIME_MANIPULATIONS_MODULE)
    "http://www.w3.org/2000/SMIL20/CR/TimeManipulations",
#else
    "",
#endif
    "http://www.w3.org/2000/SMIL20/CR/TransitionModifiers",
    "http://www.w3.org/2000/SMIL20/CR/WallclockTiming",
    "http://www.3gpp.org/SMIL20/PSS4/",
    "http://www.3gpp.org/SMIL20/PSS5/",
    NULL
};

static const struct smilTagTable
{
    SMILNodeTag m_tag;
    const char* m_name;
} SmilTagTable[] =
{
    {SMILSmil,              "smil"},
    {SMILMeta,              "meta"},
    {SMILMetadata,          "metadata"},
    {SMILHead,              "head"},
    {SMILBody,              "body"},
    {SMILBasicLayout,       "layout"},
    {SMILRootLayout,        "root-layout"},
    {SMILRegion,      "region"},
    {SMILRegPoint,          "regPoint"},
    {SMILViewport,          "topLayout"},
    {SMILTransition,        "transition"},
    {SMILSwitch,      "switch"},
    {SMILText,              "text"},
    {SMILImg,               "img"},
    {SMILRef,               "ref"},
    {SMILAudio,             "audio"},
    {SMILVideo,             "video"},
    {SMILAnimation,         "animation"},
    {SMILTextstream,        "textstream"},
    {SMILBrush,             "brush"},
    {SMILPrefetch,          "prefetch"},
    {SMILAnchor,      "anchor"},
    {SMILArea,              "area"},
    {SMILAAnchor,     "a"},
    {SMILPar,               "par"},
    {SMILSeq,               "seq"},
    {SMILExcl,              "excl"},
    {SMILPriorityClass,     "priorityClass"},
    {SMILCustomAttributes,  "customAttributes"},
    {SMILCustomTest,        "customTest"},
    {SMILRNRendererList,    RN_TAG_RENDERER_LIST},
    {SMILRendererPreFetch,  RN_TAG_RENDERER},
    {SMILAnimate,           "animate"},
    {SMILSet,               "set"},
    {SMILAnimateMotion,     "animateMotion"},
    {SMILAnimateColor,      "animateColor"},
    {SMILUnknown,     "unknown"}
};

SMILNamespace::SMILNamespace(SMILNamespace* pNS)
{
    if (pNS)
    {
        m_name   = new_string(pNS->m_name);
        m_pValue = pNS->m_pValue;
        if (m_pValue)
        {
            m_pValue->AddRef();
        }
    }
}

SMILNamespace::SMILNamespace(const char* name, IHXBuffer* pVal)
{
    m_name   = new_string(name);
    m_pValue = pVal;
    if (m_pValue)
    {
        m_pValue->AddRef();
    }
}

SMILNamespace::~SMILNamespace()
{
    HX_VECTOR_DELETE(m_name);
    HX_RELEASE(m_pValue);
}


CSmilParser::CSmilParser(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_pCustomTestMap(NULL),
    m_bNoNamespaces(FALSE),
    m_bRNNamespace(FALSE),
    // XXXJHUG -- shouldn't we always ignor?
    m_bIgnoreUnrecognizedElements(TRUE),
    m_bSMILRootLayoutAlreadyFound(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_lLastCheckPendingTime(-1),
    m_ulBandwidthPreference(0),
    m_ulScreenHeightPreference(0),
    m_ulScreenWidthPreference(0),
    m_ulScreenDepthPreference(0),
    m_pLanguagePreferenceList(0),
    m_bCaptionsPreference(FALSE),
    m_pOverdubOrCaptionPreference(0),
    m_bSystemAudioDescPreference(FALSE),
    m_bUseSystemCPU(TRUE), // /If regkey not present, treat as "TRUE"
    m_bUseSystemOS(TRUE), // /If regkey not present, treat as "TRUE"
    m_pBasePath(0),
    m_pTagAttributeMap(0),
    m_bContainsSource(FALSE),
    m_bContainsInitiallyScheduledTrack(FALSE),
    m_ulDurIfNoInitialTracksScheduled(DEFAULT_DUR_IF_NO_SOURCES_SCHEDULED),
    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_pBeginTimeSyncList(NULL)
    , m_pEndTimeSyncList(NULL)
    , m_pBeginEventList(NULL)
    , m_pEndEventList(NULL)
    , m_pBeginMediaMarkerList(NULL)
    , m_pEndMediaMarkerList(NULL)
    , m_pClipBeginMarkerList(NULL)
    , m_pClipEndMarkerList(NULL)
    , m_pPendingBeginTimeList(NULL)
    , m_pPendingEndTimeList(NULL)
    , m_pOnLoadURLList(NULL)
    , m_pOnLoadURLListCopyForPostSeek(NULL)
    , m_bHandlePostSeekOnLoadURLs(FALSE)
#if defined(HELIX_FEATURE_SMIL2_TRANSITIONS)
    , m_pTransitionMap(NULL)
#endif /* #if defined(HELIX_FEATURE_SMIL2_TRANSITIONS) */
    , m_bFirstPacket(TRUE)
    , m_pDefaultNamespace(NULL)
    , m_ulPlatformVer(HX_PLATFORM_UNKNOWN)
    , m_ulPersistentComponentID(0)
    , m_ulPersistentComponentDelay(0)
    , m_ulPersistentComponentDuration(0)
    , m_bAllowPlaylistBehavior(FALSE)
    , m_elementWithinTag(WithinUnknown)
    , m_pElementMap(NULL)
    , m_pAttributeMap(NULL)
#if defined(HELIX_FEATURE_SMIL2_VALIDATION)
    , m_pExtElementMap(NULL)
    , m_pExtAttributeMap(NULL)
    , m_pNamespaceMap(NULL)
    , m_pLegalAttrMap(NULL)
    , m_pContentModelMap(NULL)
    , m_pAttrType(NULL)
    , m_ppEnumAttrMap(NULL)
    , m_ppReqAttrList(NULL)
#endif /* #if defined(HELIX_FEATURE_SMIL2_VALIDATION) */
    , m_pValNSList(NULL)
    , m_pXMMFElementList(NULL)
    , m_lParseError(HXR_OK)
    , m_pAnimateElementList(NULL)
    , m_pBeginTimeMap(NULL)
    , m_pEndTimeMap(NULL)
    , m_pExternalEventList(NULL)
    , m_pExternalEventListPos(NULL)
    , m_bAllTracksNeedReflushHint(FALSE)
    , m_pElementsWithHandlerList(NULL)
    , m_pVarName(NULL)
    , m_ulNextVar(1024)
    , m_pTimelineElementManager(NULL)
{
    if(m_pContext)
    {
      m_pContext->AddRef();
      m_pContext->QueryInterface(IID_IHXCommonClassFactory, (void**)&m_pClassFactory);
    }

    initRequireTags();
    initTagAttributes();
    getPreferences();
    initParsingMaps();

    m_versionInfo.dwPlatformId = HX_PLATFORM_UNKNOWN;
    m_versionInfo.dwMachineType = HX_MACHINE_UNKNOWN;
    // /Get the OS and CPU version information:
    m_ulPlatformVer = HXGetWinVer(&m_versionInfo);
    // Allocate the variable name buffer
    m_pVarName = new char [256];
    // Allocate the timeline element manager
    m_pTimelineElementManager = new CSmilTimelineElementManager;
}

CSmilParser::~CSmilParser()
{
    deleteTagAttributes();
    HX_DELETE(m_pRequireTagsMap);
    HX_DELETE(m_pCustomTestMap);
    HX_DELETE(m_pElementMap);
    HX_DELETE(m_pAttributeMap);
#if defined(HELIX_FEATURE_SMIL2_VALIDATION)
    HX_DELETE(m_pExtElementMap);
    HX_DELETE(m_pExtAttributeMap);
    HX_DELETE(m_pNamespaceMap);
    HX_DELETE(m_pLegalAttrMap);
    HX_DELETE(m_pContentModelMap);
    HX_VECTOR_DELETE(m_pAttrType);
    deleteEnumAttrMaps();
    deleteReqAttrLists();
#endif /* #if defined(HELIX_FEATURE_SMIL2_VALIDATION) */
    HX_DELETE(m_pAnimateElementList);
    deleteValidationNamespaceList();

    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)
      {
          SMILNamespace* pNS = (SMILNamespace*)(*ndx);
          HX_DELETE(pNS);
      }
      HX_DELETE(m_pNSConflictList);
    }

#if defined(HELIX_FEATURE_SMIL2_TRANSITIONS)
    HX_DELETE(m_pTransitionMap);
#endif /* #if defined(HELIX_FEATURE_SMIL2_TRANSITIONS) */

    // /XXXEH- TODO: check mem leaks; should be clean already...
    HX_DELETE(m_pBeginTimeSyncList);
    HX_DELETE(m_pEndTimeSyncList);

    // /XXXEH- TODO: check mem leaks; should be clean already...
    HX_DELETE(m_pBeginEventList);
    HX_DELETE(m_pEndEventList);

    // /XXXEH- TODO: check mem leaks; should be clean already...
    HX_DELETE(m_pBeginMediaMarkerList);
    HX_DELETE(m_pEndMediaMarkerList);
    HX_DELETE(m_pXMMFElementList);
    HX_DELETE(m_pClipBeginMarkerList);
    HX_DELETE(m_pClipEndMarkerList);

    // /XXXEH- TODO: check mem leaks; should be clean already...
    HX_DELETE(m_pPendingBeginTimeList);
    HX_DELETE(m_pPendingEndTimeList);
    HX_DELETE(m_pOnLoadURLList);
    HX_DELETE(m_pOnLoadURLListCopyForPostSeek);

    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
CSmilParser::initRequireTags()
{
    //XXXBAB - add required tags here
#if 0
    m_pRequireTagsMap = new CHXMapStringToOb;
    (*m_pRequireTagsMap)["foo-require"] = 0;
    (*m_pRequireTagsMap)["boo-require"] = 0;
#endif

}


void
CSmilParser::GetSystemScreenInfo(REF(UINT32) rulScreenHeight,
                         REF(UINT32) rulScreenWidth,
                         REF(UINT32) rulScreenBitDepth)
{
    rulScreenHeight = rulScreenWidth = rulScreenBitDepth = 0;
#if defined(_WINDOWS)
    rulScreenHeight = (UINT32)GetSystemMetrics(SM_CYSCREEN);
    rulScreenWidth = (UINT32)GetSystemMetrics(SM_CXSCREEN);
    HDC hDCMain = GetDC(NULL); // /Get screen DC.
    if (hDCMain)
    {
      rulScreenBitDepth = (UINT32)GetDeviceCaps(hDCMain, BITSPIXEL);
      ReleaseDC(NULL, hDCMain);
    }
#elif defined(_UNIX) && (!(defined(_BEOS))) && (!(defined(_MAC_UNIX)))
    // /Pass NULL string to XOpenDisplay to get default display, which
    // is the one that we're playing to:
    Display* pDisplay = XOpenDisplay(NULL);
    if (pDisplay)
    {
      XLockDisplay(pDisplay);
      Screen* pScreen = XDefaultScreenOfDisplay(pDisplay);
      XUnlockDisplay(pDisplay);
      rulScreenHeight = (UINT32)HeightOfScreen(pScreen);
      rulScreenWidth = (UINT32)WidthOfScreen(pScreen);
      rulScreenBitDepth = (UINT32)DefaultDepthOfScreen(pScreen);
    }
#elif defined(_MACINTOSH) || defined(_MAC_UNIX)
    // /XXXEH- note: on a Mac, you can have multiple display devices and our
    // player can actually play to more than one at once, and can be dragged
    // from one to the other while running.  Dynamic re-evaluation is needed.
    GDHandle mainGD = ::GetMainDevice();
    rulScreenHeight = (UINT32)((**mainGD).gdRect.bottom - (**mainGD).gdRect.top);
    rulScreenWidth = (UINT32)((**mainGD).gdRect.right - (**mainGD).gdRect.left);
    PixMapHandle pmh = (**mainGD).gdPMap;
    if (pmh)
    {
      rulScreenBitDepth = (UINT32)((**pmh).pixelSize);
    }
#elif defined(_SYMBIAN)
    TInt aValue;
    TReal aTrg1;
    TReal aTrg2;
    TInt log1;
    TInt log2;
    HAL::Get(HALData::EDisplayColors,aValue);
    TScreenInfoV01 screenInfo;
    TPckg<TScreenInfoV01> si(screenInfo);
    UserSvr::ScreenInfo(si);
    rulScreenWidth = screenInfo.iScreenSize.iWidth;
    rulScreenHeight = screenInfo.iScreenSize.iHeight;
    log1 = Math::Ln(aTrg1,aValue);
    log2 = Math::Ln(aTrg2,2);
    rulScreenBitDepth = (TUint32)(aTrg1/aTrg2);
#else

    HX_ASSERT(0  &&  "Contact ehodge: need screen info from this OS");
#endif
}

void
CSmilParser::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)
          {
            // /Fixes TLC-set part of PR 58151: sometimes this list will
            // have tokens separated by ", " instead of just ",", so first
            // remove all whitespace chars after the ',' (and, if we run
            // into a whitespace-only string, go past the next comma):
            while (isspace(*pTok)  ||  ',' == *pTok)
            {
                *pTok++;
            }
            if ('\0' == *pTok)
            {
                break; // /Whitespace-only or emtpy string is not valid.
            }

            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 2.0 version): on Mac, 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);
      }

      UINT32 rulScreenHeight = 0;
      UINT32 rulScreenWidth = 0;
      UINT32 rulScreenBitDepth = 0;
      // /Each value found here will be used if the player prefs does not
      // already contain it.  For PR 58072 and PR 58075:
      GetSystemScreenInfo(rulScreenHeight, rulScreenWidth, rulScreenBitDepth);

      if(HXR_OK == pPrefs->ReadPref("screen_depth", pBuf))
      {
          m_ulScreenDepthPreference =
            (UINT32)atol((const char*)pBuf->GetBuffer());
          HX_RELEASE(pBuf);
      }
      // /Fixes PR 58075: if not in the player registry, then use the
      // screen (bit) depth obtained from the OS API call:
      else
      {
          m_ulScreenDepthPreference = rulScreenBitDepth;
      }

      if(HXR_OK == pPrefs->ReadPref("screen_height", pBuf))
      {
          m_ulScreenHeightPreference =
            (UINT32)atol((const char*)pBuf->GetBuffer());
          HX_RELEASE(pBuf);
      }
      // /Fixes height part of PR 58072: if not in the player registry, then
      // use the screen height obtained from the OS API call:
      else
      {
          m_ulScreenHeightPreference = rulScreenHeight;
      }

      if(HXR_OK == pPrefs->ReadPref("screen_width", pBuf))
      {
          m_ulScreenWidthPreference =
            (UINT32)atol((const char*)pBuf->GetBuffer());
          HX_RELEASE(pBuf);
      }
      // /Fixes width part of PR 58072: if not in the player registry, then
      // use the screen width obtained from the OS API call:
      else
      {
          m_ulScreenWidthPreference = rulScreenWidth;
      }

      if(HXR_OK == pPrefs->ReadPref("caption_switch", pBuf))
      {
          m_bCaptionsPreference =
            (BOOL)((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);
// /XXXEH- TLC needs to allow BOTH captions and audio descriptions
// so they'll need a "overdub_or_caption" option (choice) AND
// a separate "audio_desc_switch" like "caption_switch", above.
#define XXXEH_NEED_TLC_TO_HAVE_SEPARATE_audio_desc_AND_caption_PREFS
#if defined(XXXEH_NEED_TLC_TO_HAVE_SEPARATE_audio_desc_AND_caption_PREFS)
          // /In our player (RP8), "caption_switch" is what gets set to
          // TRUE when *ALL* accessibility features are enabled.  So,
          // it's possible to have accessibility features disabled but
          // to have overdub_or_captions set to one or the other; we need
          // to look at the caption_switch pref to see if we should enable
          // audio descriptions:
          if (m_bCaptionsPreference)
          {
            m_bSystemAudioDescPreference = !strcmp(pStr, "overdub");
            if (m_bSystemAudioDescPreference)
            {
                m_bCaptionsPreference = FALSE;
            }
          }
#endif
          HX_RELEASE(pBuf);
      }
      if(HXR_OK == pPrefs->ReadPref("systemAudioDesc", pBuf))
      {
          m_bSystemAudioDescPreference =
                (BOOL)((UINT32)atol((const char*)pBuf->GetBuffer()));
          HX_RELEASE(pBuf);
      }
      // /Fixes PR 64428:
      if(HXR_OK == pPrefs->ReadPref("UseSystemCPU", pBuf))
      {
          m_bUseSystemCPU =
                (BOOL)((UINT32)atol((const char*)pBuf->GetBuffer()));
          HX_RELEASE(pBuf);
      }
      // /Fixes PR 64428:
      if(HXR_OK == pPrefs->ReadPref("UseSystemOS", pBuf))
      {
          m_bUseSystemOS =
                (BOOL)((UINT32)atol((const char*)pBuf->GetBuffer()));
          HX_RELEASE(pBuf);
      }

      HX_RELEASE(pPrefs);
    }

    HX_RELEASE(pRegistry);
}


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

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

    if(m_pIDMap)
    {
      CHXMapStringToOb::Iterator i = m_pIDMap->Begin();
      for(; i != m_pIDMap->End(); ++i)
      {
          SMILNode* pNode = (SMILNode*)(*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)
      {
          CSmilAddGroup* pAddGroup = (CSmilAddGroup*)(*i);
          delete pAddGroup;
      }
      HX_DELETE(m_pAddGroupMap);
    }

    if(m_pSourceUpdateList)
    {
      CHXSimpleList::Iterator i = m_pSourceUpdateList->Begin();
      for(; i != m_pSourceUpdateList->End(); ++i)
      {
          CSmilSourceUpdate* pUpdate = (CSmilSourceUpdate*)(*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)
      {
          SMILNamespace* pNS = (SMILNamespace*)(*ndx);
          HX_DELETE(pNS);
      }
      HX_DELETE(m_pNSConflictList);
    }

#if defined(HELIX_FEATURE_SMIL2_TRANSITIONS)
    HX_DELETE(m_pTransitionMap);
#endif /* #if defined(HELIX_FEATURE_SMIL2_TRANSITIONS) */

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

    // Clear the begin time map
    clearTimeValueMap(SmilBeginTimeList);
    // Clear the end time map
    clearTimeValueMap(SmilEndTimeList);
    // Clear and delete the custom event list
    clearExternalEventList();
    HX_DELETE(m_pExternalEventList);
    // Clear the handler list
    HX_DELETE(m_pElementsWithHandlerList);
}

HX_RESULT
CSmilParser::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;
    }

    SMILNode* pRootNode = new SMILNode;
    pRootNode->m_id = "root";
    pRootNode->m_name = "root";
    m_pNodeList = new SMILNodeList;
    pRootNode->m_pNodeList = m_pNodeList;
    m_pNodeList->m_pParentNode = pRootNode;
    m_pNodeListStack->Push(pRootNode);
#if !defined(USE_EXPAT_FOR_SMIL)
//#define USE_EXPAT_FOR_SMIL
#endif
#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;
      }
    }
    if (SUCCEEDED(rc))
    {
      m_pResponse = new CSmilParserResponse(this);
      m_pResponse->AddRef();
      // Expat is created off the CCF.
      // In strict mode it requires 100% compliant XML.
      rc = m_pParser->Init(m_pResponse, "iso-8859-1", TRUE);
    }

#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 CSmilParserResponse(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
CSmilParser::parse(IHXBuffer* pBuffer, BOOL bIsFinal)
{
    HX_RESULT rc = HXR_OK;

    if (m_bFirstPacket)
    {
      m_bFirstPacket = FALSE;
      time(&m_tRefTime);
    }


    rc = m_pParser->Parse(pBuffer, bIsFinal);
    if (SUCCEEDED(rc) && FAILED(m_lParseError))
    {
        rc = m_lParseError;
    }
    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
CSmilParser::durationResolved(const char* pID, UINT32 ulDuration,
                        // /Defaults to FALSE:
                        BOOL bSetByParent,
                        BOOL bDurationExtendingDueToPause)
{
    HX_RESULT rc = HXR_OK;

    SMILNode*     pNode = NULL;

    if(m_pIDMap->Lookup(pID, (void*&)pNode))
    {
      if (pNode->m_pElement->m_bIndefiniteDuration)
      {
#if defined(XXXEH_OLD_SPECIAL_CASE_HANDLING_OF_INDEF_DUR)
          goto cleanup;
#endif
      }

      // add duration to parent element
      if(pNode &&
         pNode->m_pElement &&
         pNode->m_pElement->m_pTimelineElement)
      {
          pNode->m_pElement->m_pTimelineElement->setDuration(ulDuration,
                bSetByParent, bDurationExtendingDueToPause);
      }
    }

#if defined(XXXEH_OLD_SPECIAL_CASE_HANDLING_OF_INDEF_DUR)
cleanup:
#endif

    return rc;
}

BOOL AncestorEventsAreResolved(SMILNode* pNode, SMILNode* pOriginalChildNode)
{
    if (!pNode  ||  !pNode->m_pElement  ||
          !pNode->m_pParent  ||  pNode->m_tag == SMILBody)
    {
      return TRUE;
    }
    // /For any parent that's not a timeline element, e.g., priorityClass,
    // just ignore and return its parent's results:
    else if (!pNode->m_pElement->m_pTimelineElement)
    {
      return AncestorEventsAreResolved(pNode->m_pParent, pOriginalChildNode);
    }
    //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()  &&
      // /Fixes 1/2 of PR 50535: don't care about duration event for child
      // itself; it's OK to add a timeline element that has an unresolved
      // end; (but if parent has unresolved end, we may need to wait):
      pNode != pOriginalChildNode)  ||
      (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, pOriginalChildNode);
}


HX_RESULT
CSmilParser::adjustForNegativeOffset(const char* pID)
{
    HX_RESULT pnresult = HXR_OK;
    SMILNode* pNode = 0;
    if(!m_pIDMap->Lookup(pID, (void*&)pNode))
    {
      pnresult = HXR_FAILED;
    }
    else if (pNode  &&  pNode->m_pElement  &&
          !pNode->m_pElement->m_bInsertedIntoTimeline  &&
          pNode->m_pParent  &&  pNode->m_pParent->m_pElement)
    {
      // /Use syncBase, not parent here (in case parent is <a>, <switch>,
      // or <priorityClass> element); this fixes bug (PR 56233 repro case)
      // where clipBegin was calculted wrong, below, because priorityClass
      // was being used as syncbase and it had a begin time later than this:
      SMILNode* pSyncBaseNode = getSyncAncestor(pNode);
      if (!pSyncBaseNode  ||  !pSyncBaseNode->m_pElement)
      {
          pnresult = HXR_FAILED;
          goto cleanup;
      }

      // /If we have a negative begin offset or delay which starts us
      // earlier than the begin offset or delay of our syncbase, we need
      // to do a clip-begin equal to the difference, and we need to
      // update our duration as well:
      LONG32 lParentBegin =
            // /If delay is valid, use it otherwise use valid beginOffset
            pSyncBaseNode->m_pElement->m_ulDelay != ((UINT32)-1) ?
            (LONG32)pSyncBaseNode->m_pElement->m_ulDelay :
            pSyncBaseNode->m_pElement->m_bBeginOffsetSet?
            pSyncBaseNode->m_pElement->m_lBeginOffset : 0;
      LONG32 lCurElementBegin =
            pNode->m_pElement->m_ulDelay != ((UINT32)-1) ?
            (LONG32)pNode->m_pElement->m_ulDelay :
            pNode->m_pElement->m_bBeginOffsetSet?
            pNode->m_pElement->m_lBeginOffset :
            // /In adjustForNegativeOffset():
            // /Fixes case where parent has explicit dur, explicit
            // begin offset, and pNode's element begins on an event;
            // this used to set lCurElementBegin to 0 and erroneous
            // clip-begin equal to parent's begin offset would ensue:
            MAX_LONG32;

      // /First, we need to see if this is happening during playback; if
      // so, our begin may have resolved to a time in the past that is
      // later than our sync-parent's begin; in that case, we need to
      // adjust the clip-begin and delay to account for the diff between
      // now and lCurElementBegin:
      if (m_lLastCheckPendingTime > (lCurElementBegin +
            // /(re)Fixes broken long-sync-arc test file
            // BUG-20001110_BeginOffsetInPar...BeginningOnClickAllowsSync.smi
            // where the clipBegin was being *re*-computed here, after
            // having already been adjusted in setDelay() for a long-sync-
            // arc negative offset.  We shouldn't do any adjustment here
            // if the difference is very small (which is not due to
            // user or authored delay clipping, but is due to processor
            // time between when our parent resolved and when the last
            // check pending time):
            CHECKPENDING_TIME_VS_CURTIME_FUDGE_FACTOR))
      {
          lParentBegin = m_lLastCheckPendingTime;
      }

      if (lCurElementBegin < lParentBegin)
      {
          LONG32 lDiff = lParentBegin - lCurElementBegin;
          HX_ASSERT(lDiff >= 0);
          if (lDiff > 0)
          {
            ULONG32 ulPriorPureDuration = pNode->m_pElement->getPureDuration();
            ULONG32 ulDiff = (ULONG32)lDiff;
            // /If clip-begin is invalid, set it otherwise add to it:
            pNode->m_pElement->m_ulClipBegin = ((UINT32)-1 ==
                  pNode->m_pElement->m_ulAuthoredClipBegin? ulDiff :
                  ulDiff+pNode->m_pElement->m_ulAuthoredClipBegin);

            if ((UINT32)-1 != pNode->m_pElement->m_ulDuration)
            {
                if (pNode->m_pElement->m_ulDuration > ulDiff)
                {
                  pNode->m_pElement->m_ulDuration -= ulDiff;
                }
                // /else duration is negative; it can't ever play:
                else
                {
                  pNode->m_pElement->m_ulDuration = 0;
                }
            }

            // /And, we should now begin when our parent does:
            pNode->m_pElement->m_ulDelay = lParentBegin;
            // /Only reset duration if it's not 0xFFFFFFFF and
            // if it's not the same as it was before:
            if ((UINT32)-1 != pNode->m_pElement->m_ulDuration  &&
                  ulPriorPureDuration != pNode->m_pElement->getPureDuration())
            {
                resetTimelineElementDuration(pID,
                      pNode->m_pElement->getPureDuration(),
                      ulPriorPureDuration);
            }
                if (m_pTimelineElementManager) m_pTimelineElementManager->notify(pID);
          }
      }
    }

cleanup:
    return pnresult;
}


void
CSmilParser::insertTimelineElement(const char* pID, UINT32 ulDelay)
{
#if defined(_DEBUG)  &&  defined(XXXEH_DEBUGOUT_ADDDURATION)
{
    FILE* f1 = ::fopen("c:\\smil2AddDuration.txt", "a+");
    ::fprintf(f1, "CSmilParser::insertTimelineElement(%s, delay=%lu) _-_-_-_-_-_-_-_-_\n",
          (const char*)pID, ulDelay);
    ::fclose(f1);
}
#endif

    SMILNode* pNode = 0;
    if(m_pIDMap->Lookup(pID, (void*&)pNode))
    {
      if(pNode &&
          pNode->m_pElement &&
          // /XXXEH- TODO: we need to handle restart!="never" which
          // means that the following if condition should take this
          // into account:
          !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 is dependent on another (not-yet-
          // resolved) element:
          ((!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, pNode)
          // /For SMIL 2.0, we can have begin="indefinite" which should
          // be treated as un unresolved event (but only if that's the
          // sole begin time specified; begin="indefinite; 5s" should be
          // treated as begin="5s"):
          &&  !pNode->m_pElement->m_bIndefiniteBegin
          )
      {
          ULONG32 ulDelayBeyondParentDelay = 0;
          ULONG32 ulSyncBaseDelay = 0;
          SMILNode* pSyncAncestor = getSyncAncestor(pNode);
          HX_ASSERT((UINT32)-1 != pSyncAncestor->m_pElement->m_ulDelay);

          // /Check hard time boundaries set by parent:
          if  (pSyncAncestor  && pSyncAncestor->m_pElement  &&
                (UINT32)-1 != pSyncAncestor->m_pElement->m_ulDelay)
          {
            ulSyncBaseDelay = pSyncAncestor->m_pElement->m_ulDelay;
            ulDelayBeyondParentDelay = ulDelay - ulSyncBaseDelay;
            HX_ASSERT(ulDelay >= ulSyncBaseDelay);
            if (ulDelay < ulSyncBaseDelay)
            {
                ulDelayBeyondParentDelay = 0;
            }
            // /If child has delay beyond parent's delay+duration, i.e.,
            // begin resolved on an event or sync arc, then don't insert it:
            else
            {
                if (pSyncAncestor->m_pElement->m_bHasExplicitDur  &&
                      !pSyncAncestor->m_pElement->m_bIndefiniteDuration  &&
                      !pSyncAncestor->m_pElement->m_bIndefiniteEnd  &&
                      (ulDelayBeyondParentDelay >=
                      pSyncAncestor->m_pElement->m_ulDuration) )
                {
                  goto cleanup; //It can't ever play.
                }
                else if (pSyncAncestor->m_pElement->m_bEndOffsetSet  &&
                      !pSyncAncestor->m_pElement->m_bIndefiniteDuration  &&
                      !pSyncAncestor->m_pElement->m_bIndefiniteEnd)
                {
                  if (ulDelayBeyondParentDelay >=
                        pSyncAncestor->m_pElement->m_ulDuration)
                  {
                      goto cleanup; //It can't ever play.
                  }
                }
            }
          }
          else
          {
            // /Fixes PR 50411: long sync-arc begin resolved before parent
            // begin resolved and is thus not adjusted for any offset
            // from parent begin; dur:=0 if it ends before parent begin:
            if (pSyncAncestor)
            {
                pNode->m_pElement->m_bAwaitingSyncAncestorBeginNotification=
                      TRUE;
            }
            goto cleanup; // /Wait to insert this until parent is resolved.
          }

#define XXXEH_ABOVE_SHOULD_HANDLE_THIS__REMOVE_IF_NO_ASSERT0_BY_20010530
#if defined(XXXEH_ABOVE_SHOULD_HANDLE_THIS__REMOVE_IF_NO_ASSERT0_BY_20010530)
          // /[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_bBeginOffsetSet &&
                pNode->m_pParent->m_pElement->m_bEndOffsetSet &&
                (pNode->m_pElement->m_lBeginOffset >
                pNode->m_pParent->m_pElement->m_lEndOffset) )
          {
            // /See if above replaces this code; if no assert reported by
            // 2001-05-20, then I'm going to remove this whole if() block:
            HX_ASSERT(0  &&  "mailto: ehodge@real.com");
            goto cleanup; //Don't insert this because it can't ever play.
          }
          // /Also, need to check parent *dur* and make sure we don't
          // exceed it if it's been explicitly set to a finite amount:
          if ( pNode->m_pParent  &&  pNode->m_pParent->m_pElement  &&
                pNode->m_pElement->m_bBeginOffsetSet  &&
                pNode->m_pParent->m_pElement->m_bHasExplicitDur  &&
                !pNode->m_pParent->m_pElement->m_bIndefiniteDuration  &&
                (pNode->m_pElement->m_lBeginOffset > 0  &&
                (UINT32)pNode->m_pElement->m_lBeginOffset >
                pNode->m_pParent->m_pElement->m_ulDuration) )
          {
            // /See if above replaces this code; if no assert reported by
            // 2001-05-20, then I'm going to remove this whole if() block:
            HX_ASSERT(0  &&  "mailto: ehodge@real.com");
            goto cleanup; //Don't insert this because it can't ever play.
          }
#endif

            // If this element uses media markers for clipBegin
            // then those markers need to be resolved
            if (pNode->m_pElement->m_bClipBeginUsesMarker &&
                !pNode->m_pElement->m_bClipBeginMarkerResolved)
            {
                // Set the flag saying we WOULD have scheduled
                // this element except the clip begin marker wasn't
                // resolved yet.
                pNode->m_pElement->m_bWaitingOnClipBeginToResolve = TRUE;
                goto cleanup; // don't insert cause we don't know the clipBegin yet
            }

            // If this element uses media markers for clipEnd
            // then those markers need to be resolved
            if (pNode->m_pElement->m_bClipEndUsesMarker &&
                !pNode->m_pElement->m_bClipEndMarkerResolved)
            {
                // Set the flag saying we WOULD have scheduled
                // this element except the clip end marker wasn't
                // resolved yet.
                pNode->m_pElement->m_bWaitingOnClipEndToResolve = TRUE;
                goto cleanup; // don't insert cause we don't know the clipEnd yet
            }

          // skip the element if its duration == 0
          if (0 == pNode->m_pElement->m_ulDuration)
          {
            durationResolved(pNode->m_id, 0);
          }
          else
          {
                // XXXMEH - we need to know about animations that
                // involve sources before we know about the sources.
                // Therefore, we need to ensure that the animate
                // elements are put before the source elements.
                UINT32 ulTS = INITIAL_STREAM1_TIMESTAMP;
                if (pNode->m_tag == SMILAnimate      ||
                    pNode->m_tag == SMILSet          ||
                    pNode->m_tag == SMILAnimateColor ||
                    pNode->m_tag == SMILAnimateMotion)
                {
                    ulTS = INITIAL_STREAM0_TIMESTAMP;
                }
            pNode->m_pElement->m_ulDelay               = ulDelay;
            pNode->m_pElement->m_ulTimestamp           = ulTS;
            pNode->m_pElement->m_bInsertedIntoTimeline = TRUE;
            pNode->m_pElement->m_bHasBeenScheduled = TRUE;

            // /This helps fix problem where time container has multiple
            // begin conditions and, when second begin was being
            // processed, wasn't being seen as having started before so
            // prepForRestart() wasn't being called when it should.
            // Allow more than one level, i.e., recurse up the tree to
            // set this for all ancestor time containers:
            SMILNode* pNextHigherSyncAncestor = pSyncAncestor;
            while (pNextHigherSyncAncestor  &&
                  pNextHigherSyncAncestor->m_pElement)
            {
                pNextHigherSyncAncestor->m_pElement->m_bInsertedIntoTimeline = TRUE;
                pNextHigherSyncAncestor->m_pElement->m_bHasBeenScheduled = TRUE;
                pNextHigherSyncAncestor = getSyncAncestor(
                      pNextHigherSyncAncestor);
            }

            insertElementByTimestamp(pNode->m_pElement);
          }
      }
    }
cleanup:
    return;
}

HX_RESULT
CSmilParser::trackRemoved(const char* pID,
                    UINT32 ulDuration)
{
    HX_RESULT     hr = HXR_OK;
    SMILNode*     pNode = NULL;
    IHXBuffer* pBuf = NULL;

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

    return hr;
}

void
CSmilParser::resetTimelineElementDuration(const char* pID,
      // /These durations must NOT include delay (for PR 79699 et al):
      UINT32 ulPureDuration,
      UINT32 ulPriorPureDuration)
{
#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG_RESETTIMELINEELEMENTDUR_AND_DELAY)
{
    FILE* f1 = ::fopen("c:\\smil2ResetTimelineDurOrDelay.txt", bFirstResetTimelineElementDuration?"w":"a+");
    bFirstResetTimelineElementDuration = FALSE;
    ::fprintf(f1, "Element id = %s;\t%sresetting (pure) duration to %lu from %lu\n",
          pID, ulPureDuration==ulPriorPureDuration?
#if XXXEH_TEST_THIS_MORE
          "Skipping ":"",
#else
          "":"",
#endif
          ulPureDuration, ulPriorPureDuration);
    ::fclose(f1);

    f1 = ::fopen("c:\\smil2AddDuration.txt", "a+");
    ::fprintf(f1, "\nElement id = %s;\t%sresetting duration to %lu from %lu\n\n",
          pID, ulPureDuration==ulPriorPureDuration?
#if XXXEH_TEST_THIS_MORE
          "Skipping ":"",
#else
          "":"",
#endif
          ulPureDuration, ulPriorPureDuration);
    ::fclose(f1);
}
#endif

    SMILNode* pNode = NULL;
    // /Fixes bug exposed by fixes for PR 59584 and others (by undoing the
    // original fix for PR 61174(version1) and par version of PR 56686).
    // Don't reset duration if it's the same, otherwise SourceUpdate will be
    // created possibly prior to TrackDurationSet() even being called, which
    // would cause this delay event to be used to possibly mess things up in
    // rendererInitialized():
#if defined(XXXEH_TEST_THIS_MORE)
    // /TEST THIS! A test file fails due to this when both are same,
    // "..relatedToPR59584andPR62688(endsync1stWithRestartVersion..smil"
    if (ulPureDuration == ulPriorPureDuration)
    {
      goto cleanup;
    }
#endif
    if(m_pIDMap->Lookup(pID, (void*&)pNode))
    {
      CSmilSourceUpdate* pUpdate = new CSmilSourceUpdate;
      pUpdate->m_ulTimestamp = INITIAL_STREAM1_TIMESTAMP;
      pUpdate->m_srcID = pID;
      pUpdate->m_updateTag = UpdateDuration;
      pUpdate->m_ulUpdatedDuration = ulPureDuration;
      pUpdate->m_bDurationIsPureOfDelay = TRUE;
      if(!m_pSourceUpdateList)
      {
          m_pSourceUpdateList = new CHXSimpleList;
      }
      m_pSourceUpdateList->AddTail(pUpdate);
      insertElementByTimestamp(pUpdate);
    }
#if defined(XXXEH_TEST_THIS_MORE)
cleanup:
#endif
    return;
}

void
CSmilParser::resetTimelineElementDelay(const char* pID,
                               UINT32 ulDelay,
                               UINT32 ulPriorDelay)
{
#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG_RESETTIMELINEELEMENTDUR_AND_DELAY)
{
    FILE* f1 = ::fopen("c:\\smil2ResetTimelineDurOrDelay.txt", bFirstResetTimelineElementDuration?"w":"a+");
    bFirstResetTimelineElementDuration = FALSE;
    ::fprintf(f1, "Element id = %s;\t%sresetting delay to %lu from %lu\n",
          pID, ulDelay==ulPriorDelay?"Skipping ":"", ulDelay, ulPriorDelay);
    ::fclose(f1);

    f1 = ::fopen("c:\\smil2AddDuration.txt", "a+");
    ::fprintf(f1, "\nElement id = %s;\t%sresetting delay to %lu from %lu\n\n",
          pID, ulDelay==ulPriorDelay?"Skipping ":"", ulDelay, ulPriorDelay);
    ::fclose(f1);
}
#endif

    SMILNode* pNode = NULL;
    // /Fixes bug exposed by fixes for PR 59584 and others (by undoing the
    // original fix for PR 61174(version1) and par version of PR 56686).
    // Don't reset delay if it's the same, otherwise SourceUpdate will be
    // created possibly prior to TrackDurationSet() even being called, which
    // would cause this delay event to be used to possibly mess things up in
    // rendererInitialized():
    if (ulDelay == ulPriorDelay)
    {
      goto cleanup;
    }
    if(m_pIDMap->Lookup(pID, (void*&)pNode))
    {
      CSmilSourceUpdate* pUpdate = new CSmilSourceUpdate;
      pUpdate->m_ulTimestamp = INITIAL_STREAM1_TIMESTAMP;
      pUpdate->m_srcID = pID;
      pUpdate->m_updateTag = UpdateDelay;
      pUpdate->m_ulUpdatedDelay = ulDelay;

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


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

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

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

UINT32
CSmilParser::getFragmentOffset(const char* pFragment,
      //This BOOL will be set to FALSE if the fragment
      // does not exist; this was necessary to fix PR 22655.
      BOOL& bFragFoundAndResolved,
      BOOL bResolveBeginOfFragmentTarget,
      ULONG32 ulCurTime)
{
    bFragFoundAndResolved = FALSE;
    if(pFragment)
    {
      INT32 lAnchorBegin = 0;
      SMILNode* pNode = NULL;
      CSmilElement* pElement = NULL;
      CSmilElement* pFragmentElement = NULL;
      if(m_pIDMap->Lookup(pFragment, (void*&)pNode) &&
          pNode->m_pElement)
      {
          pElement = pNode->m_pElement;
          if(pNode->m_tag == SMILSwitch ||
            pNode->m_tag == SMILAAnchor)
          {
            SMILNode* pChildNode = getTimelineDescendent(
                pNode, NULL);
            while(pChildNode)
            {
                if(!pChildNode->m_bDelete)
                {
                  pFragmentElement = pChildNode->m_pElement;
                  break;
                }
                pChildNode = getTimelineDescendent(
                  pNode, pChildNode);
            }
          }
          else if(pNode->m_tag == SMILAnchor  ||
                pNode->m_tag == SMILArea)
          {
            if(pElement->m_bBeginOffsetSet)
            {
                lAnchorBegin = pElement->m_lBeginOffset;
            }
            SMILNode* pParent = pNode->m_pParent;
            if(pParent)
            {
                pFragmentElement = pParent->m_pElement;
            }
          }
          else
          {
            pFragmentElement = pElement;
          }

          if(pFragmentElement)
          {
            bFragFoundAndResolved = TRUE;
            //[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(pFragmentElement->m_ulDelay != (UINT32)-1)
            {
                INT32 offset = pFragmentElement->m_ulDelay +
                  lAnchorBegin;

                return (offset >= 0 ? (UINT32)offset: 0);
            }
            else if(pFragmentElement->m_bBeginOffsetSet)
            {
                //Changed this while fixing PR 26464:
                // This used to return pFragmentElement->m_ulDelay +
                // pFragmentElement->m_lBeginOffset + lAnchorBegin 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):
                INT32 offset = pFragmentElement->m_lBeginOffset +
                  lAnchorBegin;

                return (offset >= 0 ? (UINT32)offset: 0);
            }
            // /Fixes SMIL 2.0 Interop Linking tests #1.4 & #1.5:
            // if we've been instructed to resolve this #-ref'd
            // internally-linked element's delay, then we need to
            // do so to the current time:
            else if (bResolveBeginOfFragmentTarget  &&
                  pFragmentElement->m_pTimelineElement)
            {
                SMILNode* pSyncAncestor =
                      getSyncAncestor(pFragmentElement->m_pNode);
                // /Fixes PR 50848:
                // /The following is needed to completely fix SMIL 2.0
                // Interop Linking #1.5 (to make the clicked-on child of
                // a seq go away when the clicked-to one starts):
                if (pSyncAncestor  &&  SMILSeq == pSyncAncestor->m_tag)
                {
                  SMILNode* pSibling = pSyncAncestor->getFirstChild();
                  SMILNode* pPrevSibling = NULL;
                  while (pSibling)
                  {
                      if (pSibling->m_id ==
                            pFragmentElement->m_pNode->m_id)
                      {
                        break;
                      }
                      // /Fixes case where pFragmentElement has non-
                      // timeline-element parent, e.g., <a> or <switch>
                      else if (pFragmentElement->m_pNode->m_pParent !=
                            pSyncAncestor)
                      {
                        SMILNode* pFirstSiblingChild =
                              pSibling->getFirstChild();
                        if (pFirstSiblingChild  &&
                              pFirstSiblingChild->m_id ==
                              pFragmentElement->m_pNode->m_id)
                        {
                            break;
                        }
                      }

                      pPrevSibling = pSibling;
                      pSibling = pSyncAncestor->getNextChild();
                  }
                  if (pPrevSibling)
                  {
                      // /XXXEH- TODO: if prev sib is not a timeline
                      // element, use its first child as prev sibling.
                      if (pPrevSibling->m_pElement  &&
                           pPrevSibling->m_pElement->m_pTimelineElement)
                      {
                        ULONG32 ulCurStartTime = 0;
                        if (HXR_OK == pPrevSibling->m_pElement->
                              getCurrentScheduledStartTime(
                              ulCurStartTime)  &&
                              ulCurTime > ulCurStartTime)
                        {
                            ULONG32 ulNewDuration =
                                  ulCurTime - ulCurStartTime;

                            durationResolved(pPrevSibling->m_id,
                                  ulNewDuration);

                                    if (m_pTimelineElementManager)
                                        m_pTimelineElementManager->notify((const char*)pPrevSibling->m_id);
                        }
                      }
                  }
                }
                // /If not child of seq, then just set its delay to now:
                else
                {
                  pFragmentElement->m_pTimelineElement->setDelay(
                        ulCurTime,FALSE);
                }
            }
            else
            {
                // /Must just be a mouse move, so return the
                // unresolved value so mouse cursor can change:
                return ((UINT32)-1);
            }
          }
      }
    }
    return 0;
}

SMILNode*
CSmilParser::findFirstNode(SMILNodeList* pNodeList, SMILNodeTag tag)
{
    if(!pNodeList)
    {
      return 0;
    }

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

SMILNode*
CSmilParser::findFirstNode(SMILNodeTag tag)
{
    return findFirstNode(m_pNodeList, tag);
}

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

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

HX_RESULT
CSmilParser::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
CSmilParser::parseSyncBehaviorVal(const char* pSyncBehaviorBuf,
                   CSmilElement* pElement,
                   SMILSyncAttributeTag nTag)
{
    HX_RESULT ret = HXR_OK;
    if (NULL == pSyncBehaviorBuf  ||  (SMILSyncAttrSyncBehavior != nTag  &&
          SMILSyncAttrSyncBehaviorDefault != nTag))
    {
      HX_ASSERT(FALSE);
      return HXR_UNEXPECTED;
    }

    // First, eat all whitespace:
    const char* pCh = pSyncBehaviorBuf;
    while (*pCh  &&  isspace(*pCh))
    {
      ++pCh;
    }

    if (*pCh == '\0')
    {
      // nothing, empty attribute...
      return HXR_OK;
    }

    BOOL bParsedOK = TRUE;
    SMILSyncBehaviorType sbtype = SmilSyncBehaviorInvalid;
    if (strncmp(pCh, "canSlip", 7) == 0)
    {
      pCh += 7;
      sbtype = SmilSyncBehaviorCanSlip;
    }
    else if (strncmp(pCh, "locked", 6) == 0)
    {
      pCh += 6;
      sbtype = SmilSyncBehaviorLocked;
    }
    else if (strncmp(pCh, "independent", 11) == 0)
    {
      pCh += 11;
      sbtype = SmilSyncBehaviorIndependent;
    }
    else if (strncmp(pCh, "default", 7) == 0)
    {
      pCh += 7;
      sbtype = SmilSyncBehaviorDefault;
    }
    else if (strncmp(pCh, "inherit", 7) == 0)
    {
      pCh += 7;
      sbtype = SmilSyncBehaviorInherit;
    }
    else
    {
      bParsedOK = FALSE;
    }

    if (bParsedOK)
    {
      while (*pCh  &&  isspace(*pCh))
      {
          ++pCh;
      }
      if (*pCh)
      {
          bParsedOK = FALSE;  // /Should only be whitespace after value.
      }
    }

    if (SMILSyncAttrSyncBehavior == nTag)
    {
      pElement->m_syncBehavior = sbtype;
      // /syncBehavior can not be "inherit":
      bParsedOK = bParsedOK  &&  (SmilSyncBehaviorInherit != sbtype);
    }
    else
    {
      pElement->m_syncBehaviorDefault = sbtype;
      // /syncBehaviorDefault can not be "default":
      bParsedOK = bParsedOK  &&  (SmilSyncBehaviorDefault != sbtype);
    }

    if (SmilSyncBehaviorInvalid == sbtype  ||  !bParsedOK)
    {
      ret = HXR_INVALID_PARAMETER;
    }

    return ret;
}

HX_RESULT
CSmilParser::parsePeersHigherLower(const char* pBuf,
                   CSmilPriorityClassElement* pPCElement,
                   SMILPriorityClassPeersHigherLowerAttrib nAttrib)
{
    HX_RESULT ret = HXR_OK;
    if (NULL == pBuf  ||  (SMILPriorityClassPeers != nAttrib  &&
          SMILPriorityClassHigher != nAttrib  &&
          SMILPriorityClassLower != nAttrib))
    {
      HX_ASSERT(FALSE);
      return HXR_UNEXPECTED;
    }

    BOOL bParsedOK = TRUE;
    SMILPriorityClassPeersHigherLowerVal val =
          SMILPriorityClassPeersHigherLowerInvalid;

    // First, eat all whitespace:
    const char* pCh = pBuf;
    while (*pCh  &&  isspace(*pCh))
    {
      ++pCh;
    }

    if (*pCh == '\0')
    {
      ret = HXR_INVALID_PARAMETER;
      goto cleanup;
    }

    if (strncmp(pCh, "stop", 4) == 0)
    {
      pCh += 4;
      val = SMILPriorityClassStop;
    }
    else if (strncmp(pCh, "pause", 5) == 0)
    {
      pCh += 5;
      val = SMILPriorityClassPause;
    }
    else if (strncmp(pCh, "defer", 5) == 0)
    {
      pCh += 5;
      val = SMILPriorityClassDefer;
    }
    else if (strncmp(pCh, "never", 5) == 0)
    {
      pCh += 5;
      val = SMILPriorityClassNever;
    }
    else
    {
      bParsedOK = FALSE;
    }

    if (bParsedOK)
    {
      while (*pCh  &&  isspace(*pCh))
      {
          ++pCh;
      }
      if (*pCh)
      {
          bParsedOK = FALSE;  // /Should only be whitespace after value.
      }
    }

    if (SMILPriorityClassPeers == nAttrib)
    {
      pPCElement->m_peers = val;
    }
    else if (SMILPriorityClassHigher == nAttrib)
    {
      pPCElement->m_higher = val;
      // /higher can't be "defer" or "never":
      bParsedOK = bParsedOK  &&  (SMILPriorityClassDefer != val  &&
            SMILPriorityClassNever != val);
    }
    else // /SMILPriorityClassLower
    {
      pPCElement->m_lower = val;
      // /lower can't be "stop" or "pause":
      bParsedOK = bParsedOK  &&  (SMILPriorityClassStop != val  &&
            SMILPriorityClassPause != val);
    }

    if (SMILPriorityClassPeersHigherLowerAttribInvalid== val  ||  !bParsedOK)
    {
      ret = HXR_INVALID_PARAMETER;
    }

cleanup:
    return ret;
}

HX_RESULT
CSmilParser::parsePauseDisplay(const char* pBuf,
                   CSmilPriorityClassElement* pPCElement)
{
    HX_RESULT ret = HXR_OK;
    if (NULL == pBuf)
    {
      HX_ASSERT(FALSE);
      return HXR_UNEXPECTED;
    }

    BOOL bParsedOK = TRUE;

    // First, eat all whitespace:
    const char* pCh = pBuf;
    while (*pCh  &&  isspace(*pCh))
    {
      ++pCh;
    }

    if (*pCh == '\0')
    {
      ret = HXR_INVALID_PARAMETER;
      goto cleanup;
    }

    pPCElement->m_pauseDisplay = SMILPriorityClassPauseDisplayInvalid;
    if (strncmp(pCh, "disable", 7) == 0)
    {
      pCh += 7;
      pPCElement->m_pauseDisplay = SMILPriorityClassPauseDisplayDisable;
    }
    else if (strncmp(pCh, "hide", 4) == 0)
    {
      pCh += 4;
      pPCElement->m_pauseDisplay = SMILPriorityClassPauseDisplayHide;
    }
    else if (strncmp(pCh, "show", 4) == 0)
    {
      pCh += 4;
      pPCElement->m_pauseDisplay = SMILPriorityClassPauseDisplayShow;
    }
    else
    {
      bParsedOK = FALSE;
    }

    if (bParsedOK)
    {
      while (*pCh  &&  isspace(*pCh))
      {
          ++pCh;
      }
      if (*pCh)
      {
          bParsedOK = FALSE;  // /Should only be whitespace after value.
      }
    }

    if (SMILPriorityClassPauseDisplayInvalid == pPCElement->m_pauseDisplay
          ||  !bParsedOK)
    {
      ret = HXR_INVALID_PARAMETER;
    }

cleanup:
    return ret;
}


HX_RESULT
CSmilParser::parseAnchorCoords(const char* pCoords,
                         CSmilAnchorElement* pAnchor)
{
    HX_RESULT rc = HXR_OK;

    INT16 iNumCoords = 0;

    BOOL bIsCircle = FALSE;
    BOOL bIsPoly = FALSE;

    if (0==pAnchor->m_shape.GetLength()  ||
          0==strcmp(pAnchor->m_shape, "rect"))
    {
      iNumCoords = 4;
    }
    else if (0==strcmp(pAnchor->m_shape, "circle"))
    {
      bIsCircle = TRUE;
      iNumCoords = 3;
    }
    // /XXXEH- TODO: handle shape="poly":
    else if (0==strcmp(pAnchor->m_shape, "poly"))
    {
      // /Handles conversion of string to array:
      pAnchor->convertRawPolyData(pCoords);
      bIsPoly = TRUE;
    }

    else
    {
      return HXR_INVALID_PARAMETER;
    }

    if (!bIsPoly)
    {
      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<iNumCoords,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];
      if (bIsCircle)
      {
          pAnchor->m_ulOriginalRadius = pAnchor->m_ulRadius =
            (UINT32)coordArray[2];
          pAnchor->m_bRadiusIsPercent = percentArray[2];
      }
      else // /Rect:
      {
          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;
}

// Functions to parse SMIL Boston Begin & Ends
HX_RESULT
CSmilParser::parseBeginEnd(const char* pBuffer,
                   CSmilElement* pElement,
                   SMILSyncAttributeTag nTag)
{
    HX_RESULT ret = HXR_OK;
    if (pBuffer == NULL || (nTag != SMILSyncAttrBegin &&
      nTag != SMILSyncAttrEnd))
    {
      HX_ASSERT(FALSE);
      return HXR_FAIL;
    }

    // First we will eat all whitespace.
    const char* pCh = pBuffer;
    while (*pCh && isspace(*pCh))
    {
      ++pCh;
    }

    if (*pCh == '\0')
    {
      // nothing, empty attribute...
      return HXR_OK;
    }

    // /XXXEH- make sure this is not followed by a list of times,
    // which would be an error, e.g., begin="id(foo)(begin); 5s".
    // we need to check for a smil-1.0-syncbase-value && indefinite
    if (strncmp(pCh, "id(", 3) == 0)
    {
      // /XXXEH- TODO: make sure this value gets inserted into the
      // begin|end time list so that any calls to getNextResolvedTimeValue
      // return the correct (and only) value:
      ret = parseSmil1SyncbaseValue(pCh, pElement, nTag);
      // smil-1.0-syncbase-value
      if (SMILSyncAttrEnd == nTag)
      {
          // /This is needed to trick the core into letting the
          // media play when we give it a yet-unresolved end:
          pElement->m_ulDuration = WAY_IN_THE_FUTURE;
      }
    }
    else
    {
      // /If we see "indefinite" in a begin or end list, we need to ignore
      // it if any other begin or end time in the list is valid:
      BOOL bAlreadyHasValidNonIndefiniteValue = FALSE;

      // /Fixes PR 50676(parts 1,2,3 & 4) where element has end=[some event]
      // or end="indefinite" and dur=[some val]; we want to use the dur and
      // ignore the end (for now, if it's an event-based end) in that case:
      if (pElement->m_bHasExplicitDur  &&  !pElement->m_bIndefiniteDuration)
      {
          bAlreadyHasValidNonIndefiniteValue = TRUE;
      }

      // It's a value or value list:
      do
      {
          if (*pCh == ';')
          {
            ++pCh;
            // /SMIL Boston spec says that leading and trailing white-
            // space characters are allowed and should be ignored.
            // e.g., "5s; foo.activateEvent":
            // /Get rid of white space after each ';'
            while (*pCh  &&  isspace(*pCh) )
            {
                ++pCh;
            }
            if (!(*pCh))
            {
                break; // /We've found nothing but spaces after the ;
            }
          }

          SmilTimeValue* pVal = new SmilTimeValue(m_pContext,
            pElement->m_pNode->m_ulTagStartLine, pElement);

          const char* begin = pCh;
          char* buffer = NULL;

          while (*pCh && *pCh != ';')
          {
            ++pCh;
          }

          if (*pCh == ';')
          {
#if XXXEH
            // /SMIL Boston spec says that leading and trailing white-
            // space characters are allowed and should be ignored.
            // e.g., "5s; foo.activateEvent":
            // /Get rid of white space before each ';'
            const char* pBkCh = pCh-1;
            while (*pBkCh  &&  pBkCh!=begin  &&  isspace(*pBkCh) )
            {
                --pCh;
                --pBkCh;
            }
#endif
            buffer = new char[pCh - begin + 1];
            strncpy(buffer, begin, pCh - begin); /* Flawfinder: ignore */
            buffer[pCh - begin] = '\0';
            begin = buffer;
          }

          if (strcmp(begin, "indefinite") == 0  &&
                !bAlreadyHasValidNonIndefiniteValue)
          {
            // / "indefinite"
            if (nTag == SMILSyncAttrBegin)
            {
                pElement->m_bIndefiniteBegin = TRUE;
            }
            else if (nTag == SMILSyncAttrEnd)
            {
                pElement->m_bIndefiniteEnd = TRUE;
                pElement->m_bIndefiniteDuration = TRUE;
            }
          }
          else
          {
            ret = pVal->parseValue(begin, nTag,
                  (const char*)(pElement->m_pNode->m_id));
            HX_VECTOR_DELETE(buffer); // /Fix mem leak for >1st list times.
            pVal->setTimeOffset(m_tRefTime);

            HX_VECTOR_DELETE(buffer);
            if (SUCCEEDED(ret))
            {
                if (SmilTimeEvent == pVal->m_type)
                {
                  pElement->m_bHasAtLeastOneEventBasedBegin = TRUE;
                }
                else
                {
                  pElement->m_bHasAtLeastOneNonEventBasedBegin = TRUE;
                }

                if (nTag == SMILSyncAttrBegin)
                {
                  // /If time found was a resolved time (i.e., not an
                  // event-arc or sync-arc, then reset indefinite-begin
                  // flag since we've found another begin time (which
                  // means we should now ignore any "indefinite"
                  // value(s) already found):
                  if (pVal->isTimeResolved())
                  {
                      pElement->m_bIndefiniteBegin = FALSE;
                      bAlreadyHasValidNonIndefiniteValue = TRUE;
                  }

                  if (!pElement->m_pBeginTimeList)
                  {
                      pElement->m_pBeginTimeList = new CHXSimpleList;
                  }
                  pElement->m_pBeginTimeList->AddTail(pVal);
                }
                else // (nTag == SMILSyncAttrEnd)
                {
                  // /If time found was a resolved time (i.e., not an
                  // event-arc or sync-arc, then reset indefinite-begin
                  // flag since we've found another end time (which
                  // means we should now ignore any "indefinite"
                  // value(s) already found):
                  if (pVal->isTimeResolved())
                  {
                      pElement->m_bIndefiniteEnd = FALSE;
                      pElement->m_bIndefiniteDuration = FALSE;
                      bAlreadyHasValidNonIndefiniteValue = TRUE;
                  }

                  if (!pElement->m_pEndTimeList)
                  {
                      pElement->m_pEndTimeList = new CHXSimpleList;
                  }
                  pElement->m_pEndTimeList->AddTail(pVal);
                }
            }
          }
      }
      while (SUCCEEDED(ret) && *pCh == ';');

      if (SMILSyncAttrEnd == nTag  &&
            !bAlreadyHasValidNonIndefiniteValue)
      {
          // /XXXEH- this is needed to trick the core into letting the
          // media play when we give it an indefinite end:
          pElement->m_ulDuration = WAY_IN_THE_FUTURE;
      }

      if (SUCCEEDED(ret))
      {
          if (nTag == SMILSyncAttrBegin)
          {
            if (FAILED(pElement->setBeginTime(this)))
            {
                ret = HXR_FAIL;
                CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                errHandler.ReportError(SMILErrorTimeValueNotallowed,
                  pBuffer, pElement->m_pNode->m_ulTagStartLine);
            }

          }
          else
          {
            if (FAILED(pElement->setEndTime(this)))
            {
                ret = HXR_FAIL;
                CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                errHandler.ReportError(SMILErrorTimeValueNotallowed,
                  pBuffer, pElement->m_pNode->m_ulTagStartLine);
            }
          }
      }
    }
    return ret;
}


HX_RESULT
CSmilParser::parseRestart(const char* pBuffer,
                   CSmilElement* pElement)
{
    HX_RESULT ret = HXR_OK;
    if (NULL == pBuffer  ||  NULL == pElement)
    {
      ret = HXR_FAILED;
    }
    else if (strcmp(pBuffer, "never") == 0)
    {
      pElement->m_restartBehavior = SmilRestartNever;
    }
    else if (strcmp(pBuffer, "whenNotActive") == 0)
    {
      pElement->m_restartBehavior = SmilRestartWhenNotActive;
    }
    else if (strcmp(pBuffer, "always") == 0)
    {
      pElement->m_restartBehavior = SmilRestartAlways;
    }
    else if (strcmp(pBuffer, "default") == 0)
    {
      pElement->m_restartBehavior =
            pElement->m_restartDefaultBehavior;
      if (SmilRestartInherit == pElement->m_restartBehavior)
      {
          pElement->getParentRestartDefault();
      }
    }
    else
    {
      ret = HXR_FAILED;
    }
    return ret;
}


// /XXXEH- TODO: use this in all parseXXX() functions:
const char*
CSmilParser::removeSurroundingWhitespace(const char* pValue)
{
    const char* pRetStr = pValue;
    if (pRetStr)
    {
      while (*pRetStr  &&  isspace(*pRetStr))
      {
          pRetStr++;
      }
    }
    UINT32 ulLen = (UINT32)strlen(pRetStr);
    char* pRetStrEnd = ulLen>0? (char*)(&(pRetStr[ulLen-1])) : NULL;
    if (pRetStrEnd)
    {
      while (pRetStrEnd>pRetStr  &&  *pRetStrEnd  &&
            isspace(*pRetStrEnd))
      {
          pRetStrEnd--;
      }
      // /End the string (this may already be '\0', but who cares?):
      *(pRetStrEnd+1) = '\0';
    }
    return pRetStr;
}


HX_RESULT
CSmilParser::parseSensitivity(const char* pValue, CSmilElement* pSource)
{
    HX_RESULT ret = HXR_OK;

    const char* pWhitespaceRemoved = removeSurroundingWhitespace(pValue);

    if (NULL == pWhitespaceRemoved  ||  NULL == pSource)
    {
      ret = HXR_INVALID_PARAMETER;
    }
    else
    {
      UINT32 ulStrLen = strlen(pWhitespaceRemoved);

      if (strcmp(pWhitespaceRemoved, "opaque") == 0)
      {
          pSource->m_sensitivityToMouseEvents = pWhitespaceRemoved;
      }
      else if (strcmp(pWhitespaceRemoved, "transparent") == 0)
      {
          pSource->m_sensitivityToMouseEvents = pWhitespaceRemoved;
      }
      // /Can't be longer than "+100%" or shorter than "0%":
      else if (ulStrLen <= 5  &&  ulStrLen >= 2)
      {
          // /Spec "Valid values are non-negative CSS2 percentage values":
          if ('+' == *pWhitespaceRemoved)
          {
            pWhitespaceRemoved++;
            ulStrLen--;
          }
          // /Then, add up the numbers:
          char* pTmp = (char*)pWhitespaceRemoved;
          ULONG32 ulNum = 0;
          BOOL bFoundNumber = FALSE;
          while (*pTmp  &&  (*pTmp>='0'  &&  *pTmp<='9'))
          {
            ulNum *= 10;
            ulNum += *pTmp-'0';
            ++pTmp;
            bFoundNumber=TRUE;
          }
          // /Verify # was found, followed by '%', followed by nothing else:
          if (!bFoundNumber  ||  *pTmp != '%'  ||  *(pTmp+1) != '\0')
          {
            ret = HXR_INVALID_PARAMETER;
          }
          else
          {
            *pTmp = '\0';
            pSource->m_sensitivityToMouseEvents = pWhitespaceRemoved;
          }
      }
      else
      {
          ret = HXR_INVALID_PARAMETER;
      }
    }
    return ret;
}



HX_RESULT
CSmilParser::parseRestartDefault(const char* pBuffer,
                   CSmilElement* pElement)
{
    HX_RESULT ret = HXR_OK;
    if (NULL == pBuffer  ||  NULL == pElement)
    {
      ret = HXR_FAILED;
    }
    else if (strcmp(pBuffer, "never") == 0)
    {
      pElement->m_restartDefaultBehavior = pElement->m_restartBehavior =
            SmilRestartNever;
    }
    else if (strcmp(pBuffer, "whenNotActive") == 0)
    {
      pElement->m_restartDefaultBehavior = pElement->m_restartBehavior =
            SmilRestartWhenNotActive;
    }
    else if (strcmp(pBuffer, "always") == 0)
    {
      pElement->m_restartDefaultBehavior = pElement->m_restartBehavior =
            SmilRestartAlways;
    }
    else if (strcmp(pBuffer, "inherit") == 0)
    {
      pElement->getParentRestartDefault();
    }
    else
    {
      ret = HXR_FAILED;
    }
    return ret;
}

FillDefaultType CSmilParser::getFillDefault(CSmilElement* pElement)
{
    FillDefaultType eFillDefault = FillDefaultAuto;

    if (pElement)
    {
        // Get our fillDefault
        eFillDefault = pElement->m_eFillDefault;
        // If the fillDefault is "inherit", then
        // get our parent's fillDefault
        if (eFillDefault == FillDefaultInherit &&
            pElement->m_pNode && pElement->m_pNode->m_pParent)
        {
            eFillDefault = getFillDefault(pElement->m_pNode->m_pParent->m_pElement);
        }
    }

    return eFillDefault;
}

void CSmilParser::resolveFillValue(CSmilElement* pElement)
{
    if (pElement)
    {
        // Get the tag of this element
        SMILNodeTag eTag = SMILUnknown;
        if (pElement->m_pNode)
        {
            eTag = pElement->m_pNode->m_tag;
        }
        // Resolve based on the parsed fill value.
        // If fill="default", then we resolve based on
        // the value of fillDefault. Also, if fill="transition"
        // and this is a time container, then we ignore
        // the parsed value (which makes it revert to the
        // default of fill="default").
        if (pElement->m_eFill == FillDefault ||
            (pElement->m_eFill == FillTransition &&
             (eTag == SMILPar || eTag == SMILSeq || eTag == SMILExcl)))
        {
            // Resolve the fillDefault attribute
            FillDefaultType eDefault = getFillDefault(pElement);
            // Map this to a fill value
            if (eDefault == FillDefaultRemove)
            {
                pElement->m_eActualFill = FillDefault;
            }
            else if (eDefault == FillDefaultFreeze)
            {
                pElement->m_eActualFill = FillFreeze;
            }
            else if (eDefault == FillDefaultHold)
            {
                pElement->m_eActualFill = FillHold;
            }
            else if (eDefault == FillDefaultTransition)
            {
                pElement->m_eActualFill = FillTransition;
            }
            else if (eDefault == FillDefaultAuto)
            {
                // For fill="auto", we have to determine if either "dur",
                // "end", "repeatCount", or "repeatDur" are specified.
                // If none of these are specified, then we use fill="freeze".
                // Otherwise, it's fill="remove". So initially we assign
                // fill="remove" and then if none of these four are
                // specified, then we replace it with fill="freeze".
                if (isAttributeSpecified(pElement, "dur")         ||
                    isAttributeSpecified(pElement, "end")         ||
                    isAttributeSpecified(pElement, "repeatCount") ||
                    isAttributeSpecified(pElement, "repeatDur"))
                {
                    pElement->m_eActualFill = FillRemove;
                }
                else
                {
                    pElement->m_eActualFill = FillFreeze;
                }
            }
        }
        else
        {
            pElement->m_eActualFill = pElement->m_eFill;
        }
    }
}

BOOL CSmilParser::isAttributeSpecified(CSmilElement* pElement,
                                       const char*   pszAttr)
{
    BOOL bRet = FALSE;

    if (pElement &&
        pElement->m_pNode &&
        pElement->m_pNode->m_pValues &&
        pszAttr)
    {
        IHXBuffer* pTmp = NULL;
        HX_RESULT   rv   = pElement->m_pNode->m_pValues->GetPropertyCString(pszAttr, pTmp);
        if (SUCCEEDED(rv))
        {
            bRet = TRUE;
        }
        HX_RELEASE(pTmp);
    }

    return bRet;
}

#if defined(HELIX_FEATURE_SMIL2_TRANSITIONS)

HX_RESULT CSmilParser::getNextTransitionEnd(CSmilElement* pElement, REF(UINT32) rulTime)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pElement)
    {
        // Make sure this element has defined start
        // and end times
        if (pElement->m_ulDelay    != ((UINT32) -1) &&
            pElement->m_ulDuration != ((UINT32) -1))
        {
            // Get the time we would normally be removed
            // XXXMEH - DELAY_DUR_FIX
            UINT32 ulOurNormalRemoveTime = pElement->m_ulDelay + pElement->m_ulDuration -
                                           (pElement->m_bBeginOffsetSet ? pElement->m_lBeginOffset : 0);
            UINT32    ulStop = 0;
            HX_RESULT rv     = pElement->getCurrentScheduledStopTime(ulStop);
            if (SUCCEEDED(rv))
            {
                ulOurNormalRemoveTime = ulStop;
            }
            // Get the parent of this element
            SMILNode* pParentNode = getSyncAncestor(pElement->m_pNode);
            if (pParentNode)
            {
                // Loop through the children of the parent
                BOOL      bFound     = FALSE;
                UINT32    ulNextTime = 0;
                SMILNode* pChildNode = pParentNode->getFirstChild();
                while (pChildNode)
                {
                    // Get the CSmilElement for this child
                    CSmilElement* pChildElement = pChildNode->m_pElement;
                    if (pChildElement)
                    {
                        // Does this child have a begin transition?
                        if (pChildElement->m_beginTransition.GetLength() > 0)
                        {
                            // Is this child scheduled?
                            if (pChildElement->m_ulDelay != ((UINT32) -1))
                            {
                                // Look up the transition
                                void* pVoid = NULL;
                                if (m_pTransitionMap &&
                                    m_pTransitionMap->Lookup((const char*) pChildElement->m_beginTransition,
                                                             pVoid) &&
                                    pVoid)
                                {
                                    // Get the start time of this transition
                                    UINT32 ulStart = pChildElement->m_ulDelay;
                                    // Get the ending time of this transition
                                    CSmilTransition* pTrans = (CSmilTransition*) pVoid;
                                    UINT32           ulEnd  = pChildElement->m_ulDelay +
                                                              pTrans->m_ulDuration;
                                    // Does this transition start after our
                                    // normal remove time?
                                    if (ulStart >= ulOurNormalRemoveTime)
                                    {
                                        // We have found a potential transition, so
                                        // save the minimum time of these potential
                                        // transition ends
                                        if (bFound)
                                        {
                                            if (ulEnd < ulNextTime)
                                            {
                                                ulNextTime = ulEnd;
                                            }
                                        }
                                        else
                                        {
                                            bFound     = TRUE;
                                            ulNextTime = ulEnd;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    pChildNode = pParentNode->getNextChild();
                }
                // Did we find any suitable transition end time?
                if (bFound)
                {
                    rulTime = ulNextTime;
                    retVal  = HXR_OK;
                }
            }
        }
    }

    return retVal;
}

#endif /* #if defined(HELIX_FEATURE_SMIL2_TRANSITIONS) */

void CSmilParser::initParsingMaps()
{
    UINT32 i = 0;
    // Init the element map
    HX_DELETE(m_pElementMap);
    m_pElementMap = new CHXMapStringToOb();
    if (m_pElementMap)
    {
        SMILElementTable* pTmp = (SMILElementTable*) &g_ElementTable[0];
        while (pTmp && pTmp->m_eElement != NumSMIL2Elements)
        {
            m_pElementMap->SetAt(pTmp->m_pszString,
                                 (void*) pTmp->m_eElement);
            pTmp++;
        }
    }
    // Alloc the attribute map
    HX_DELETE(m_pAttributeMap);
    m_pAttributeMap = new CHXMapStringToOb();
    if (m_pAttributeMap)
    {
        // Init the SMIL 2 attribute map. This
        // ONLY contains attributes from the SMIL 2 namespace
        SMILAttributeTable* pTmp = (SMILAttributeTable*) &g_AttributeTable[0];
        while (pTmp && pTmp->m_eAttribute != NumSMIL2Attributes)
        {
            m_pAttributeMap->SetAt(pTmp->m_pszString,
                                   (void*) pTmp->m_eAttribute);
            pTmp++;
        }
    }
#if defined(HELIX_FEATURE_SMIL2_VALIDATION)
    // Init the RN extension element map
    HX_DELETE(m_pExtElementMap);
    m_pExtElementMap = new CHXMapStringToOb();
    if (m_pExtElementMap)
    {
        SMILElementTable* pTmp = (SMILElementTable*) &g_ExtElementTable[0];
        while (pTmp && pTmp->m_eElement != NumSMIL2Elements)
        {
            m_pExtElementMap->SetAt(pTmp->m_pszString,
                                    (void*) pTmp->m_eElement);
            pTmp++;
        }
    }
    // Create the attribute type array
    HX_VECTOR_DELETE(m_pAttrType);
    m_pAttrType = new XMLAttributeType [NumSMIL2Attributes];
    if (m_pAttrType)
    {
        // Init the SMIL 2 attribute map. This
        // ONLY contains attributes from the SMIL 2 namespace
        SMILAttributeTable* pTmp = (SMILAttributeTable*) &g_AttributeTable[0];
        while (pTmp && pTmp->m_eAttribute != NumSMIL2Attributes)
        {
            m_pAttrType[pTmp->m_eAttribute] = pTmp->m_eType;
            pTmp++;
        }
    }
    // Alloc the extension attribute map
    HX_DELETE(m_pExtAttributeMap);
    m_pExtAttributeMap = new CHXMapStringToOb();
    if (m_pExtAttributeMap)
    {
        // Init the SMIL 2 extension attribute map. This
        // ONLY contains attributes from the RN extension namespace
        SMILAttributeTable* pTmp = (SMILAttributeTable*) &g_ExtAttributeTable[0];
        while (pTmp && pTmp->m_eAttribute != NumSMIL2Attributes)
        {
            m_pExtAttributeMap->SetAt(pTmp->m_pszString,
                                      (void*) pTmp->m_eAttribute);
            if (m_pAttrType) m_pAttrType[pTmp->m_eAttribute] = pTmp->m_eType;
            pTmp++;
        }
    }
    // Init the legal attribute map
    HX_DELETE(m_pLegalAttrMap);
    m_pLegalAttrMap = new CRNBinaryMap();
    if (m_pLegalAttrMap)
    {
        HX_RESULT rv = m_pLegalAttrMap->Init(NumSMIL2Elements, NumSMIL2Attributes);
        if (SUCCEEDED(rv))
        {
            BYTE* pVal   = (BYTE*) g_LegalAttr;
            BYTE* pLimit = pVal + sizeof(g_LegalAttr);
            while (pVal < pLimit && *pVal != NumSMIL2Elements)
            {
                // First in list is an element
                UINT32 ulElement = *pVal++;
                // Next is the number of attribute collections
                UINT32 ulNumColl = *pVal++;
                // Next is the number of attributes not in collections
                UINT32 ulNumAttr = *pVal++;
                // Process the attribute collections
                for (i = 0; i < ulNumColl; i++)
                {
                    UINT32 ulCollectionIndex = *pVal++;
                    processCollection(m_pLegalAttrMap, ulElement, ulCollectionIndex);
                }
                // Process the attributes which are not in a collection
                for (i = 0; i < ulNumAttr; i++)
                {
                    UINT32 ulAttr = *pVal++;
                    m_pLegalAttrMap->Set(ulElement, ulAttr);
                }
            }
        }
    }
    // Init the content model
    HX_DELETE(m_pContentModelMap);
    m_pContentModelMap = new CRNBinaryMap();
    if (m_pContentModelMap)
    {
        HX_RESULT rv = m_pContentModelMap->Init(NumSMIL2Elements, NumSMIL2Elements);
        if (SUCCEEDED(rv))
        {
            BYTE*  pVal   = (BYTE*) g_LegalContent;
            BYTE*  pLimit = pVal + sizeof(g_LegalContent);
            while (pVal < pLimit && *pVal != NumSMIL2Elements)
            {
                // First in list is an element
                UINT32 ulElement = *pVal++;
                // Next is the number of legal elements which
                // can be children of this element
                UINT32 ulNumElements = *pVal++;
                // Process the list of elements
                for (i = 0; i < ulNumElements; i++)
                {
                    UINT32 ulLegalContentElement = *pVal++;
                    m_pContentModelMap->Set(ulElement, ulLegalContentElement);
                }
            }
        }
    }
    // Init the enumerated attribute map
    deleteEnumAttrMaps();
    m_ppEnumAttrMap = new CHXMapStringToOb* [NumSMIL2Attributes];
    if (m_ppEnumAttrMap)
    {
        // Null out the pointer array
        memset((void*) m_ppEnumAttrMap, 0,
               NumSMIL2Attributes * sizeof(CHXMapStringToOb*));
        // Now run through the enumerated attribute table
        struct EnumAttrTable* pEntry = (struct EnumAttrTable*) g_EnumAttrTable;
        while (pEntry &&
               pEntry->m_eAttr != NumSMIL2Attributes)
        {
            // Get the attribute
            SMIL2Attribute eAttr = pEntry->m_eAttr;
            // Does a map already exist for this attribute?
            if (!m_ppEnumAttrMap[eAttr])
            {
                m_ppEnumAttrMap[eAttr] = new CHXMapStringToOb();
            }
            if (m_ppEnumAttrMap[eAttr])
            {
                // Set the enumerated type string into the map
                m_ppEnumAttrMap[eAttr]->SetAt(pEntry->m_pszStr,
                                              (void*) pEntry->m_ucEnum);
            }
            pEntry++;
        }
    }
    // Init the required attribute lists
    deleteReqAttrLists();
    m_ppReqAttrList = new CHXSimpleList* [NumSMIL2Elements];
    if (m_ppReqAttrList)
    {
        // Null out the pointer array
        memset((void*) m_ppReqAttrList, 0,
               NumSMIL2Elements * sizeof(CHXSimpleList*));
        // Now run through the required attribute table
        BYTE* pVal   = (BYTE*) g_RequiredAttr;
        BYTE* pLimit = pVal + sizeof(g_RequiredAttr);
        while (pVal < pLimit && *pVal != NumSMIL2Elements)
        {
            // Get the element
            UINT32 ulElement = *pVal++;
            // Get the number of required attributes
            UINT32 ulNumReqAttrs = *pVal++;
            // Do we already have a list for this element?
            if (!m_ppReqAttrList[ulElement])
            {
                // We don't already have one, so create one
                m_ppReqAttrList[ulElement] = new CHXSimpleList();
            }
            if (m_ppReqAttrList[ulElement])
            {
                for (UINT32 i = 0; i < ulNumReqAttrs; i++)
                {
                    // Get the required attribute
                    UINT32 ulAttr = *pVal++;
                    // Add it to the list
                    m_ppReqAttrList[ulElement]->AddTail((void*) ulAttr);
                }
            }
        }
    }
    // Init the namespace map
    HX_DELETE(m_pNamespaceMap);
    m_pNamespaceMap = new CHXMapStringToOb();
    if (m_pNamespaceMap)
    {
        struct NamespaceTable* pEntry = (struct NamespaceTable*) g_NamespaceTable;
        while (pEntry->m_eNamespace != NamespaceNotImplemented)
        {
            m_pNamespaceMap->SetAt(pEntry->m_pszStr, (void*) pEntry->m_eNamespace);
            pEntry++;
        }
    }
#endif /* #if defined(HELIX_FEATURE_SMIL2_VALIDATION) */
}

#if defined(HELIX_FEATURE_SMIL2_VALIDATION)

void CSmilParser::processCollection(CRNBinaryMap* pMap,
                                    UINT32        ulElement,
                                    UINT32        ulCollection)
{
    if (pMap                            &&
        ulElement    < NumSMIL2Elements &&
        ulCollection < NumSMIL2AttributeCollections)
    {
        // Init the parsing variables
        BYTE* pVal   = (BYTE*) g_AttrCollections;
        BYTE* pLimit = pVal + sizeof(g_AttrCollections);
        // Find our collection
        while (pVal < pLimit)
        {
            // Get the current collection
            UINT32 ulColl = *pVal++;
            // Get the number of attributes in this collection
            UINT32 ulNumAttr = *pVal++;
            // Is this our collection?
            if (ulColl == ulCollection)
            {
                // This IS our collection, so process it
                for (UINT32 i = 0; i < ulNumAttr; i++)
                {
                    // Get the attribute
                    UINT32 ulAttr = *pVal++;
                    // Set it in the map
                    pMap->Set(ulElement, ulAttr);
                }
                // Now we can break out of the loop
                break;
            }
            else
            {
                // Not our collection, so just skip past it
                pVal += ulNumAttr;
            }
        }
    }
}

void CSmilParser::deleteEnumAttrMaps()
{
    if (m_ppEnumAttrMap)
    {
        for (UINT32 i = 0; i < NumSMIL2Attributes; i++)
        {
            HX_DELETE(m_ppEnumAttrMap[i]);
        }
    }
    HX_VECTOR_DELETE(m_ppEnumAttrMap);
}


void CSmilParser::deleteReqAttrLists()
{
    if (m_ppReqAttrList)
    {
        for (UINT32 i = 0; i < NumSMIL2Elements; i++)
        {
            HX_DELETE(m_ppReqAttrList[i]);
        }
    }
    HX_VECTOR_DELETE(m_ppReqAttrList);
}

#endif /* #if defined(HELIX_FEATURE_SMIL2_VALIDATION) */

void CSmilParser::deleteValidationNamespaceList()
{
    if (m_pValNSList)
    {
        LISTPOSITION pos = m_pValNSList->GetHeadPosition();
        while (pos)
        {
            CNamespaceInfo* pInfo = (CNamespaceInfo*) m_pValNSList->GetNext(pos);
            HX_DELETE(pInfo);
        }
    }
    HX_DELETE(m_pValNSList);
}

void CSmilParser::checkForXMMFDependency(CSmilElement* pElement)
{
    if (pElement->m_bUsesExternalMediaMarkerFile)
    {
        if (!m_pXMMFElementList)
        {
            m_pXMMFElementList = new CHXSimpleList();
        }
        if (m_pXMMFElementList)
        {
            m_pXMMFElementList->AddTail((void*) pElement);
        }
    }
}

void CSmilParser::handleAllXMMFReferences()
{
    if (m_pXMMFElementList)
    {
        LISTPOSITION pos = m_pXMMFElementList->GetHeadPosition();
        while (pos)
        {
            CSmilElement* pElement =
                (CSmilElement*) m_pXMMFElementList->GetNext(pos);
            if (pElement)
            {
                // Handle any external media marker file
                // references in the begin time list
                handleBeginEndListXMMFReferences(pElement,
                                                 pElement->m_pBeginTimeList);
                // Handle any external media marker file
                // references in the end time list
                handleBeginEndListXMMFReferences(pElement,
                                                 pElement->m_pEndTimeList);
                // Handle any external media marker file
                // references in clipBegin
                if (pElement->m_bClipBeginUsesMarker &&
                    pElement->m_pszClipBeginExternalMarkerFileName)
                {
                    handleClipBeginEndXMMFReference(pElement, TRUE);
                }
                // Handle any external media marker file
                // references in clipEnd
                if (pElement->m_bClipEndUsesMarker &&
                    pElement->m_pszClipEndExternalMarkerFileName)
                {
                    handleClipBeginEndXMMFReference(pElement, FALSE);
                }
            }
        }
    }
}

void CSmilParser::handleBeginEndListXMMFReferences(CSmilElement*  pElement,
                                                   CHXSimpleList* pList)
{
    if (pElement && pList)
    {
        LISTPOSITION pos = pList->GetHeadPosition();
        while (pos)
        {
            SmilTimeValue* pValue = (SmilTimeValue*) pList->GetNext(pos);
            if (pValue &&
                pValue->getTimeType() == SmilTimeMediaMarker &&
                pValue->isExternalMarker())
            {
                // Get the external file name
                const char* pszExtFile = pValue->getExternalMarkerFileName();
                // Get the src that this external
                // file name is relative to
                const char*   pszSrc     = NULL;
                CSmilElement* pIDElement = findElement(pValue->getIdRef());
                if (pIDElement &&
                    pIDElement->m_pNode &&
                    (isMediaObject(pIDElement->m_pNode) &&
                     pIDElement->m_pNode->m_tag != SMILBrush))
                {
                    CSmilSource* pSource = (CSmilSource*) pIDElement;
                    if (pSource &&
                        pSource->m_src.GetLength() > 0)
                    {
                        pszSrc = (const char*) pSource->m_src;
                    }
                }
                // Handle this external file
                pElement->handleXMMF(pValue->getIdRef(), pszExtFile, pszSrc);
            }
        }
    }
}

void CSmilParser::handleClipBeginEndXMMFReference(CSmilElement*  pElement,
                                                  BOOL           bIsClipBegin)
{
    if (pElement && pElement->m_pNode)
    {
        // Get the id for this element
        const char* pszID = (const char*) pElement->m_pNode->m_id;
        // Get the src attribute for this element
        const char* pszSrc = NULL;
        if (isMediaObject(pElement->m_pNode) &&
            pElement->m_pNode->m_tag != SMILBrush)
        {
            CSmilSource* pSource = (CSmilSource*) pElement;
            pszSrc = (const char*) pSource->m_src;
        }
        // Get the external file name
        const char* pszExt = (bIsClipBegin ?
                              pElement->m_pszClipBeginExternalMarkerFileName :
                              pElement->m_pszClipEndExternalMarkerFileName);
        // Now handle the external marker file
        pElement->handleXMMF(pszID, pszExt, pszSrc);
    }
}

HX_RESULT CSmilParser::setElementHandler(SMILNode*            pNode,
                                         CSmilElementHandler* pHandler)
{
    HX_RESULT retVal = HXR_OK;

    if (pNode)
    {
        // Does this node have a CSmilElement?
        if (pNode->m_pElement)
        {
            pNode->m_pElement->m_pHandler = pHandler;
        }
        // Loop through the children and set them as well
        if (pNode->m_pNodeList)
        {
            LISTPOSITION pos = pNode->m_pNodeList->GetHeadPosition();
            while (pos && SUCCEEDED(retVal))
            {
                SMILNode* pChildNode =
                    (SMILNode*) pNode->m_pNodeList->GetNext(pos);
                retVal = setElementHandler(pChildNode, pHandler);
            }
        }
    }

    return retVal;
}

void CSmilParser::addToBeginOrEndTimeMap(SmilTimeValue*     pValue,
                                         SmilTimingListType eType)
{
    if (pValue && (eType == SmilBeginTimeList || eType == SmilEndTimeList))
    {
        const char* pszEventName = pValue->getEventName();
        const char* pszElementID = pValue->getIdRef();
        if (pszEventName && pszElementID)
        {
            CHXMapStringToOb* pEventNameMap = NULL;
            if (eType == SmilBeginTimeList)
            {
                if (!m_pBeginTimeMap)
                {
                    m_pBeginTimeMap = new CHXMapStringToOb();
                }
                pEventNameMap = m_pBeginTimeMap;
            }
            else
            {
                if (!m_pEndTimeMap)
                {
                    m_pEndTimeMap = new CHXMapStringToOb();
                }
                pEventNameMap = m_pEndTimeMap;
            }
            if (pEventNameMap)
            {
                // This is a two-layer map.
                // First layer is a event name lookup
                // Second layer is an element ID lookup
                // The element ID lookup points to a list of SmilTimeValue's.
                //
                // Do we already have an event name entry?
                CHXMapStringToOb* pElementIDMap = NULL;
                void*             pVoid         = NULL;
                if (!pEventNameMap->Lookup(pszEventName, pVoid))
                {
                    // We don't have an event name entry for this event name
                    // so first create one
                    pElementIDMap = new CHXMapStringToOb();
                    // And now stick it in the event name map
                    if (pElementIDMap)
                    {
                       pEventNameMap->SetAt(pszEventName, (void*) pElementIDMap);
                    }
                }
                else
                {
                    pElementIDMap = (CHXMapStringToOb*) pVoid;
                }
                if (pElementIDMap)
                {
                    // Now see if we already have a list for this element ID
                    CHXSimpleList* pTimeValueList = NULL;
                    pVoid                         = NULL;
                    if (!pElementIDMap->Lookup(pszElementID, pVoid))
                    {
                        // We don't have a list of time values for
                        // that element ID, so create one
                        pTimeValueList = new CHXSimpleList();
                        // And now stick that list in the map
                        if (pTimeValueList)
                        {
                            pElementIDMap->SetAt(pszElementID, (void*) pTimeValueList);
                        }
                    }
                    else
                    {
                        pTimeValueList = (CHXSimpleList*) pVoid;
                    }
                    if (pTimeValueList)
                    {
                        // Add this SmilTimeValue to the list
                        pTimeValueList->AddTail((void*) pValue);
                    }
                }
            }
        }
    }
}

void CSmilParser::removeFromBeginOrEndTimeMap(SmilTimeValue*     pValue,
                                              SmilTimingListType eType)
{
    if (pValue && (eType == SmilBeginTimeList || eType == SmilEndTimeList))
    {
        const char* pszEventName = pValue->getEventName();
        const char* pszElementID = pValue->getIdRef();
        if (pszEventName && pszElementID)
        {
            // Get event name map, depending
            // on whether this is a begin or end time value
            CHXMapStringToOb* pEventNameMap = (eType == SmilBeginTimeList ?
                                               m_pBeginTimeMap : m_pEndTimeMap);
            if (pEventNameMap)
            {
                // Look up element ID map
                void* pVoid1 = NULL;
                if (pEventNameMap->Lookup(pszEventName, pVoid1) && pVoid1)
                {
                    // Cast to CHXMapStringToOb
                    CHXMapStringToOb* pElementIDMap = (CHXMapStringToOb*) pVoid1;
                    // Lookup SmilTimeValue list
                    void* pVoid2 = NULL;
                    if (pElementIDMap->Lookup(pszElementID, pVoid2) && pVoid2)
                    {
                        // Cast to CHXSimpleList*
                        CHXSimpleList* pTimeValueList = (CHXSimpleList*) pVoid2;
                        // Now run through the list to find the value
                        LISTPOSITION pos = pTimeValueList->GetHeadPosition();
                        while (pos)
                        {
                            SmilTimeValue* pListValue =
                                (SmilTimeValue*) pTimeValueList->GetAt(pos);
                            if (pListValue &&
                                pListValue == pValue)
                            {
                                // Remove this entry from the list
                                pos = pTimeValueList->RemoveAt(pos);
                            }
                            else
                            {
                                // Just go the next one
                                pTimeValueList->GetNext(pos);
                            }
                        }
                    }
                }
            }
        }
    }
}

BOOL CSmilParser::isTimeValueListPresent(const char*         pszEventName,
                                         const char*         pszElementID,
                                         SmilTimingListType  eType,
                                         REF(CHXSimpleList*) rpList)
{
    BOOL bRet = FALSE;

    if (pszEventName && pszElementID  &&
        (eType == SmilBeginTimeList ||
         eType == SmilEndTimeList))
    {
        // Set default
        rpList = NULL;
        // Get event name map, depending
        // on whether this is a begin or end time value
        CHXMapStringToOb* pEventNameMap = (eType == SmilBeginTimeList ?
                                           m_pBeginTimeMap : m_pEndTimeMap);
        if (pEventNameMap)
        {
            // Look up element ID map
            void* pVoid1 = NULL;
            if (pEventNameMap->Lookup(pszEventName, pVoid1) && pVoid1)
            {
                // Cast to CHXMapStringToOb
                CHXMapStringToOb* pElementIDMap = (CHXMapStringToOb*) pVoid1;
                // Lookup SmilTimeValue list
                void* pVoid2 = NULL;
                if (pElementIDMap->Lookup(pszElementID, pVoid2) && pVoid2)
                {
                    // Cast to CHXSimpleList*
                    rpList = (CHXSimpleList*) pVoid2;
                    // set return value
                    bRet = TRUE;
                }
            }
        }
    }

    return bRet;
}

void CSmilParser::clearTimeValueMap(SmilTimingListType eType)
{
    if (eType == SmilBeginTimeList ||
        eType == SmilEndTimeList)
    {
        CHXMapStringToOb* pEventNameMap = (eType == SmilBeginTimeList ?
                                           m_pBeginTimeMap : m_pEndTimeMap);
        if (pEventNameMap)
        {
            POSITION pos1 = pEventNameMap->GetStartPosition();
            while (pos1)
            {
                const char* pszKey1 = NULL;
                void*       pVoid1  = NULL;
                pEventNameMap->GetNextAssoc(pos1, pszKey1, pVoid1);
                if (pVoid1)
                {
                    // Cast to a CHXMapStringToOb
                    CHXMapStringToOb* pElementIDMap = (CHXMapStringToOb*) pVoid1;
                    // Run through all entries in this map
                    POSITION pos2 = pElementIDMap->GetStartPosition();
                    while (pos2)
                    {
                        const char* pszKey2 = NULL;
                        void*       pVoid2  = NULL;
                        pElementIDMap->GetNextAssoc(pos2, pszKey2, pVoid2);
                        if (pVoid2)
                        {
                            // Cast to a CHXSimpleList
                            CHXSimpleList* pTimeValueList = (CHXSimpleList*) pVoid2;
                            // Now delete it
                            HX_DELETE(pTimeValueList);
                        }
                    }
                }
            }
        }
        if (eType == SmilBeginTimeList)
        {
            HX_DELETE(m_pBeginTimeMap);
        }
        else
        {
            HX_DELETE(m_pEndTimeMap);
        }
    }
}

void CSmilParser::checkNodeForExternalEvents(SMILNode* pNode)
{
    if (pNode)
    {
        // Does this node have a CSmilElement?
        if (pNode->m_pElement)
        {
            // Check its begin time list for custom events
            checkNodeTimeListForExternalEvents(pNode->m_pElement->m_pBeginTimeList);
            // Check its end time list for custom events
            checkNodeTimeListForExternalEvents(pNode->m_pElement->m_pEndTimeList);
        }
        // Recursively check the children of this node
        if (pNode->m_pNodeList)
        {
            LISTPOSITION pos = pNode->m_pNodeList->GetHeadPosition();
            while (pos)
            {
                // Get a child node
                SMILNode* pChildNode = (SMILNode*) pNode->m_pNodeList->GetNext(pos);
                // Check it for custom events
                checkNodeForExternalEvents(pChildNode);
            }
        }
    }
}

void CSmilParser::checkNodeTimeListForExternalEvents(CHXSimpleList* pList)
{
    if (pList)
    {
        LISTPOSITION pos = pList->GetHeadPosition();
        while (pos)
        {
            SmilTimeValue* pTime = (SmilTimeValue*) pList->GetNext(pos);
            if (pTime &&
                pTime->getTimeType() == SmilTimeEvent)
            {
                // Get the event name
                const char* pszEventName = pTime->getEventName();
                // Check whether this is a namespaced event name
                const char* pszEventNameNoPrefix = NULL;
                CNamespaceInfo* pInfo = getNamespaceInfo(pszEventName,
                                                         pszEventNameNoPrefix);
                if (pInfo)
                {
                    // This *is* a namespaced event name, so we need
                    // to add this custom event to the list
                    addExternalEventToList(pTime->getIdRef(),
                                           pszEventName,
                                           pszEventNameNoPrefix,
                                           pInfo);
                }
            }
        }
    }
}

void CSmilParser::addExternalEventToList(const char*     pszID,
                                         const char*     pszFullName,
                                         const char*     pszName,
                                         CNamespaceInfo* pInfo)
{
    if (pszID && pszFullName && pszName && pInfo)
    {
        if (!m_pExternalEventList)
        {
            m_pExternalEventList = new CHXSimpleList();
        }
        if (m_pExternalEventList)
        {
            // First see if this custom event is already present
            BOOL         bPresent = FALSE;
            LISTPOSITION pos      = m_pExternalEventList->GetHeadPosition();
            while (pos)
            {
                ExternalEventInfo* pListInfo =
                    (ExternalEventInfo*) m_pExternalEventList->GetNext(pos);
                if (pListInfo                                     &&
                    pListInfo->m_EventBaseID       == pszID       &&
                    pListInfo->m_PrefixedEventName == pszFullName &&
                    pListInfo->m_EventName         == pszName     &&
                    pListInfo->m_pInfo             == pInfo)
                {
                    bPresent = TRUE;
                    break;
                }
            }
            // If it's not present, then add it
            if (!bPresent)
            {
                ExternalEventInfo* pNewInfo = new ExternalEventInfo;
                if (pNewInfo)
                {
                    // Set the members
                    pNewInfo->m_EventBaseID       = pszID;
                    pNewInfo->m_PrefixedEventName = pszFullName;
                    pNewInfo->m_EventName         = pszName;
                    pNewInfo->m_pInfo             = pInfo;
                    // Append to the list
                    m_pExternalEventList->AddTail((void*) pNewInfo);
                }
            }
        }
    }
}

void CSmilParser::clearExternalEventList()
{
    if (m_pExternalEventList)
    {
        LISTPOSITION pos = m_pExternalEventList->GetHeadPosition();
        while (pos)
        {
            ExternalEventInfo* pInfo =
                (ExternalEventInfo*) m_pExternalEventList->GetNext(pos);
            HX_DELETE(pInfo);
        }
        m_pExternalEventList->RemoveAll();
    }
}

void CSmilParser::addStringProperty(IHXValues* pValues,
                                    IUnknown*   pContext,
                                    const char* pszName,
                                    const char* pszValue)
{
    if (pValues && pContext && pszName && pszValue)
    {
        IHXCommonClassFactory* pFact = NULL;
        pContext->QueryInterface(IID_IHXCommonClassFactory, (void**) &pFact);
        if (pFact)
        {
            IHXBuffer* pBuf = NULL;
            pFact->CreateInstance(CLSID_IHXBuffer, (void**) &pBuf);
            if (pBuf)
            {
                HX_RESULT rv = pBuf->Set((const UCHAR*) pszValue, strlen(pszValue) + 1);
                if (SUCCEEDED(rv))
                {
                    pValues->SetPropertyCString(pszName, pBuf);
                }
            }
            HX_RELEASE(pBuf);
        }
        HX_RELEASE(pFact);
    }
}


const char* CSmilParser::getEnumAttrString(SMIL2Attribute eAttr, BYTE ucVal)
{
    const char* pRet = NULL;

    struct EnumAttrTable* pEntry = (struct EnumAttrTable*) &g_EnumAttrTable[0];
    while (pEntry && pEntry->m_eAttr != NumSMIL2Attributes)
    {
        if (pEntry->m_eAttr  == eAttr &&
            pEntry->m_ucEnum == ucVal)
        {
            pRet = pEntry->m_pszStr;
            break;
        }
        pEntry++;
    }

    return pRet;
}

BOOL CSmilParser::isAttributeAnimated(const char* pszElementID,
                                      UINT32      ulAttrName)
{
    BOOL bRet = FALSE;

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)
    if (m_pAnimateElementList)
    {
        LISTPOSITION pos = m_pAnimateElementList->GetHeadPosition();
        while (pos)
        {
            CSmilAnimateElement* pAnim =
                (CSmilAnimateElement*) m_pAnimateElementList->GetNext(pos);
            if (pAnim &&
                ((UINT32) pAnim->m_ucAttributeName) == ulAttrName &&
                pAnim->m_pTargetElementID &&
                *pAnim->m_pTargetElementID == pszElementID)
            {
                bRet = TRUE;
                break;
            }
        }
    }
#endif /* #if defined(HELIX_FEATURE_SMIL2_ANIMATION) */

    return bRet;
}

SMIL2Element CSmilParser::getSMIL2Element(const char* pszStr)
{
    SMIL2Element eRet = NumSMIL2Elements;

    if (pszStr && m_pElementMap)
    {
        void* pVoid = NULL;
        if (m_pElementMap->Lookup(pszStr, pVoid))
        {
            eRet = (SMIL2Element) (UINT32) pVoid;
        }
    }

    return eRet;
}

SMIL2Attribute CSmilParser::getSMIL2Attribute(const char* pszStr)
{
    SMIL2Attribute eRet = NumSMIL2Attributes;

    if (pszStr && m_pAttributeMap)
    {
        void* pVoid = NULL;
        if (m_pAttributeMap->Lookup(pszStr, pVoid))
        {
            eRet = (SMIL2Attribute) (UINT32) pVoid;
        }
    }

    return eRet;
}

void CSmilParser::checkForExternalEvents()
{
    // Get the <body> node
    SMILNode* pBody = findFirstNode(SMILBody);
    if (pBody)
    {
        // Check recursively for custom events
        checkNodeForExternalEvents(pBody);
    }
}

BOOL CSmilParser::anyExternalEvents(const char* pszID)
{
    BOOL bRet = FALSE;

    if (m_pExternalEventList)
    {
        LISTPOSITION pos = m_pExternalEventList->GetHeadPosition();
        while (pos)
        {
            ExternalEventInfo* pInfo =
                (ExternalEventInfo*) m_pExternalEventList->GetNext(pos);
            if (pInfo &&
                pInfo->m_EventBaseID == pszID)
            {
                bRet = TRUE;
                break;
            }
        }
    }

    return bRet;
}

ExternalEventInfo* CSmilParser::getFirstExternalEvent(const char* pszID)
{
    ExternalEventInfo* pRet = NULL;

    if (m_pExternalEventList)
    {
        // Initialize the iterator to the first external
        // event in the list
        m_pExternalEventListPos = m_pExternalEventList->GetHeadPosition();
        // Now just call getNextExternalEvent()
        pRet = getNextExternalEvent(pszID);
    }

    return pRet;
}

ExternalEventInfo* CSmilParser::getNextExternalEvent(const char* pszID)
{
    ExternalEventInfo* pRet = NULL;

    if (m_pExternalEventList)
    {
        while (m_pExternalEventListPos)
        {
            ExternalEventInfo* pInfo =
                (ExternalEventInfo*) m_pExternalEventList->GetNext(m_pExternalEventListPos);
            if (pInfo &&
                pInfo->m_EventBaseID == pszID)
            {
                pRet = pInfo;
                break;
            }
        }
    }

    return pRet;
}

void CSmilParser::checkForEventHandlers()
{
    if (m_pElementsWithHandlerList)
    {
        LISTPOSITION pos = m_pElementsWithHandlerList->GetHeadPosition();
        while (pos)
        {
            CSmilSource* pSrc =
                (CSmilSource*) m_pElementsWithHandlerList->GetNext(pos);
            if (pSrc && pSrc->m_pNode && pSrc->m_HandlerID.GetLength() > 0)
            {
                // Look up the element with this handler id
                CSmilElement* pEl =
                    findElement((const char*) pSrc->m_HandlerID);
                if (pEl && isMediaObject(pEl->m_pNode))
                {
                    // Cast to a source
                    CSmilSource* pHandlerSrc = (CSmilSource*) pEl;
                    // Set the "handler-for" member in this element
                    pHandlerSrc->m_HandlerFor = pSrc->m_pNode->m_id;
                }
            }
        }
    }
}

AccessErrorBehavior CSmilParser::getAccessErrorBehavior(SMILNode* pNode)
{
    // This is default
    AccessErrorBehavior eRet = AccessErrorBehaviorInherit;

    if (pNode)
    {
        // Now init the value to be AccessErrorBehaviorInherit
        // Get the value from the CSmilElement if there is one
        if (pNode->m_pElement)
        {
            eRet = pNode->m_pElement->m_eAccessErrorBehavior;
        }
        // If this value is inherit, then look to the parent
        if (eRet == AccessErrorBehaviorInherit)
        {
            if (pNode->m_pParent)
            {
                eRet = getAccessErrorBehavior(pNode->m_pParent);
            }
            else
            {
                eRet = AccessErrorBehaviorContinue;
            }
        }
    }

    return eRet;
}

#ifdef XXXMEH_EXTERNAL_MEDIA_MARKER_HACK

HX_RESULT CSmilParser::getExternalMarkerFileTime(const char* pszMarker,
                                                 const char* pszSMILFileURL,
                                                 const char* pszSrcURL,
                                                 REF(UINT32) rulTime)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pszMarker)
    {
        CHXString cMarker;
        CHXString cExtFile;
        BOOL      bExternal = FALSE;
        retVal = parseMarkerURI(pszMarker, cMarker, bExternal, cExtFile);
        if (SUCCEEDED(retVal))
        {
            if (bExternal)
            {
                CHXString cExtFileName;
                retVal = constructExternalMarkerFileName(cExtFile,
                                                         pszSMILFileURL,
                                                         pszSrcURL,
                                                         cExtFileName);
                if (SUCCEEDED(retVal))
                {
                    // Strip the "file://" off the front
                    const char* pszExtFileName = (const char*) cExtFileName;
                    if (!strncmp(pszExtFileName, "file://", 7))
                    {
                        pszExtFileName += 7;
                    }
                    // Open the file
                    FILE* fp = fopen(pszExtFileName, "r");
                    if (fp)
                    {
                        char szLine[128]; /* Flawfinder: ignore */
                        fgets(szLine, 128, fp);
                        if (szLine[strlen(szLine) - 1] == '\r' ||
                            szLine[strlen(szLine) - 1] == '\n')
                        {
                            szLine[strlen(szLine) - 1] = '\0';
                        }
                        if (szLine[strlen(szLine) - 1] == '\r' ||
                            szLine[strlen(szLine) - 1] == '\n')
                        {
                            szLine[strlen(szLine) - 1] = '\0';
                        }
                        if (!strcmp(szLine, "// The format of this file is solely for SMIL 2.0 Interop testing."))
                        {
                            fgets(szLine, 128, fp);
                            if (szLine[strlen(szLine) - 1] == '\r' ||
                                szLine[strlen(szLine) - 1] == '\n')
                            {
                                szLine[strlen(szLine) - 1] = '\0';
                            }
                            if (szLine[strlen(szLine) - 1] == '\r' ||
                                szLine[strlen(szLine) - 1] == '\n')
                            {
                                szLine[strlen(szLine) - 1] = '\0';
                            }
                            if (!strcmp(szLine, "// It must not be supported in publicly-released software."))
                            {
                                // Now loop through the lines
                                while (!feof(fp))
                                {
                                    fgets(szLine, 128, fp);
                                    if (strlen(szLine) > 0)
                                    {
                                        if (szLine[strlen(szLine) - 1] == '\r' ||
                                            szLine[strlen(szLine) - 1] == '\n')
                                        {
                                            szLine[strlen(szLine) - 1] = '\0';
                                        }
                                        if (szLine[strlen(szLine) - 1] == '\r' ||
                                            szLine[strlen(szLine) - 1] == '\n')
                                        {
                                            szLine[strlen(szLine) - 1] = '\0';
                                        }
                                        if (strlen(szLine) > 0)
                                        {
                                            // Parse the line
                                            char* pszToken = strtok(szLine, " \t");
                                            if (pszToken)
                                            {
                                                if (!strcmp(pszToken, (const char*) cMarker))
                                                {
                                                    pszToken = strtok(NULL, " \t");
                                                    if (pszToken)
                                                    {
                                                        HX_RESULT rv = parseClockValue(pszToken, rulTime);
                                                        if (SUCCEEDED(rv))
                                                        {
                                                            retVal  = HXR_OK;
                                                            break;
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        fclose(fp);
                    }
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
    }

    return retVal;
}

HX_RESULT CSmilParser::constructExternalMarkerFileName(const char*    pszMarkerFile,
                                                       const char*    pszSMILFileURL,
                                                       const char*    pszSrcURL,
                                                       REF(CHXString) rcExtMarkerFileName)
{
    HX_RESULT retVal = HXR_OK;

    if (pszMarkerFile && pszSMILFileURL && pszSrcURL)
    {
        // Get the URL prefix, root, and fragment
        CHXString cPrefix;
        CHXString cURLRoot;
        char*     pURLFragment = NULL;
        CHXURL::GeneratePrefixRootFragment(pszSMILFileURL,
                                           cPrefix,
                                           cURLRoot,
                                           pURLFragment);
        // Get the full URL for the src
        CHXString  cFullSrcURL;
        CHXURL     urlObj(pszSrcURL);
        CHXHeader* pHeader = urlObj.GetProperties();
        if (pHeader)
        {
            IHXBuffer* pBuffer = NULL;
            HX_RESULT   rc      = pHeader->GetPropertyBuffer(PROPERTY_SCHEME, pBuffer);
            if (SUCCEEDED(rc))
            {
                // fully qualified URL
                cFullSrcURL = pszSrcURL;
            }
            else
            {
                // relative URL
                // if it starts with '/', make it relative to the root of
                // the URL prefix

                if(*pszSrcURL == '/')
                {
                    cFullSrcURL = cURLRoot + pszSrcURL;
                }
                else
                {
                    cFullSrcURL = cPrefix + pszSrcURL;
                }
            }
            HX_RELEASE(pBuffer);
            HX_RELEASE(pHeader);
        }
        if (cFullSrcURL.GetLength() > 0)
        {
            HX_VECTOR_DELETE(pURLFragment);
            CHXURL::GeneratePrefixRootFragment(cFullSrcURL,
                                               cPrefix,
                                               cURLRoot,
                                               pURLFragment);
            // Now we need to create the external marker file name
            CHXString  cFullMarkerURL;
            CHXURL     urlObj2(pszMarkerFile);
            CHXHeader* pHeader2 = urlObj2.GetProperties();
            if (pHeader2)
            {
                IHXBuffer* pBuffer = NULL;
                HX_RESULT   rc      = pHeader2->GetPropertyBuffer(PROPERTY_SCHEME, pBuffer);
                if (SUCCEEDED(rc))
                {
                    // fully qualified URL
                    cFullMarkerURL = pszMarkerFile;
                }
                else
                {
                    // relative URL
                    // if it starts with '/', make it relative to the root of
                    // the URL prefix

                    if(*pszMarkerFile == '/')
                    {
                        cFullMarkerURL = cURLRoot + pszMarkerFile;
                    }
                    else
                    {
                        cFullMarkerURL = cPrefix + pszMarkerFile;
                    }
                }
                HX_RELEASE(pBuffer);
                HX_RELEASE(pHeader);
            }
            rcExtMarkerFileName = cFullMarkerURL;
        }
        HX_VECTOR_DELETE(pURLFragment);
    }
    else
    {
        retVal = HXR_OK;
    }

    return retVal;
}

#endif // XXXMEH_EXTERNAL_MEDIA_MARKER_HACK

HX_RESULT CSmilParser::parseMarkerURI(const char*    pszStr,
                                      REF(CHXString) rcMarker,
                                      REF(BOOL)      rbExternal,
                                      REF(CHXString) rcExternalFileName)
{
    HX_RESULT retVal = HXR_OK;

    if (pszStr)
    {
        // Look for a '#'
        char* pHash = (char *)strchr(pszStr, '#');
        if (pHash)
        {
            // We DO have a hash. Is there a file
            // name in front of it?
            if (pHash > pszStr)
            {
                // We DO have a hash, so parse out the
                // external file name AND the marker name
                *pHash             = '\0';
                rbExternal         = TRUE;
                rcExternalFileName = pszStr;
                rcMarker           = (const char*) pHash + 1;
            }
            else
            {
                // No file name in front of hash, so
                // it's still an internal marker
                rbExternal = FALSE;
                rcMarker   = (const char*) pHash + 1;
            }
        }
        else
        {
            // No '#', so we know it's NOT an external marker
            rbExternal = FALSE;
            // So the marker is just the entire string
            rcMarker = pszStr;
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT CSmilParser::parseHandlerForID(const char*    pszStr,
                                         REF(CHXString) rcHandlerID)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pszStr)
    {
        const char* pPnd = strchr(pszStr, '#');
        if (pPnd)
        {
            rcHandlerID = pPnd + 1;
        }
    }

    return retVal;
}

HX_RESULT CSmilParser::parseAccessErrorBehavior(const char*              pszStr,
                                                REF(AccessErrorBehavior) reErr)
{
    HX_RESULT retVal = HXR_OK;

    if (pszStr)
    {
        if (!strcmp(pszStr, "inherit"))
        {
            reErr = AccessErrorBehaviorInherit;
        }
        else if (!strcmp(pszStr, "continue"))
        {
            reErr = AccessErrorBehaviorContinue;
        }
        else if (!strcmp(pszStr, "stop"))
        {
            reErr = AccessErrorBehaviorStop;
        }
        else
        {
            retVal = HXR_FAIL;
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

#if defined(XXXMEH_SPLINE_ANIMATION)

HX_RESULT CSmilParser::parseKeyTimes(const char* pszVal, CSmilAnimateElement* pAnim)
{
    HX_RESULT retVal = HXR_OK;

    if (pszVal && pAnim)
    {
        // Allocate some temporary memory to hold
        // a copy of the string
        char* pTmp = new char [strlen(pszVal) + 1];
        if (pTmp)
        {
            // Make a copy of the string, because
            // strtok() is destructive
            strcpy(pTmp, pszVal); /* Flawfinder: ignore */
            // First run through and count the number of tokens
            UINT32        ulNum    = 0;
            const char*   pszSep   = " ;\t\r\n";
            char*         pszToken = strtok(pTmp, pszSep);
            while (pszToken)
            {
                // Increment the number of tokens
                ++ulNum;
                // Get the next token
                pszToken = strtok(NULL, pszSep);
            }
            // Recopy the string
            strcpy(pTmp, pszVal); /* Flawfinder: ignore */
            // Set the number of keyTimes
            pAnim->m_ulNumKeyTimes = ulNum;
            // Allocate space for the keyTimes
            HX_VECTOR_DELETE(pAnim->m_pdKeyTime);
            pAnim->m_pdKeyTime = new double [pAnim->m_ulNumKeyTimes];
            if (pAnim->m_pdKeyTime)
            {
                // Parse the string using strtok()
                ulNum    = 0;
                pszToken = strtok(pTmp, pszSep);
                while (pszToken && SUCCEEDED(retVal))
                {
                    // Parse the current token
                    double dTmp = 0.0;
                    retVal = HXParseDouble(pszToken, dTmp);
                    if (SUCCEEDED(retVal))
                    {
                        // SPEC: Each time value in the keyTimes list is specified
                        // as a floating point value between 0 and 1 (inclusive),
                        // representing a proportional offset into the simple
                        // duration of the animation element.
                        if (dTmp >= 0.0 && dTmp <= 1.0)
                        {
                            // Set the value
                            pAnim->m_pdKeyTime[ulNum++] = dTmp;
                            // Get the next token
                            pszToken = strtok(NULL, pszSep);
                        }
                        else
                        {
                            retVal = HXR_FAIL;
                        }
                    }
                }
            }
            else
            {
                retVal = HXR_OUTOFMEMORY;
            }
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
        HX_VECTOR_DELETE(pTmp);
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT CSmilParser::parseKeySplines(const char* pszVal, CSmilAnimateElement* pAnim)
{
    HX_RESULT retVal = HXR_OK;

    if (pszVal && pAnim)
    {
        // Allocate some temporary memory to hold
        // a copy of the string
        char* pTmp = new char [strlen(pszVal) + 1];
        if (pTmp)
        {
            // Make a copy of the string, because
            // strtok() is destructive
            strcpy(pTmp, pszVal); /* Flawfinder: ignore */
            // First run through and count the number of keySplines
            UINT32        ulNum    = 0;
            const char*   pszSep   = ";";
            char*         pszToken = strtok(pTmp, pszSep);
            while (pszToken)
            {
                // Increment the number of tokens
                ++ulNum;
                // Get the next token
                pszToken = strtok(NULL, pszSep);
            }
            // Recopy the string
            strcpy(pTmp, pszVal); /* Flawfinder: ignore */
            // Set the number of keyTimes
            pAnim->m_ulNumKeySplines = ulNum;
            // Allocate space for the keyTimes
            HX_VECTOR_DELETE(pAnim->m_pKeySpline);
            pAnim->m_pKeySpline = new KeySpline [pAnim->m_ulNumKeySplines];
            if (pAnim->m_pKeySpline)
            {
                // Null out the array
                UINT32 i = 0;
                for (i = 0; i < pAnim->m_ulNumKeySplines; i++)
                {
                    pAnim->m_pKeySpline[i].x1 = 0.0;
                    pAnim->m_pKeySpline[i].y1 = 0.0;
                    pAnim->m_pKeySpline[i].x2 = 0.0;
                    pAnim->m_pKeySpline[i].y2 = 0.0;
                }
                // Allocate a buffer for holding the char*'s
                char** ppTok = new char * [pAnim->m_ulNumKeySplines]; /* Flawfinder: ignore */
                if (ppTok)
                {
                    // Null out the array
                    memset(ppTok, 0, pAnim->m_ulNumKeySplines * sizeof(char*));
                    // Parse the string using strtok(). We simply
                    // run through and save the pointers to the
                    // keySplines so that we can parse them separately
                    // in a separate loop. This is due to restrictions
                    // of using strtok().
                    ulNum    = 0;
                    pszToken = strtok(pTmp, pszSep);
                    while (pszToken)
                    {
                        ppTok[ulNum++] = pszToken;
                        // Get the next keySpline
                        pszToken = strtok(NULL, pszSep);
                    }
                    // Now we can individually parse each keySpline
                    for (UINT32 i = 0; i < pAnim->m_ulNumKeySplines && SUCCEEDED(retVal); i++)
                    {
                        if (ppTok[i])
                        {
                            char*       pszKS      = ppTok[i];
                            const char* pszKSSep   = " ,\t\r\n";
                            char*       pszKSToken = strtok(pszKS, pszKSSep);
                            for (UINT32 j = 0; j < 4 && SUCCEEDED(retVal); j++)
                            {
                                if (pszKSToken)
                                {
                                    // Parse the current token
                                    double dTmp = 0.0;
                                    retVal = HXParseDouble(pszKSToken, dTmp);
                                    if (SUCCEEDED(retVal))
                                    {
                                        // SPEC: The [keySplines] values must
                                        // all be in the range 0 to 1.
                                        if (dTmp >= 0.0 && dTmp <= 1.0)
                                        {
                                            // Set the value
                                            switch (j)
                                            {
                                                case 0: pAnim->m_pKeySpline[i].x1 = dTmp; break;
                                                case 1: pAnim->m_pKeySpline[i].y1 = dTmp; break;
                                                case 2: pAnim->m_pKeySpline[i].x2 = dTmp; break;
                                                case 3: pAnim->m_pKeySpline[i].y2 = dTmp; break;
                                            }
                                            // Get the next token
                                            pszKSToken = strtok(NULL, pszKSSep);
                                        }
                                        else
                                        {
                                            retVal = HXR_FAIL;
                                        }
                                    }
                                }
                                else
                                {
                                    retVal = HXR_FAIL;
                                }
                            }
                        }
                        else
                        {
                            retVal = HXR_FAIL;
                        }
                    }
                }
                HX_VECTOR_DELETE(ppTok);
            }
            else
            {
                retVal = HXR_OUTOFMEMORY;
            }
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
        HX_VECTOR_DELETE(pTmp);
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT CSmilParser::parseSVGPath(const char* pszVal, CSmilAnimateElement* pAnim)
{
    HX_RESULT retVal = HXR_OK;

    if (pszVal && pAnim)
    {
        // XXXMEH - first we make a more restrictive parser that
        // requires whitespace or commas in between all tokens.
        // We will need to go back later and make a looser parser
        // that complies with the BNF.
        //
        // Allocate some memory to copy the string
        char* pTmp = new char [strlen(pszVal) + 1];
        if (pTmp)
        {
            // Copy the string, since strtok() is destructive
            strcpy(pTmp, pszVal); /* Flawfinder: ignore */
            // This array will hold PathCmd*'s and we
            // will copy them into the final array at the end
            CHXPtrArray cTmpArr;
            UINT32 ulNumCmd = 0;
            // This array will hold the list of coordinates
            // until we know how many there are
            UINT32  ulNumTmp      = 0;
            UINT32  ulNumTmpAlloc = 0;
            double* pdTmp         = NULL;
            // Loop through the tokens
            const char* pszSep  = " ,\t\r\n";
            char*       pTok    = strtok(pTmp, pszSep);
            PathCmd*    pCurCmd = NULL;
            while (pTok && SUCCEEDED(retVal))
            {
                // Is this token a command?
                BOOL        bIsCmdType = FALSE;
                BOOL        bRelative  = FALSE;
                PathCmdType eType      = PathCmdTypeMoveTo;
                if (strlen(pTok) == 1)
                {
                    char c = *pTok;
                    if (isalpha(c))
                    {
                        if (islower(c))
                        {
                            bRelative = TRUE;
                            c = toupper(c);
                        }
                        bIsCmdType = TRUE;
                        switch(c)
                        {
                            case 'M': eType = PathCmdTypeMoveTo;             break;
                            case 'Z': eType = PathCmdTypeClosePath;          break;
                            case 'L': eType = PathCmdTypeLineTo;             break;
                            case 'H': eType = PathCmdTypeHorzLineTo;         break;
                            case 'V': eType = PathCmdTypeVertLineTo;         break;
                            case 'C': eType = PathCmdTypeCubicBezierCurveTo; break;
                            default: bIsCmdType = FALSE;
                        }
                    }
                }
                // We now know whether or not this token
                // is a command or not. It either has to
                // be a command or a coordinate. If it's a command
                // and we already have a current PathCmd, then
                // add the current one to the list and create
                // another one. If it's not a command (and thus
                // is a coordinate), then we have to decide where
                // it goes in the current PathCmd.
                if (bIsCmdType)
                {
                    // This is a command type. Do
                    // we already have a current PathCmd?
                    if (pCurCmd)
                    {
                        // Check the number of coordinates against
                        // the type. MoveTo and LineTo commands must
                        // have an even number of coordinates. ClosePath
                        // commands should have zero coordinates.
                        // BezierCurveTo should have a multiple of 6
                        // coordinates.
                        BOOL bLegalNumber = TRUE;
                        switch (pCurCmd->m_eType)
                        {
                            case PathCmdTypeMoveTo:
                            case PathCmdTypeLineTo:
                                {
                                    if (!ulNumTmp || (ulNumTmp%2))
                                    {
                                        bLegalNumber = FALSE;
                                    }
                                }
                                break;
                            case PathCmdTypeHorzLineTo:
                            case PathCmdTypeVertLineTo:
                                {
                                    if (!ulNumTmp)
                                    {
                                        bLegalNumber = FALSE;
                                    }
                                }
                                break;
                            case PathCmdTypeClosePath:
                                {
                                    if (ulNumTmp)
                                    {
                                        bLegalNumber = FALSE;
                                    }
                                }
                                break;
                            case PathCmdTypeCubicBezierCurveTo:
                                {
                                    if (!ulNumTmp || (ulNumTmp%6))
                                    {
                                        bLegalNumber = FALSE;
                                    }
                                }
                                break;

                        }
                        // Do we have a legal number of coordinates?
                        if (bLegalNumber)
                        {
                            // Set the number of coordinates
                            pCurCmd->m_ulNumCoords = ulNumTmp;
                            // Alloc space
                            if (ulNumTmp)
                            {
                                pCurCmd->m_pdCoord = new double [ulNumTmp];
                                if (pCurCmd->m_pdCoord)
                                {
                                    // Copy the values and zero out the
                                    // tmp array
                                    for (UINT32 i = 0; i < ulNumTmp; i++)
                                    {
                                        pCurCmd->m_pdCoord[i] = pdTmp[i];
                                        pdTmp[i]              = 0.0;
                                    }
                                    // Reset the number of coordinates
                                    ulNumTmp = 0;
                                }
                                else
                                {
                                    retVal = HXR_OUTOFMEMORY;
                                }
                            }
                            if (SUCCEEDED(retVal))
                            {
                                // We already have a current PathCmd,
                                // so add the current one to the array
                                cTmpArr.SetAt(ulNumCmd++, (void*) pCurCmd);
                            }
                        }
                        else
                        {
                            retVal = HXR_FAIL;
                        }
                    }
                    if (SUCCEEDED(retVal))
                    {
                        // Create a new PathCmd
                        pCurCmd = new PathCmd();
                        if (pCurCmd)
                        {
                            pCurCmd->m_eType     = eType;
                            pCurCmd->m_bRelative = bRelative;
                        }
                        else
                        {
                            retVal = HXR_OUTOFMEMORY;
                        }
                    }
                }
                else
                {
                    // This must be a coordinate, so parse it as
                    // a coordinate
                    double dTmp = 0;
                    retVal = HXParseDouble(pTok, dTmp);
                    if (SUCCEEDED(retVal))
                    {
                        // We successfully parsed a coordinate, so check
                        // and see if the array has enough space to add
                        // one more element
                        if (ulNumTmp >= ulNumTmpAlloc)
                        {
                            // Not enough space, so we need to alloc
                            // some more space. Double the size of
                            // the array
                            UINT32 ulNewNumTmpAlloc = ulNumTmpAlloc << 1;
                            if (!ulNewNumTmpAlloc)
                            {
                                ulNewNumTmpAlloc = 8;
                            }
                            double* pCpy = new double [ulNewNumTmpAlloc];
                            if (pCpy)
                            {
                                // Copy the old values
                                UINT32 i = 0;
                                for (i = 0; i < ulNumTmpAlloc; i++)
                                {
                                    pCpy[i] = pdTmp[i];
                                }
                                // NULL out the rest of the array
                                for (i = ulNumTmpAlloc; i < ulNewNumTmpAlloc; i++)
                                {
                                    pCpy[i] = 0.0;
                                }
                                // Delete the old array
                                HX_VECTOR_DELETE(pdTmp);
                                // Assign it to the new array
                                pdTmp = pCpy;
                                // Update the number allocated
                                ulNumTmpAlloc = ulNewNumTmpAlloc;
                            }
                            else
                            {
                                retVal = HXR_OUTOFMEMORY;
                            }
                        }
                        if (SUCCEEDED(retVal))
                        {
                            pdTmp[ulNumTmp++] = dTmp;
                        }
                    }
                }
                // Get the next token
                pTok = strtok(NULL, pszSep);
            }
            // Copy the last PathCmd* to the list
            if (pCurCmd)
            {
                // Set the number of coordinates
                pCurCmd->m_ulNumCoords = ulNumTmp;
                // Alloc space
                if (ulNumTmp)
                {
                    pCurCmd->m_pdCoord = new double [ulNumTmp];
                    if (pCurCmd)
                    {
                        // Copy the values and zero out the
                        // tmp array
                        for (UINT32 i = 0; i < ulNumTmp; i++)
                        {
                            pCurCmd->m_pdCoord[i] = pdTmp[i];
                            pdTmp[i]              = 0.0;
                        }
                        // Reset the number of coordinates
                        ulNumTmp = 0;
                    }
                    else
                    {
                        retVal = HXR_OUTOFMEMORY;
                    }
                }
                if (SUCCEEDED(retVal))
                {
                    // We already have a current PathCmd,
                    // so add the current one to the array
                    cTmpArr.SetAt(ulNumCmd++, (void*) pCurCmd);
                }
            }
            // Now we can delete the temporary array of coordinates
            HX_VECTOR_DELETE(pdTmp);
            // Copy from the temporary list to the CSmilAnimateElement
            if (SUCCEEDED(retVal))
            {
                HX_VECTOR_DELETE(pAnim->m_ppPathCmd);
                if (ulNumCmd)
                {
                    pAnim->m_ppPathCmd = new PathCmd* [ulNumCmd];
                    if (pAnim->m_ppPathCmd)
                    {
                        for (UINT32 i = 0; i < ulNumCmd; i++)
                        {
                            pAnim->m_ppPathCmd[i] = (PathCmd*) cTmpArr[i];
                        }
                    }
                    else
                    {
                        retVal = HXR_OUTOFMEMORY;
                    }
                }
                else
                {
                    pAnim->m_ulNumPathCmds = 0;
                }
            }
            if (FAILED(retVal))
            {
                for (UINT32 i = 0; i < ulNumCmd; i++)
                {
                    PathCmd* pCmd = (PathCmd*) cTmpArr[i];
                    HX_DELETE(pCmd);
                }
            }
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
        HX_VECTOR_DELETE(pTmp);
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

#endif // #if defined(XXXMEH_SPLINE_ANIMATION)

BOOL CSmilParser::isDataURL(const char* pszURL)
{
    BOOL bRet = FALSE;

    if (pszURL)
    {
        // Get the string length
        UINT32 ulLen = strlen(pszURL);
        if (ulLen)
        {
            // Skip any leading whitespace char
            // Make sure we have at least 5 characters
            // past the whitespace for "data:"
            UINT32 ulNumWS = strspn(pszURL, " \t\r\n");
            if (ulNumWS + 5 < ulLen)
            {
                // Advance to the beginning of non-whitespace
                pszURL += ulNumWS;
                // Check for "data:"
                if (strncmp(pszURL, "data:", 5) == 0)
                {
                    bRet = TRUE;
                }
            }
        }
    }

    return bRet;
}

HX_RESULT CSmilParser::validateDataURL(const char* pszURL)
{
    HX_RESULT retVal = HXR_OK;

    if (pszURL)
    {
        if (isDataURL(pszURL))
        {
            // For complete syntax of data: URLs see:
            // data: URL RFC (http://www.cis.ohio-state.edu/cs/Services/rfc/rfc-text/rfc2397.txt)
            // URI syntax RFC (http://www.cis.ohio-state.edu/cgi-bin/rfc/rfc2396.html)
            //
            // Initialize the return value to fail
            retVal = HXR_FAIL;
            // Look for "data:"
            const char* pCh = strstr(pszURL, "data:");
            if (pCh)
            {
                // Now find the ","
                pCh = strchr(pCh, ',');
                if (pCh)
                {
                    // Skip past the ","
                    ++pCh;
                    // From now to the end of the string, enforce
                    // the BNF's allowed characters
                    BOOL   bIllegalChar = FALSE;
                    BOOL   bEscapeSeq   = FALSE;
                    UINT32 ulNumEscape  = 0;
                    while (pCh && *pCh)
                    {
                        char c = *pCh++;
                        if (bEscapeSeq)
                        {
                            if (isxdigit(c))
                            {
                                ++ulNumEscape;
                            }
                            else
                            {
                                bIllegalChar = TRUE;
                                break;
                            }
                            if (ulNumEscape >= 2)
                            {
                                bEscapeSeq  = FALSE;
                                ulNumEscape = 0;
                            }
                        }
                        else if (c == '%')
                        {
                            // Make sure the next two characters are
                            // legally escaped
                            bEscapeSeq = TRUE;
                        }
                        else if (!isalnum(c))
                        {
                            if (!(c == ';'  ||
                                  c == '/'  ||
                                  c == '?'  ||
                                  c == ':'  ||
                                  c == '@'  ||
                                  c == '&'  ||
                                  c == '='  ||
                                  c == '+'  ||
                                  c == '$'  ||
                                  c == ','  ||
                                  c == '-'  ||
                                  c == '_'  ||
                                  c == '.'  ||
                                  c == '!'  ||
                                  c == '~'  ||
                                  c == '*'  ||
                                  c == 0x27 || // "'"
                                  c == '('  ||
                                  c == ')'))
                            {
                                bIllegalChar = TRUE;
                                break;
                            }
                        }
                    }
                    if (!bIllegalChar && !bEscapeSeq)
                    {
                        retVal = HXR_OK;
                    }
                }
            }
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT
CSmilParser::addBeginEventElement(SmilTimeValue* pTimeVal)
{
    HX_RESULT rslt = HXR_OK;

    if (!m_pBeginEventList)
    {
      m_pBeginEventList = new CHXSimpleList;
      if (!m_pBeginEventList)
      {
          return HXR_OUTOFMEMORY;
      }
    }

    m_pBeginEventList->AddTail(pTimeVal);

    // Add it to the map
    addToBeginOrEndTimeMap(pTimeVal, SmilBeginTimeList);

    return rslt;
}


// /This function gets called on the fly whenever an event occurs, e.g.,
// when the user clicks on an element.
// ulEventTime is the time a click or other mouse event occured (which
// should be roughly the current playback time.  It is absolute, relative
// to the beginning of the presentation (i.e., current clip).  pEventElement
// is the element where the event occurred (e.g., clicked on) and is
// passed in here so that we can see if anyone in the beginEventList or
// endEventList has a begin event or end event, respectively, equal to this
// event on this pEventElement.
HX_RESULT
CSmilParser::tryToResolveBeginEndEvents(const char* pEventName,
                              const char* pEventElementId,
                              ULONG32 ulEventTime)
{
    HX_RESULT ret = HXR_OK;

    // /XXEH- make sure casting is OK here; what if event time
    // is greater than 24.855 days (i.e., is > 0x7fffffff)?
    HX_ASSERT(ulEventTime < 0x7fffffff);
    INT32 lEventTime = (INT32)ulEventTime;

    if (pEventName && strlen(pEventName) &&
          // /pEventElementId can't be NULL and it can't be empty:
          pEventElementId  &&  strlen(pEventElementId))
    {
      BOOL bIsResumeEvent = (0 == strcmp(pEventName, "resumeEvent"));

        CHXSimpleList* pBeginEventList = NULL;
        if (isTimeValueListPresent(pEventName, pEventElementId,
                                   SmilBeginTimeList, pBeginEventList))
        {
            LISTPOSITION pos = pBeginEventList->GetHeadPosition();
            while (pos)
            {
                BOOL bATimeWasResolved = FALSE;

                SmilTimeValue* pValue = (SmilTimeValue*) pBeginEventList->GetNext(pos);

                if (!pValue->m_pElement)
                {
                    HX_ASSERT(pValue->m_pElement);
                    continue;
                }

                if (!pValue->getIdRef()  ||  !pValue->getEventName())
                {
                    HX_ASSERT(pValue->getIdRef()  && pValue->getEventName());
                    continue;
                }
                // /Now go through all begin times that are awaiting this event
                // from this id'd element and resolve them:
                // /XXXEH- TODO: verify that SMIL Boston final spec still says to
                // resolve & use begin times first before doing so for end times:
                ret = pValue->m_pElement->resolveEventTimeValues(lEventTime,
                                    pEventName, pEventElementId,
                                    SmilBeginTimeList,
                                    bATimeWasResolved /*passed by ref*/);

                // /XXXEH- TODO: verify algorithm in SMIL Boston final draft that
                // says we should first resolve begin values awaiting this event
                // and act on the result, e.g., start an element, and THEN resolve
                // end values awaiting this event.  The order that this is done
                // DOES affect playback in some cases (due to restart and other
                // factors).
                if (SUCCEEDED(ret)  &&  bATimeWasResolved)
                {
                    ret = insertElementWithPendingBeginOrEnd(
                            pValue->m_pElement, lEventTime, SmilBeginTimeList);
                // /Found while fixing PR 79300 & PR 85885: an insert
                // failure is OK for resumeEvent:
                if (!SUCCEEDED(ret)  &&
                      bIsResumeEvent)
                {
                  ret = HXR_OK;
                }
                }
            }// /end walking begin event list.
        }


        // /XXXEH- TODO: move the "setDelay..() calls, above,
        // to below this if () block; we only want to start an element if
        // it's not currently playing and if it has a begin time that is
        // not the same as its end time.  All end times that are in the
        // past at the point of resolution of a begin time are ignored.
        // /XXXEH-  ---make sure I got the above right!  I think that
        //      ---the SMIL Boston spec says that begin="5s" end="4s"
        //      ---doesn't play because, at time 0, 4<5.  However, end isn't
        //      ---ignored if foo is clicked at 5s with the following timing:
        //      ---begin="foo.activateEvent" end="4s" because end is in the past
        //      ---at 5s and is thus ignored.  (RIGHT???).
        CHXSimpleList* pEndEventList = NULL;
        if (SUCCEEDED(ret) &&
          // /Again, don't bother with end values since resumeEvent should
          // only be in begin list (and had better not be in end list):
          !bIsResumeEvent  &&
            isTimeValueListPresent(pEventName, pEventElementId,
                                   SmilEndTimeList, pEndEventList))
        {
            LISTPOSITION pos = pEndEventList->GetHeadPosition();
            while (pos)
            {
                BOOL bATimeWasResolved = FALSE;

                SmilTimeValue* pValue = (SmilTimeValue*) pEndEventList->GetNext(pos);

                if (!pValue->m_pElement)
                {
                    HX_ASSERT(pValue->m_pElement);
                    continue;
                }

                if (!pValue->getIdRef()  ||  !pValue->getEventName())
                {
                    HX_ASSERT(pValue->getIdRef()  && pValue->getEventName());
                    continue;
                }
                // /Now go through all begin times that are awaiting this event
                // from this id'd element and resolve them:
                // /XXXEH- TODO: verify that SMIL Boston final spec still says to
                // resolve & use begin times first before doing so for end times:
                ret = pValue->m_pElement->resolveEventTimeValues(lEventTime,
                                    pEventName, pEventElementId,
                                    SmilEndTimeList,
                                    bATimeWasResolved /*passed by ref*/);

                // /XXXEH- TODO: verify algorithm in SMIL Boston final draft that
                // says we should first resolve begin values awaiting this event
                // and act on the result, e.g., start an element, and THEN resolve
                // end values awaiting this event.  The order that this is done
                // DOES affect playback in some cases (due to restart and other
                // factors).  Note: we may be able to combine above begin-list
                // for loop with this end-list for loop.
                if (SUCCEEDED(ret)  &&  bATimeWasResolved)
                {
                    ret = insertElementWithPendingBeginOrEnd(
                            pValue->m_pElement, lEventTime,
                            SmilEndTimeList);
                }
            }// /end walking begin event list.
        }
    }
    else
    {
        ret = HXR_UNEXPECTED;
    }

    return ret;
}

HX_RESULT
CSmilParser::addEndEventElement(SmilTimeValue* pTimeVal)
{
    HX_RESULT rslt = HXR_OK;

    if (!m_pEndEventList)
    {
      m_pEndEventList = new CHXSimpleList;
      if (!m_pEndEventList)
      {
          return HXR_OUTOFMEMORY;
      }
    }

    m_pEndEventList->AddTail(pTimeVal);

    // Add it to the map
    addToBeginOrEndTimeMap(pTimeVal, SmilEndTimeList);

    return rslt;
}

HX_RESULT CSmilParser::addBeginMediaMarkerSyncElement(SmilTimeValue* pTmpVal)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pTmpVal)
    {
        if (!m_pBeginMediaMarkerList)
        {
            m_pBeginMediaMarkerList = new CHXSimpleList();
        }
        if (m_pBeginMediaMarkerList)
        {
            m_pBeginMediaMarkerList->AddTail((void*) pTmpVal);
            retVal = HXR_OK;
        }
    }

    return retVal;
}

HX_RESULT CSmilParser::addEndMediaMarkerSyncElement(SmilTimeValue* pTmpVal)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pTmpVal)
    {
        if (!m_pEndMediaMarkerList)
        {
            m_pEndMediaMarkerList = new CHXSimpleList();
        }
        if (m_pEndMediaMarkerList)
        {
            m_pEndMediaMarkerList->AddTail((void*) pTmpVal);
            retVal = HXR_OK;
        }
    }

    return retVal;
}

HX_RESULT CSmilParser::resolveMediaMarkerTime(const char* pszID,
                                              const char* pszMarkerName,
                                              UINT32      ulMarkerTime,
                                              REF(BOOL)   rbNeedHandleElements)
{
    HX_RESULT retVal = HXR_OK;

    if (pszID && pszMarkerName)
    {
        // Clear the handle elements flag
        rbNeedHandleElements = FALSE;
        // XXXMEH - TODO - linearly searching lists is not
        // a scalable solution - we should be looking up a
        // string in a map. Change how this is implemented
        //
        // First resolve the begin media marker list
        if (m_pBeginMediaMarkerList)
        {
            LISTPOSITION pos = m_pBeginMediaMarkerList->GetHeadPosition();
            while (pos)
            {
                SmilTimeValue* pValue =
                    (SmilTimeValue*) m_pBeginMediaMarkerList->GetNext(pos);
                if (pValue &&
                    pValue->getIdRef() &&
                    !strcmp(pszID, pValue->getIdRef()) &&
                    pValue->getMarkerName() &&
                    !strcmp(pszMarkerName, pValue->getMarkerName()))
                {
//                    // Find out the scheduled start time for the element
//                    // we are based off of. We already know that this
//                    // element should be resolved since it is playing
//                    // and resolving markers.
//                    CSmilElement* pElement = findElement(pValue->getIdRef());
//                    if (pElement)
//                    {
//                        UINT32    ulStart = 0;
//                        HX_RESULT rv      = pElement->getCurrentScheduledStartTime(ulStart);
//                        if (SUCCEEDED(rv))
//                        {
//                            // Set this time as the "resolved" to time (this is
//                            // the time without the offset)
//                            pValue->setResolvedToTime((INT32) ulStart);
//                            pValue->setWhenTimeWasResolved((INT32) ulStart);
//                        }
//                    }
                    // Set the marker time
                    pValue->setMarkerTime(ulMarkerTime);
                    // XXXMEH - try this out - we want to now
                    // treat this timeval as a sync-arc.
                    addBeginTimeSyncElement(pValue->m_pElement);
                    pValue->m_pElement->m_BeginEventSourceID    = pValue->m_idRef;
                    pValue->m_pElement->m_nBeginEventSourceTag  = pValue->m_position;
                    pValue->m_pElement->m_lBeginEventClockValue = pValue->getTimeOffset();
                    m_pTimelineElementManager->addNotification(pValue->m_pElement->m_BeginEventSourceID,
                                                               pValue->m_pElement->m_pTimelineElement);
                    pValue->m_pElement->m_pTimelineElement->setDelayEventResolved(FALSE);
//                    HX_RESULT rv = insertElementWithPendingBeginOrEnd(pValue->m_pElement,
//                                                                      SMILTIME_NEGATIVE_INFINITY,
//                                                                      SmilBeginTimeList);
                    m_pTimelineElementManager->notify(pValue->m_idRef);
                    m_pTimelineElementManager->notify(pValue->m_pElement->m_pNode->m_id);
                    if (pValue->m_pElement->m_pTimelineElement->getParent())
                    {
                        pValue->m_pElement->m_pTimelineElement->getParent()->checkChildrenFillBehavior();
                    }
                    rbNeedHandleElements = TRUE;
                }
            }
        }
        // Now resolve the end media marker list
        if (m_pEndMediaMarkerList)
        {
            LISTPOSITION pos = m_pEndMediaMarkerList->GetHeadPosition();
            while (pos)
            {
                SmilTimeValue* pValue =
                    (SmilTimeValue*) m_pEndMediaMarkerList->GetNext(pos);
                if (pValue &&
                    pValue->getIdRef() &&
                    !strcmp(pszID, pValue->getIdRef()) &&
                    pValue->getMarkerName() &&
                    !strcmp(pszMarkerName, pValue->getMarkerName()))
                {
//                    // Find out the scheduled start time for the element
//                    // we are based off of. We already know that this
//                    // element should be resolved since it is playing
//                    // and resolving markers.
//                    CSmilElement* pElement = findElement(pValue->getIdRef());
//                    if (pElement)
//                    {
//                        UINT32    ulStart = 0;
//                        HX_RESULT rv      = pValue->m_pElement->getCurrentScheduledStartTime(ulStart);
//                        if (SUCCEEDED(rv))
//                        {
//                            // Set this time as the "resolved" to time (this is
//                            // the time without the offset)
//                            pValue->setResolvedToTime((INT32) ulStart);
//                            pValue->setWhenTimeWasResolved((INT32) ulStart);
//                        }
//                    }
                    // Set the time that the marker resolved to
                    pValue->setMarkerTime(ulMarkerTime);
                    // XXXMEH - try this out - we want to now
                    // treat this timeval as a sync-arc
                    addEndTimeSyncElement(pValue->m_pElement);
                    pValue->m_pElement->m_EndEventSourceID    = pValue->m_idRef;
                    pValue->m_pElement->m_nEndEventSourceTag  = pValue->m_position;
                    pValue->m_pElement->m_lEndEventClockValue = pValue->getTimeOffset();
                    m_pTimelineElementManager->addNotification(pValue->m_pElement->m_EndEventSourceID,
                                                               pValue->m_pElement->m_pTimelineElement);
                    pValue->m_pElement->m_pTimelineElement->setDelayEventResolved(FALSE);
//                    HX_RESULT rv = insertElementWithPendingBeginOrEnd(pValue->m_pElement,
//                                                                      SMILTIME_NEGATIVE_INFINITY,
//                                                                      SmilEndTimeList);
                    m_pTimelineElementManager->notify(pValue->m_idRef);
                    m_pTimelineElementManager->notify(pValue->m_pElement->m_pNode->m_id);
                    if (pValue->m_pElement->m_pTimelineElement->getParent())
                    {
                        pValue->m_pElement->m_pTimelineElement->getParent()->checkChildrenFillBehavior();
                    }
                    rbNeedHandleElements = TRUE;
                }
            }
        }
        // Now resolve any clipBegin's which use this marker
        if (m_pClipBeginMarkerList)
        {
            LISTPOSITION pos = m_pClipBeginMarkerList->GetHeadPosition();
            while (pos)
            {
                CSmilElement* pElement = (CSmilElement*) m_pClipBeginMarkerList->GetNext(pos);
                if (pElement &&
                    pElement->m_pNode &&
                    !strcmp(pszID, pElement->m_pNode->m_id) &&
                    !strcmp(pszMarkerName, pElement->m_pszClipBeginMarkerName))
                {
                    // Set the clipBegin time to the resolved time
                    pElement->m_ulClipBegin         = ulMarkerTime;
                    pElement->m_ulAuthoredClipBegin = ulMarkerTime;
                    // Set the flag saying we're resolved
                    pElement->m_bClipBeginMarkerResolved = TRUE;
                    // If the delay and duration have been resolved
                    // for this element, then we may have tried to
                    // schedule this element previously and could
                    // not because the clipBegin wasn't resolved.
                    // If we have NOT attempted to schedule this
                    // element, then don't try and schedule it now -
                    // wait on the normal process.
                    if (pElement->m_bWaitingOnClipBeginToResolve)
                    {
                        // Clear the flag
                        pElement->m_bWaitingOnClipBeginToResolve = FALSE;
                        // We DID try and schedule this element earlier,
                        // and could not due to not knowing the
                        // clipBegin. Therefore, now that we know the
                        // clipBegin, we will attempt to insert it again.
                        insertTimelineElement(pElement->m_pNode->m_id,
                                              pElement->m_ulDelay);
                        // Set the flag saying we need to
                        // call handleElements()
                        rbNeedHandleElements = TRUE;
                    }
                }
            }
        }
        // Now resolve any clipEnd's which use this marker
        if (m_pClipEndMarkerList)
        {
            LISTPOSITION pos = m_pClipEndMarkerList->GetHeadPosition();
            while (pos)
            {
                CSmilElement* pElement = (CSmilElement*) m_pClipEndMarkerList->GetNext(pos);
                if (pElement &&
                    pElement->m_pNode &&
                    !strcmp(pszID, pElement->m_pNode->m_id) &&
                    !strcmp(pszMarkerName, pElement->m_pszClipEndMarkerName))
                {
                    // Set the clipEnd time to the resolved time
                    pElement->m_ulClipEnd = ulMarkerTime;
                    // Set the flag saying we're resolved
                    pElement->m_bClipEndMarkerResolved = TRUE;
                    // If the delay and duration have been resolved
                    // for this element, then we may have tried to
                    // schedule this element previously and could
                    // not because the clipEnd wasn't resolved.
                    // If we have NOT attempted to schedule this
                    // element, then don't try and schedule it now -
                    // wait on the normal process.
                    if (pElement->m_bWaitingOnClipEndToResolve)
                    {
                        // Clear the flag
                        pElement->m_bWaitingOnClipEndToResolve = FALSE;
                        // We DID try and schedule this element earlier,
                        // and could not due to not knowing the
                        // clipBegin. Therefore, now that we know the
                        // clipBegin, we will attempt to insert it again.
                        insertTimelineElement(pElement->m_pNode->m_id,
                                              pElement->m_ulDelay);
                        // Set the flag saying we need to
                        // call handleElements()
                        rbNeedHandleElements = TRUE;
                    }
                }
            }
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT
CSmilParser::addBeginTimeSyncElement(CSmilElement* pElement)
{
    if (!m_pBeginTimeSyncList)
    {
      m_pBeginTimeSyncList = new CHXSimpleList;
      if (!m_pBeginTimeSyncList)
      {
          return HXR_OUTOFMEMORY;
      }
    }

    m_pBeginTimeSyncList->AddTail(pElement);

    return HXR_OK;
}

HX_RESULT
CSmilParser::addEndTimeSyncElement(CSmilElement* pElement)
{
    if (!m_pEndTimeSyncList)
    {
      m_pEndTimeSyncList = new CHXSimpleList;
      if (!m_pEndTimeSyncList)
      {
          return HXR_OUTOFMEMORY;
      }
    }

    m_pEndTimeSyncList->AddTail(pElement);

    return HXR_OK;
}

HX_RESULT
CSmilParser::resolveSyncBaseElements()
{
    HX_RESULT ret = HXR_OK;
    if (m_pBeginTimeSyncList)
    {
      CHXSimpleList::Iterator i;
      for (i = m_pBeginTimeSyncList->Begin();
          i != m_pBeginTimeSyncList->End(); ++i)
      {
          CSmilElement* pElement = (CSmilElement*)*i;

          LISTPOSITION lPos = NULL;
          if (NULL != pElement->m_pBeginTimeList)
          {
            lPos = pElement->m_pBeginTimeList->GetHeadPosition();
          }
          if (NULL == lPos) // /list is empty.
          {
            continue;
          }

          while (lPos  &&  HXR_OK == ret)
          {
            SmilTimeValue* pValue =
                  (SmilTimeValue*)
                  (pElement->m_pBeginTimeList->GetNext(lPos));

            if (SmilTimeSyncBase == pValue->m_type)
            {
                INT32 offset = pValue->getTimeOffset();
#if defined(_DEBUG) // /XXXEH- testing!:
                LONG32 lEffectiveResolvedToTime = 0;
                pValue->getEffectiveResolvedTime(
                      lEffectiveResolvedToTime);
                // /This will change so assert for now so when we
                // change how these vars are used we won't miss this code
                HX_ASSERT(pValue->isTimeResolved()?
                      offset==lEffectiveResolvedToTime : TRUE);
#endif

                CSmilElement* pSyncElement =
                      findElement((const char*)pValue->m_idRef);
#if defined(XXXEH_REPEAT_VALUE_TIMING_SHOULD_BE_EVENT_BASED)
                if (!pSyncElement)
#else
                // / "xxxx_repeat_copy_nnn" is OK since it's based on
                // xxxx.repeat(nnn) and is supposed to be treated as an
                // event and event id's don't have to exist:
                if (!pSyncElement  &&  !strstr(
                      (const char*)pValue->m_idRef, "_repeat_copy_") )
#endif
                {
                  ret = HXR_FAIL;
                  CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                  errHandler.ReportError(SMILErrorNonexistentID,
                      (const char*)pValue->m_idRef,
                      pElement->m_pNode->m_ulTagStartLine);
                  break;
                }
                // /XXXEH- why don't we need to do this Josh?:
                //else
                //{
                //    pElement->m_BeginEventSourceID = pValue->m_idRef;
                //    pElement->m_lBeginEventClockValue = offset;
                //    pElement->m_nBeginEventSourceTag = pValue->m_position;
                //}
            }
          } // /end while(lPos ...).
      }
    }
    if (SUCCEEDED(ret) && m_pEndTimeSyncList)
    {
      CHXSimpleList::Iterator i;
      for (i = m_pEndTimeSyncList->Begin();
          i != m_pEndTimeSyncList->End(); ++i)
      {
          CSmilElement* pElement = (CSmilElement*)*i;

          LISTPOSITION lPos = NULL;
          if (NULL != pElement->m_pBeginTimeList)
          {
            lPos = pElement->m_pEndTimeList->GetHeadPosition();
          }
          if (NULL == lPos) // /list is empty.
          {
            continue;
          }

          while (lPos  &&  HXR_OK == ret)
          {
            SmilTimeValue* pValue =
                  (SmilTimeValue*)
                  (pElement->m_pEndTimeList->GetNext(lPos));

            if (SmilTimeSyncBase == pValue->m_type)
            {
                INT32 offset = pValue->getTimeOffset();
#if defined(_DEBUG) // /XXXEH- testing!:
                LONG32 lEffectiveResolvedToTime = 0;
                pValue->getEffectiveResolvedTime(
                      lEffectiveResolvedToTime);
                // /This will change so assert for now so when we
                // change how these vars are used we won't miss this code
                HX_ASSERT(pValue->isTimeResolved()?
                      offset==lEffectiveResolvedToTime : TRUE);
#endif

                CSmilElement* pSyncElement =
                      findElement((const char*)pValue->m_idRef);
#if defined(XXXEH_REPEAT_VALUE_TIMING_SHOULD_BE_EVENT_BASED)
                if (!pSyncElement)
#else
                // / "xxxx_repeat_copy_nnn" is OK since it's based on
                // xxxx.repeat(nnn) and is supposed to be treated as an
                // event and event id's don't have to exist:
                if (!pSyncElement  &&  !strstr(
                      (const char*)pValue->m_idRef, "_repeat_copy_") )
#endif
                {
                  ret = HXR_FAIL;
                  CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                  errHandler.ReportError(SMILErrorNonexistentID,
                      (const char*)pValue->m_idRef,
                      pElement->m_pNode->m_ulTagStartLine);
                  break;
                }
                // /XXXEH- why don't we need to do this Josh?:
                //else
                //{
                //    pElement->m_EndEventSourceID = pValue->m_idRef;
                //    pElement->m_lEndEventClockValue = offset;
                //    pElement->m_nEndEventSourceTag = pValue->m_position;
                //}
            }
          } // /end while(lPos ...).
      }
    }

    return ret;
}


// /None of the descendants have yet been added to the timeline because we
// needed to know what all of the descendants' begin times are resolved to
// before we can know which to insert before playback, which not to insert
// until during playback, and which never to insert.  In the following
// method (which is called before playback) we go ahead and insert the
// first one, timewise (and lexically first if there is a tie; in that
// case, we'll start the first and let the next interrupt the first during
// playback).  We also need to extend the duration of the excl to account
// for the latest of all its descendants' begins:
HX_RESULT
CSmilParser::handleExclDescendants()
{
    HX_RESULT ret = HXR_OK;

    // /XXXEH- see note in smlelem.cpp that starts with
    // "XXXEH- TODO: [optimization]: rather than inserting"...

    HX_RESULT retval = HXR_OK;
    BOOL bSomeScheduleWasChanged = FALSE;
    BOOL bDoHandleExclBeforePlaybackStarts = TRUE;
    retval = checkPendingBeginAndEndTimes(0, 0, // <-- use group 0 for all.
          bSomeScheduleWasChanged,
          0, // /Recursion count is 0 when called externally.
          NULL, NULL, bDoHandleExclBeforePlaybackStarts);

/*XXXEH: next, see if any excl has a child that has a resolved begin that is
      beyond the dur of the presentation (group); maybe keep track of that
      in setBeginTime() by keeping overall latest begin excl descendant
      and then, here, add that element if it's beyond end of presentation
      (or just extend dur of excl):
*/
    // /If we are in an excl and we are beginning after our excl parent's
    // current child-derived end, then go ahead and insert into the timeline,
    LISTPOSITION lPos = NULL;
    CHXSimpleList* pPendList = GetPendingBeginTimeList();
    if (pPendList)
    {
      lPos = pPendList->GetHeadPosition();
    }
    LONG32 lLatestGroup0BeginTimeSoFar = 0;
    LONG32 lLatestGroup0EndTimeSoFar = 0;
    SmilTimeValue* pTimeValOfGroup0LatestEndElem = NULL;
    while (lPos)
    {
      SmilTimeValue* pCurTimeVal =
            (SmilTimeValue*)pPendList->GetNext(lPos);
      if (pCurTimeVal  &&  pCurTimeVal->m_pElement)
      {
          ULONG32 ulParentEnd = (UINT32)-1;
          SMILNode* pSyncBaseNode = getSyncAncestor(
                pCurTimeVal->m_pElement->m_pNode);
          BOOL bIsInExcl = hasAncestor(SMILExcl,
                pCurTimeVal->m_pElement->m_pNode);
          if (bIsInExcl  &&  0==pSyncBaseNode->m_nGroup)
          {
            if (pSyncBaseNode  &&  pSyncBaseNode->m_pElement  &&
                  pSyncBaseNode->m_pElement->m_pTimelineElement)
            {
                // /XXXEH- TODO: handle case where endsync is
                // SMILEventSourceID and id matches m_pSourceElement->
                // m_pNode->m_id:
                // /XXXEH- TODO: also handle when sync parent has
                // indefinite end|dur:
                // /Now, see if we can extend our parent's duration and,
                // if so, see if we begin past that end time (if set) and,
                // if so, extend our parent's duration to our end time:
                if (!pSyncBaseNode->m_pElement->m_bHasExplicitEnd  &&
                      !pSyncBaseNode->m_pElement->m_bHasExplicitDur  &&
                      (SMILEventSourceFirst !=
                      pSyncBaseNode->m_pElement->m_nEndsyncEventSourceTag  &&
                      SMILEventSourceID !=
                      pSyncBaseNode->m_pElement->m_nEndsyncEventSourceTag) )
                {
                  if ((UINT32)-1 != pSyncBaseNode->m_pElement->m_ulDuration  &&
                        (UINT32)-1 != pSyncBaseNode->m_pElement->m_ulDelay)
                  {
                      ulParentEnd = pSyncBaseNode->m_pElement->m_ulDelay +
                            pSyncBaseNode->m_pElement->m_ulDuration;
                  }

                  LONG32 lEffectiveResolvedToTime = 0;
                  HX_RESULT tmprslt =
                        pCurTimeVal->getEffectiveResolvedTime(
                        lEffectiveResolvedToTime);

                  if (HXR_OK==tmprslt  &&  lEffectiveResolvedToTime>0  &&
                        !pCurTimeVal->m_pElement->m_bHasBeenScheduled)
                  {
                      // /We default to TRUE if it's MAX UINT32 because
                      // duration of time containers hasn't neccessarily
                      // been set by children yet:
                      BOOL bDelayIsBeyondParentEnd = TRUE;
                      if ((UINT32)-1 != ulParentEnd)
                      {
                        bDelayIsBeyondParentEnd = (ulParentEnd <
                              pCurTimeVal->m_pElement->m_ulDelay);
                      }
                      if (bDelayIsBeyondParentEnd  &&
                            lEffectiveResolvedToTime >
                            lLatestGroup0BeginTimeSoFar)
                      {
                        lLatestGroup0BeginTimeSoFar =
                              lEffectiveResolvedToTime;
                        lLatestGroup0EndTimeSoFar =
                              lEffectiveResolvedToTime +
                              ( (UINT32)-1 == pCurTimeVal->
                              m_pElement->m_ulDuration? 10 :
                              pCurTimeVal->m_pElement->m_ulDuration);
                        pTimeValOfGroup0LatestEndElem = pCurTimeVal;

                        // /Fix related to PR 56690: excl itself needs
                        // to know that it's got at least one child
                        // with a scheduled begin time(s):
                        HX_ASSERT(pSyncBaseNode->m_pElement->m_pTimelineElement);
                        if (pSyncBaseNode->m_pElement->m_pTimelineElement)
                        {
                            pSyncBaseNode->m_pElement->m_pTimelineElement->
                                  setHasChildWithScheduledBegin(TRUE);
                        }
                      }
                  }
                }
            }
          }
      }
    }

    if (lLatestGroup0EndTimeSoFar > 0  &&  pTimeValOfGroup0LatestEndElem)
    {
#if defined(XXXEH_THE_FOLLOWING_BREAKS_PAUSE_RESUME_IN_EXCL__NEEDS_REVISIT)
      // /XXXEH- TODO: either remove from pendingBeginList or *don't*
      // insert here but rather insert a dummy source (or *somehow* tell
      // the timeline to stay open past lLatestGroup0BeginTimeSoFar):
      // See "BUG-20010427_vidDoesntPause(IThinkDueToSecondOneBeing"...
      // ..."AddedPriorToPlaybackIfParentDurIs0sAtTheTime...).smil"
      insertTimelineElement(pTimeValOfGroup0LatestEndElem->m_pElement->
            m_pTimelineElement->m_pID, lLatestGroup0BeginTimeSoFar);
#else
      HX_ASSERT(1);  // /breakpoint-only code.
#endif
    }

    return ret;
}


HX_RESULT
CSmilParser::parseSmil1SyncbaseValue(const char* pCh,
                             CSmilElement* pElement,
                             SMILSyncAttributeTag nTag)
{
    HX_RESULT ret = HXR_OK;

    BOOL bParseError = FALSE;
    BOOL bHasEvent = TRUE;

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

    BOOL bIsSMIL_1_0_idRef = TRUE;

    if (!strncmp(pCh, "id(", 3))
    {
      pCh += 3;   // skip over 'id('
    }
    else // /SMIL 2.0 id-ref can simply be the id val.
    {
      bIsSMIL_1_0_idRef = FALSE;
    }

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

      // lookup ID to see if it references an existing entity,
      // otherwise it is an error
      void* pDummy = NULL;
      // /XXXEH- TODO: make sure this is not only a valid ID,
      // but is an immediate child of pElement (as per the SMIL 2.0 spec):
      if(!m_pIDMap->Lookup(pIdTag, pDummy))
      {
          ret = HXR_FAIL;
          CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
          errHandler.ReportError(SMILErrorBadDuration, pCh,
            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)
    {
      ret = HXR_FAIL;
      CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
      errHandler.ReportError(SMILErrorBadDuration, pCh,
          pElement->m_pNode->m_ulTagStartLine);
    }
    else if(bHasEvent)
    {
      SMILEventSourceTag eSourceTag = SMILEventSourceNone;
      INT32 lEventClockValue = 0;
      if(strcmp(pEvent, "begin") == 0)
      {
          eSourceTag = SMILEventSourceBegin;
      }
      else if(strcmp(pEvent, "end") == 0)
      {
          eSourceTag = SMILEventSourceEnd;
      }
      else
      {
          // eat white space, and + or - if it is there..
          while (isspace(*pEvent))
          {
            ++pEvent;
          }
          BOOL bPos = TRUE;
          if (*pEvent == '+')
          {
            ++pEvent;
          }
          else if (*pEvent == '-')
          {
            ++pEvent;
            bPos = FALSE;
          }

          UINT32 clockValue;

          if(HXR_OK == parseClockValue(pEvent, clockValue))
          {
            eSourceTag = SMILEventSourceClock;
          }
          else
          {
            ret = HXR_FAIL;
            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
            errHandler.ReportError(SMILErrorBadDuration, pEvent,
                pElement->m_pNode->m_ulTagStartLine);
          }

          lEventClockValue =  (bPos ? clockValue:-1 * clockValue );

      }

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

          case SMILSyncAttrEnd:
          {
            pElement->m_nEndEventSourceTag = eSourceTag;
            pElement->m_lEndEventClockValue = lEventClockValue;
          }
          break;

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

          default:
          break;
      }
    }
    delete[] pEvent;

    return ret;
}


BOOL
CSmilParser::EstablishBeginTimeList()
{
    BOOL bNotNull = TRUE;
    if (!m_pPendingBeginTimeList)
    {
      m_pPendingBeginTimeList = new CHXSimpleList;
      bNotNull = (NULL != m_pPendingBeginTimeList);
    }
    return bNotNull;
}

BOOL
CSmilParser::EstablishEndTimeList()
{
    BOOL bNotNull = TRUE;
    if (!m_pPendingEndTimeList)
    {
      m_pPendingEndTimeList = new CHXSimpleList;
      bNotNull = (NULL != m_pPendingEndTimeList);
    }
    return bNotNull;
}

BOOL
CSmilParser::EstablishBeginEventList()
{
    BOOL bNotNull = TRUE;
    if (!m_pBeginEventList)
    {
      m_pBeginEventList = new CHXSimpleList;
      bNotNull = (NULL != m_pBeginEventList);
    }
    return bNotNull;
}

BOOL
CSmilParser::EstablishEndEventList()
{
    BOOL bNotNull = TRUE;
    if (!m_pEndEventList)
    {
      m_pEndEventList = new CHXSimpleList;
      bNotNull = (NULL != m_pEndEventList);
    }
    return bNotNull;
}

HX_RESULT
CSmilParser::insertElementWithPendingBeginOrEnd(CSmilElement* pElement,
                                 INT32 lCurTimeInGroupTime,
                                 SmilTimingListType listType)
{
    HX_RESULT retval = HXR_OK;
    LISTPOSITION lPos = NULL;
    LISTPOSITION lPrev = NULL;
    LISTPOSITION lPosCurVal = NULL;
    SmilTimeValue* pNextResolvedValue = NULL;

    UINT32 ulSyncBaseTime = getSyncBaseTimeInGroupTimeCoords(pElement->m_pNode);
    INT32 lCurTimeInSyncBaseTime =  lCurTimeInGroupTime - (LONG32)ulSyncBaseTime;

    CHXSimpleList* pWhichList = NULL;
    if (SmilBeginTimeList == listType) // /insert begin time:
    {
      if (!EstablishBeginTimeList())
      {
          retval = HXR_OUTOFMEMORY;
          goto cleanup;
      }
      pWhichList = m_pPendingBeginTimeList;
    }
    else if (SmilEndTimeList == listType)// /insert end time:
    {
      if (!EstablishEndTimeList())
      {
          retval = HXR_OUTOFMEMORY;
          goto cleanup;
      }
      pWhichList = m_pPendingEndTimeList;
    }
    else
    {
      HX_ASSERT(SmilBeginTimeList == listType  ||
            SmilEndTimeList == listType);
    }
    if (NULL == pWhichList)
    {
      retval = HXR_FAILED;
      goto cleanup;
    }

    lPos = pWhichList->GetHeadPosition();

    // /First, we need to get the next resolved time of pElement
    // that is at or after lCurTimeInGroupTime:
    retval = pElement->getNextResolvedTimeValue(pNextResolvedValue,
          lCurTimeInGroupTime, lCurTimeInSyncBaseTime,
          listType, /* Don't need list of resolved times:*/ NULL);
    if (!SUCCEEDED(retval)  ||  NULL==pNextResolvedValue  ||
          !pNextResolvedValue->isTimeResolved())
    {
      // /XXXEH- we shouldn't get here since we should never insert an
      // element in this list that doesn't have a future resolved
      // begin/end (depends on listType) time:
      HX_ASSERT(0);
      goto cleanup;
    }

    // /XXXEH- see "TODO: we ONLY want to..." comment below before believing
    // the following:
    // /Remove existing node in list if it's got same id and a later
    // resolved time; we only want each element once in the list and
    // list needs to be in order based on the next resolved begin time:
    // /XXXEH- TODO: we're searching the list twice; combine the following
    // with the subsequent while() loop:
    while (lPos)
    {
      lPosCurVal = lPos;
      SmilTimeValue* pCurNode =
          (SmilTimeValue*)pWhichList->GetNext(lPos);
      if (pCurNode  &&  pCurNode->m_pElement  &&
          pCurNode->m_pElement->m_pNode  &&
          pCurNode->m_pElement->m_pNode->m_id == pElement->m_pNode->m_id)
      {
/* /XXXEH- TODO: we ONLY want to remove duplicates of times that
are based on exactly the same event-base or sync-base reference,
e.g., begin="foo.activateEvent+5s; foo.activateEvent+8s" will
put two distinct begin times in this list when foo is clicked.
Modify the following so that it only removes this duplicate if it has
the same sync-base/event-base id and sync-type/event-type value (including
same offset):
          pWhichList->RemoveAt(lPosCurVal);
*/
      }
      else
      {
          // /(In)sanity check: no node should be NULL in this list and no
          // non-NULL node should have NULL m_pElement:
          HX_ASSERT(pCurNode  &&  pCurNode->m_pElement  &&
                pCurNode->m_pElement->m_pNode);
      }
    }

    lPos = pWhichList->GetHeadPosition();
    lPrev = lPos;
    lPosCurVal = lPos;

    // /Now, find where in the list to insert this new one based on temporal
    // order:
    while(lPos)
    {
      SmilTimeValue* pCurNode =
          (SmilTimeValue*)pWhichList->GetNext(lPos);
      if (!pCurNode  ||  !pCurNode->m_pElement)
      {
          HX_ASSERT(pCurNode  &&  pCurNode->m_pElement);
          continue;
      }

      // /To help fix one of the problems in PR 85885 & PR 79300, we need to
      // make sure the curTime passed in is in syncBase time space, not group
      // time, otherwise 2nd-or-later-in-seq children with scheduled begins
      // will be treated as in the past or otherwise with wrong begin time:
      UINT32 ulCurNodeSyncBaseTime = getSyncBaseTimeInGroupTimeCoords(
            pCurNode->m_pElement->m_pNode);
      INT32 lCurNodeCurTimeInSyncBaseTime = lCurTimeInGroupTime -
            (LONG32)ulCurNodeSyncBaseTime;

      // /This is the next resolved time of the current list node that
      // we're comparing to pElement's next resolved time:
      SmilTimeValue* pNextTimeValueOfCurElement = NULL;
      retval = pCurNode->m_pElement->getNextResolvedTimeValue(
            pNextTimeValueOfCurElement, lCurTimeInGroupTime,
            lCurNodeCurTimeInSyncBaseTime, listType,
            /* Don't need list of resolved times:*/ NULL);
      if (!SUCCEEDED(retval)  ||  NULL==pNextTimeValueOfCurElement  ||
            !pNextTimeValueOfCurElement->isTimeResolved())
      {
          // /XXXEH- we shouldn't get here because we should never have
          // inserted an element in this list that doesn't have a future
          // resolved begin:
/// /XXXEH- not so sure about that...         HX_ASSERT(0);
          continue;
      }
      LONG32 lCurNodeEffectiveResolvedTime;
      HX_RESULT rv1 = pNextTimeValueOfCurElement->getEffectiveResolvedTime(
            lCurNodeEffectiveResolvedTime);
      if (!SUCCEEDED(rv1))
      {
          continue;
      }
      // /This is the next resolved time of pElement:
      LONG32 lElementValueEffectiveResolvedTime;
      HX_RESULT rv2 = pNextResolvedValue->getEffectiveResolvedTime(
            lElementValueEffectiveResolvedTime);
      if (!SUCCEEDED(rv2))
      {
          retval = rv2;
          goto cleanup; // /error; we can't proceed.
      }
      // /See if pElement's next resolved time is less than the current
      // list node's:
      if (lCurNodeEffectiveResolvedTime>lElementValueEffectiveResolvedTime)
      {
          pWhichList->InsertBefore(lPrev, pNextResolvedValue);
          retval = HXR_OK;
          goto cleanup;
      }
      lPrev = lPos;
    }
    // /Nothing was inserted in the above while(), so insert it at end:
    pWhichList->AddTail(pNextResolvedValue);

cleanup:
    return retval;
}

HX_RESULT
CSmilParser::handlePendingScheduling(INT32 lCurTimeInGroupTime,
      INT16 iCurrentGroupIndex,
      REF(BOOL) bSomeScheduleWasChanged,
      /*OUT*/ CHXSimpleList* pPauseDisplayHideElementList,
      /*OUT*/ CHXSimpleList* pPauseDisplayDisableElementList)
{
    HX_RESULT retval = HXR_OK;
    HX_RESULT tmpretval = HXR_OK;
    bSomeScheduleWasChanged = FALSE;
    retval = checkPendingBeginAndEndTimes(lCurTimeInGroupTime, iCurrentGroupIndex,
          // /This is set to TRUE if any element's begin and/or end time
          // gets updated to a new time:
          bSomeScheduleWasChanged,
          // /Recursion count is 0 when called externally:
          0,
          pPauseDisplayHideElementList,
          pPauseDisplayDisableElementList,
          FALSE);

    return retval;
}


typedef enum
{
      SMILTimelineStatusUnknown = 0
    , SMILTimelineStatusNeverScheduled
    , SMILTimelineStatusStartScheduledForLater
    , SMILTimelineStatusCurrentlyPlaying
    , SMILTimelineStatusFinishedPlaying
    , SMILTimelineStatusCurrentlyPaused
    , SMILTimelineStatusCurrentlyPausedAndRestarting // /For PR 62397 fix.
} SMILTimelineStatus;
/* This looks to see if any elements in the lists have a begin or end time
 * that is not yet accounted for in the timeline and that is now at or
 * earlier than lCurTime.  If any such begin times are found, then a track
 * that's playing that is associated with that element may get restarted
 * (depending on the element's restart value) and if any such end times are
 * found then the track that's playing that's associated with that element
 * will get stopped.  Note: Each list is sorted temporally so that the
 * first element is the earliest, and each element whose time is reached
 * gets removed.
 *
 * // /XXXEH- TODO: when a new begin time of an excl becomes scheduled
 * // within this function, be sure to schedule an end for all its siblings.
 *
 * Returns HXR_FAILED or HXR_UNEXPECTED on error, returns TRUE in
 * bREFSomeScheduleWasChanged if any begin and/or end time became used to
 * either start, restart, or end any element.
 */
HX_RESULT
CSmilParser::checkPendingBeginAndEndTimes(INT32 lCurTime,
      INT16 iCurrentGroupIndex,
      REF(BOOL) bREFSomeScheduleWasChanged, INT32 lRecursionCount,
      /*OUT*/ CHXSimpleList* pPauseDisplayHideElementList,
      /*OUT*/ CHXSimpleList* pPauseDisplayDisableElementList,
      BOOL bDoHandleExclBeforePlaybackStarts)
{
    HX_RESULT retval = HXR_OK;
    bREFSomeScheduleWasChanged = FALSE;

    m_lLastCheckPendingTime = lCurTime;

    HX_ASSERT(lRecursionCount<=MAX_PENDING_CHECK_CALLDEPTH);

    // /Handle begin times that are ready to go:
    LISTPOSITION lPos = NULL;
    if (m_pPendingBeginTimeList)
    {
      lPos = m_pPendingBeginTimeList->GetHeadPosition();
    }

    // /Keep track of this at the excl-element level to allow multiple excl's
    // to go through this loop as happens, for instance, when multiple groups
    // exist in the presentation and more than one has an excl in it, or when
    // multiple excl's begin at 0s in the same par:
    CHXMapStringToOb* pExclChildAddedBeforePlayback = new CHXMapStringToOb();

    SmilTimeValue* pTimeValEndToggleToBeIgnored = NULL;

    // /Go through and see if anybody has a begin time that's ready to become
    // their active begin time:
    while (lPos  &&  HXR_OK == retval)
    {
      LISTPOSITION lPosOfCurTmpVal = lPos;
      // /Gets val at lPos and then moves lPos to next node in list:
      SmilTimeValue* pTmpVal =
            (SmilTimeValue*)m_pPendingBeginTimeList->GetNext(lPos);
      if (!pTmpVal  ||  !pTmpVal->m_pElement  ||
            !pTmpVal->m_pElement->m_pNode)
      {
          // /List shouldn't have an empty node or a node w/NULL element:
          HX_ASSERT(pTmpVal  &&  pTmpVal->m_pElement  &&
                pTmpVal->m_pElement->m_pNode);
          // /Get rid of it from the list:
          m_pPendingBeginTimeList->RemoveAt(lPosOfCurTmpVal);
          continue;
      }

      // /Fixes PR 54281 (final timing fix after core nested-meta fix):
      // Don't use this timeValue if it's element is not in the current
      // group, otherwise we'll start pending tracks based on prior group's
      // onTimeSync times (each group has zero-based OnTimeSyncs()).
      // NOTE: this can only happen in an excl which is the only place a
      // resolved-begin-before-playback element will end up in the pending
      // list:
      if (pTmpVal->m_pElement->m_pNode->m_nGroup  != iCurrentGroupIndex  &&
            // /If this is -1, then no source has ever been added to the
            // core.  Helps fix cases where event-begun media is first
            // media to play:
            iCurrentGroupIndex != -1 &&
            // /OK to do all time-0 ones prior to playback commencing
            // because they're all OK to pre-schedule, anyway:
            !bDoHandleExclBeforePlaybackStarts)
      {
          continue;
      }

      LONG32 lEffectiveResolvedTime = 0;
      LONG32 lSyncbaseDelayAddedToResolvedTime = 0;
      HX_RESULT tmprslt =
            pTmpVal->getEffectiveResolvedTime(lEffectiveResolvedTime);

      if (HXR_OK!=tmprslt)
      {
          // /No element should ever have been inserted in this list if it
          // didn't have a resolved time so we shouldn't get here!
          HX_ASSERT(HXR_OK==tmprslt);
          // /Get rid of it from the list:
          m_pPendingBeginTimeList->RemoveAt(lPosOfCurTmpVal);
          continue;
      }

      SMILNode* pExclAncestor = getSpecificAncestor(SMILExcl,
            pTmpVal->m_pElement->m_pNode);

      SMILNode* pSyncAncestor = getSyncAncestor(pTmpVal->m_pElement->m_pNode);
      // /If we're in an excl and being handled prior to playback starting
      // (or after playback starts but our excl parent has a begin offset),
      // then we may not have our delay set yet by our parent in which
      // case we need to add our parent delay to lEffectiveResolvedTime:
      // This *only* applies to scheduled (non-event) begin times that
      // resolved at time 0 before being able to adjust for parent delay
      // offset:
      if (0 == pTmpVal->getWhenTimeWasResolved()  &&
            0 == lEffectiveResolvedTime  &&
            pExclAncestor  &&
            pTmpVal->m_pElement  &&  pTmpVal->m_pElement->m_pNode  &&
            (UINT32)-1 == pTmpVal->m_pElement->m_ulDelay  &&
            (SmilTimeOffset == pTmpVal->m_type  ||
            SmilTimeClockValue == pTmpVal->m_type  ||
            SmilTimeWallclock == pTmpVal->m_type))
      {
          if (pSyncAncestor  &&  pSyncAncestor->m_pElement  &&
                (UINT32)-1 != pSyncAncestor->m_pElement->m_ulDelay)
          {
            if (bDoHandleExclBeforePlaybackStarts)
            {
                pTmpVal->setResolvedToTime(pSyncAncestor->m_pElement->m_ulDelay);
                lEffectiveResolvedTime += pSyncAncestor->m_pElement->m_ulDelay;
                lSyncbaseDelayAddedToResolvedTime =
                      pSyncAncestor->m_pElement->m_ulDelay;
            }
            else
            {
                // /We'll get here if excl parent is not 1st child of a seq.
                HX_ASSERT(1  ||  "XXXEH- BUG-20010516_PR52110(simplifiedVersionMaybe)....smil");
                lEffectiveResolvedTime += pSyncAncestor->m_pElement->m_ulDelay;
                lSyncbaseDelayAddedToResolvedTime =
                      pSyncAncestor->m_pElement->m_ulDelay;
            }
          }
          else
          {
            // /Parent has unresolved delay so we don't want to do
            // anything with its children yet:
            continue;
          }
      }
      // /If our effectiveResolvedTime is later than zero and we're in an
      // excl and have a clock-type time, then our time is still in parent
      // coordinates and not in overall timeline coordinates like lCurTime
      // is.  We need to adjust the lEffectiveResolvedTime up by the delay
      // of our parent for later comparison to lCurTime:
      else if (pExclAncestor  &&
            pTmpVal->m_pElement  &&  pTmpVal->m_pElement->m_pNode  &&
            (UINT32)-1 == pTmpVal->m_pElement->m_ulDelay  &&
            (SmilTimeOffset == pTmpVal->m_type  ||
            SmilTimeClockValue == pTmpVal->m_type  ||
            SmilTimeWallclock == pTmpVal->m_type))
      {
          // /In an excl, we don't want to use clock value times; we want
          // use resolved-to times since we're treating these clock times
          // more like events.  getEffectiveResolvedTime(), above, just
          // returns the offset if it's a clock time, i.e., it is not in
          // player time but rather in local sync-base time because
          // clock times usually are used prior to playback as local times:
          INT32 lResolvedToTime = pTmpVal->getResolvedToTimeWithoutOffset();
          if (pSyncAncestor  &&  pSyncAncestor->m_pElement  &&
                (UINT32)-1 != pSyncAncestor->m_pElement->m_ulDelay)
          {
            lEffectiveResolvedTime += pSyncAncestor->m_pElement->m_ulDelay;

            // /If this is an excl-deferred track, then lResolvedToTime
            // could be >0 and != lEffectiveResolvedTime (as in PR 55936).

            pTmpVal->setResolvedToTime(lEffectiveResolvedTime);
            pTmpVal->setWhenTimeWasResolved(lCurTime);
          }
          else
          {
            // /Parent has unresolved delay so we don't want to do
            // anything with its children yet:
            continue;
          }
      }

      ULONG32 ulActualStartTime = 0;
      ULONG32 ulActualStopTime = 0;
      HX_RESULT pnrStart =
            pTmpVal->m_pElement->getCurrentScheduledStartTime(
            ulActualStartTime);
      LONG32 lElementCurBeginTime = (LONG32)ulActualStartTime;

      HX_RESULT pnrStop =
            pTmpVal->m_pElement->getCurrentScheduledStopTime(
            ulActualStopTime);
      LONG32 lElementCurEndTime = (LONG32)ulActualStopTime;

      SMILTimelineStatus curSMILTimelineStatus = SMILTimelineStatusUnknown;
      BOOL bTrackIsFinishedAndFrozen = FALSE;

      BOOL bCurElementIsTimeContainer =
            isTimeContainerObject(pTmpVal->m_pElement->m_pNode);
      if (!pTmpVal->m_pElement->m_bInsertedIntoTimeline)
      {
          curSMILTimelineStatus = SMILTimelineStatusNeverScheduled;
      }
      else
      {
          HX_ASSERT(HXR_OK == pnrStart  &&
                (UINT32)-1 != lElementCurBeginTime  &&
                SMILTIME_INFINITY != lElementCurBeginTime);

          if (lElementCurBeginTime > lCurTime)
          {
            curSMILTimelineStatus =
                  SMILTimelineStatusStartScheduledForLater;
          }
          else
          {
            // /We need to check if this is a paused track and, if so,
            // extend its end time by how long it was paused:
            if (lEffectiveResolvedTime <= lCurTime  &&
                  pTmpVal->isResumeEvent()  &&
                  pTmpVal->m_pElement->m_pHandler)
            {
                CSmilTimelineElement* pParent = pTmpVal->m_pElement->
                      m_pTimelineElement->getParent();

                pTmpVal->m_pElement->prepForResumeInExcl();

                // /First, let's check to see if it's got an explicit end
                // time that's already in the past (Interop Timing #25.8):
                BOOL bEndIsInThePast =
                      (pTmpVal->m_pElement->m_bHasExplicitEnd  &&
                      pTmpVal->m_pElement->m_bEndOffsetSet  &&
                      pTmpVal->m_pElement->m_lEndOffset <= lCurTime);
                if (!bEndIsInThePast)
                {
                  curSMILTimelineStatus =
                        SMILTimelineStatusCurrentlyPaused;
                  LONG32 lPauseTime = pTmpVal->getPauseTime();
                  HX_ASSERT(lPauseTime < lCurTime);
                  LONG32 lPauseDuration = lCurTime>lPauseTime?
                        (lCurTime-lPauseTime) : 0;
                  ULONG32 ulDur = pTmpVal->m_pElement->m_ulDuration;
                  HX_ASSERT(pTmpVal->m_pElement->m_ulAnticipatedPauseDur <
                        WAY_IN_THE_FUTURE);
                  if (pTmpVal->m_pElement->m_ulAnticipatedPauseDur < ulDur)
                  {
                      // /Helps fix PR 86107: reduce dur by what we thought
                      // it was supposed to extend by (which may differ
                      // from actual pause time) and then add actual pause
                      // duration to the original value, below, to get
                      // total actual duration including pause:
                      ulDur -= pTmpVal->m_pElement->m_ulAnticipatedPauseDur;
                  }
                        // /Adding the conditional to this "else" re-fixes
                        // PR 85896 (& dup 79288) which were broken by PR 86107
                        // fix, and helps fix PR 100601.  This might be a valid
                        // case where the duration of the pausing element was
                        // not yet set at time of pause (i.e., it was still
                        // 0xFFFFFFFF) or it was indefinite; we shouldn't adjust
                        // "back" if we never adjusted forward:
                  else if (pTmpVal->m_pElement->m_ulAnticipatedPauseDur <
                        WAY_IN_THE_FUTURE)
                  {
                      ulDur = 0;
                      HX_ASSERT(ulDur);
                  }
                  // /Make sure this doesn't get used now that it's resuming:
                  pTmpVal->m_pElement->m_ulAnticipatedPauseDur = 0;
                  if (lPauseDuration > 0  &&  (UINT32)-1 != ulDur &&
                        SMILTIME_INFINITY != ulDur  &&
                        (!pTmpVal->m_pElement->m_bIndefiniteDuration  ||
                        pTmpVal->m_pElement->m_bIndefiniteEnd))
                  {
                      // /Hleps Fix PR 85896: don't add pauseDur to
                      // indefinite/unresolved duration:
                      if (WAY_IN_THE_FUTURE != ulDur)
                      {
                        ulDur += lPauseDuration;

#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG_EXCL_TRACK_REMOVAL)
{
    FILE* f1 = ::fopen("c:\\smil2excl.txt", bFirstExclTrackChangeDebugOut?"w":"a+");
    bFirstExclTrackChangeDebugOut = FALSE;
    ::fprintf(f1, "At %6ld: timeVal's id = %s;\tresetting duration from %lu to %lu (delay=%lu, pauseDuration=%ld)\n",
      lCurTime, (const char*)pTmpVal->m_pElement->m_pNode->m_id,
      pTmpVal->m_pElement->m_ulDuration, ulDur, pTmpVal->m_pElement->m_ulDelay, lPauseDuration);
    ::fclose(f1);
}
#endif

                        pTmpVal->m_pElement->m_pTimelineElement->
                              resetDuration(ulDur);

                        // /Helps fix PR 86106 (all 3 versions)
                        // /Force smildocrender to HandleElements():
                        bREFSomeScheduleWasChanged = TRUE;
                      }
                      else
                      {
                        // /Fix for PR 85896: make sure core doesn't
                        // extend past the "indefinite/unresolved" time
                        // due to pause-extended track; tell core to
                        // reduce dur by curTime to keep the same end:
                        UINT32 ulCurTime =
                              lCurTime<0? 0 : (UINT32)lCurTime;
                        resetTimelineElementDuration(
                              pTmpVal->m_pElement->m_pNode->m_id,
                              ulDur - ulCurTime, ulDur);
                        // /Force smildocrender to HandleElements():
                        bREFSomeScheduleWasChanged = TRUE;
                      }
                  }
                  retval = pTmpVal->m_pElement->m_pHandler->
                        handleTrackResuming(
                        (const char*)pTmpVal->m_pElement->m_pNode->m_id,
                        (INT32)pTmpVal->m_pElement->m_pNode->m_nGroup);

#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG_EXCL_TRACK_REMOVAL)
{
    FILE* f1 = ::fopen("c:\\smil2excl.txt", bFirstExclTrackChangeDebugOut?"w":"a+");
    bFirstExclTrackChangeDebugOut = FALSE;
    ::fprintf(f1, "At %6ld: timeVal's id = %s;\thandleTrackResuming() returned 0x%08x\n",
    lCurTime, (const char*)pTmpVal->m_pElement->m_pNode->m_id, retval);
    ::fclose(f1);
}
#endif
                  if (pParent)
                  {
                      // /Helps fix PR 53175 (& 54540): resuming after
                      // another ends should cause the one that ended to
                      // disappear (if its fill="freeze" or "remove"):
                      pParent->checkChildrenFillBehavior();
                  }
                }
                else
                {
                  if (pParent)
                  {
#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG_EXCL_TRACK_REMOVAL)
{
    FILE* f1 = ::fopen("c:\\smil2excl.txt", bFirstExclTrackChangeDebugOut?"w":"a+");
    bFirstExclTrackChangeDebugOut = FALSE;
    ::fprintf(f1, "At %6ld: timeVal's id = %s;\tcalling checkChildrenFillBehavior() "
       "because someone's end has been surpassed\n",
    lCurTime, (const char*)pTmpVal->m_pElement->m_pNode->m_id);
    ::fclose(f1);
}
#endif
                      // /Make sure it has chance to be visibly removed:
                      pParent->checkChildrenFillBehavior();
                  }
                  // /Get rid of it from the list:
                  m_pPendingBeginTimeList->RemoveAt(lPosOfCurTmpVal);
                  continue;
                }
            }
            else if (lElementCurEndTime > lCurTime)
            {
                curSMILTimelineStatus =
                      SMILTimelineStatusCurrentlyPlaying;
                // /Fixes PR 62397: need to check if it's a paused track
                // being restarted; if so, remove its resume event and
                // allow its currently-playing sibling to be removed:
                if (lEffectiveResolvedTime <= lCurTime  &&
                      // /Make sure it's paused; PR 50588's par *was*
                      // passing this if() test but was not paused; it
                      // has a par with begin="0s;4s" and end > 4s:
                      pTmpVal->m_pElement->m_bIsPausedInExcl  &&
                      pTmpVal->m_pElement->m_pHandler)
                {
                  // /Resume events should have been handled above:
                  HX_ASSERT(!pTmpVal->isResumeEvent());
                  curSMILTimelineStatus =
                        SMILTimelineStatusCurrentlyPausedAndRestarting;

                  // /Next, remove the resume event from the event list
                  // since this track is restarting so its old, paused,
                  // track goes away:
                  // /Also, make sure that any paused sibling awaiting
                  // the current (restarting) one's end now points to
                  // the next-in-pause-queue element instead:
                  SmilTimeValue* pTmVlCurElementResumeEvent = NULL;
                  SmilTimeValue*
                      pTmVlPausedSiblResumesWhenCurElementEnds = NULL;
                  LISTPOSITION listPos = NULL;
                  HX_ASSERT(m_pBeginEventList);
                  if (m_pBeginEventList)
                  {
                      listPos = m_pBeginEventList->GetHeadPosition();

                      while (listPos)
                      {
                        LISTPOSITION lPosOfCurTmpTimeValue = listPos;
                        // /Gets val at lPos then moves lPos to next node:
                        SmilTimeValue* pTmpTimeValue = (SmilTimeValue*)
                              m_pBeginEventList->GetNext(listPos);
                        if (!pTmpTimeValue  ||
                              !pTmpTimeValue->m_pElement)
                        {
                            // /List shouldn't have an empty or NULL node:
                            HX_ASSERT(pTmpTimeValue  &&
                                  pTmpTimeValue->m_pElement);
                            continue;
                        }
                        if (pTmpTimeValue->m_pElement ==
                              pTmpVal->m_pElement  &&
                              pTmpTimeValue->isResumeEvent())
                        {
                            // /Get rid of it from the pending list:
                            m_pBeginEventList->RemoveAt(lPosOfCurTmpTimeValue);
                                    // Get rid of it from the map
                                    removeFromBeginOrEndTimeMap(pTmpTimeValue, SmilBeginTimeList);

                            pTmVlCurElementResumeEvent = pTmpTimeValue;
                            if (pTmVlPausedSiblResumesWhenCurElementEnds)
                            {
                              // /The one (if any) that was paused by
                              // the last play of this should instead
                              // await the resume event of the next-in-
                              // line element on the "pause queue":
                              pTmVlPausedSiblResumesWhenCurElementEnds->
                                    m_idRef = pTmpTimeValue->m_idRef;
                              break; // /We found both so we're done.
                            }
                        }
                        else if (pTmpTimeValue->m_idRef ==
                              pTmpVal->m_pElement->m_pNode->m_id  &&
                              pTmpTimeValue->isResumeEvent())
                        {
                            pTmVlPausedSiblResumesWhenCurElementEnds =
                                  pTmpTimeValue;
                            // /The one (if any) that was paused by
                            // the last play of this should now
                            // await the resume event of the next-in-
                            // line element on the "pause queue":
                            if (pTmVlCurElementResumeEvent)
                            {
                              pTmVlPausedSiblResumesWhenCurElementEnds->
                                    m_idRef =
                                    pTmVlCurElementResumeEvent->m_idRef;
                              break; // /We found both so we're done.
                            }
                        }
                      }
                  }
                }
            }
            else
            {
                curSMILTimelineStatus =
                      SMILTimelineStatusFinishedPlaying;
                bTrackIsFinishedAndFrozen = FALSE;
                if (pTmpVal->m_pElement->m_ulRemoveTime > (ULONG32)lCurTime)
                {
                  bTrackIsFinishedAndFrozen = TRUE;
                }
            }
          }
      }

#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG_EXCL_TRACK_REMOVAL)
{
      const char* pCurStatus[7] =
      {     "SMILTimelineStatusUnknown",
          "SMILTimelineStatusNeverScheduled",
          "SMILTimelineStatusStartScheduledForLater",
          "SMILTimelineStatusCurrentlyPlaying",
          "SMILTimelineStatusFinishedPlaying",
          "SMILTimelineStatusCurrentlyPaused",
          "SMILTimelineStatusCurrentlyPausedAndRestarting"
      };
      FILE* f1 = ::fopen("c:\\smil2excl.txt", bFirstExclTrackChangeDebugOut?"w":"a+");
      bFirstExclTrackChangeDebugOut = FALSE;
      ::fprintf(f1, "At %6ld: timeVal's id = %s\t "
      "curSMILTimelineStatus==%s, ulActualStartTime=%lu, ulActualStopTime=%lu, "
      "lElementCurBeginTime=%ld, lEffectiveResolvedTime=%ld, "
      "lSyncbaseDelayAddedToResolvedTime=%ld\n",
      lCurTime, (const char*)pTmpVal->m_pElement->m_pNode->m_id,
      pCurStatus[curSMILTimelineStatus],ulActualStartTime, ulActualStopTime,
      lElementCurBeginTime, lEffectiveResolvedTime,
      lSyncbaseDelayAddedToResolvedTime);
      ::fclose(f1);
}
#endif

      BOOL bIsInExcl = hasAncestor(SMILExcl, pTmpVal->m_pElement->m_pNode);
      // /If this is prior to playback, all elements in the list shuold be
      // descendants of an excl:
      if (bDoHandleExclBeforePlaybackStarts  &&  !bIsInExcl)
      {
          continue;
      }

      // /Prior to playback, only add the first excl child that has a
      // begin time of 0s (or is the one with the earliest resolved begin):
      if (bIsInExcl  &&  pExclChildAddedBeforePlayback  &&
            pExclChildAddedBeforePlayback  &&  pExclAncestor  &&
            (*pExclChildAddedBeforePlayback)[(const char*)
            pExclAncestor->m_id])
      {
          HX_ASSERT(bDoHandleExclBeforePlaybackStarts);
          continue;
      }

      // /We should be in one of these four states; anything else means
      // there's an inconsistency somewhere:
      HX_ASSERT (SMILTimelineStatusUnknown != curSMILTimelineStatus);

      LONG32 lResolvedTimeToUse =
            lEffectiveResolvedTime-lSyncbaseDelayAddedToResolvedTime;
      // /Now, see if this pending time is ready to be used:
      if (lResolvedTimeToUse <= lCurTime  ||
            // /OPTIMIZATION:
            // /If this element has never been scheduled, then go ahead
            // and schedule it with this newly-resolved (and possibly
            // future) time so core has greatest opportunity to pre-
            // buffer the element prior to commencing its playback:
            (!bIsInExcl  &&
            SMILTimelineStatusNeverScheduled == curSMILTimelineStatus)  ||
            // /OPTIMIZATION:
            // /If element is scheduled to play later and this new
            // begin time is prior to that, then use this new time
            // and give core a chance to buffer it in advance:
            (SMILTimelineStatusStartScheduledForLater ==
            curSMILTimelineStatus  &&  lResolvedTimeToUse <
            lElementCurBeginTime) )
      {
          // Using the result, we need to decide what to do with
          // this element based on:
          //  (1) If it has ever been added to the timeline,
          //  (2) If it's currently playing, paused, or stopped,
          //  (3) If its restart value is "always", "never", or
          //      "whenNotActive"
          // Note: We don't want to just start the element based on a
          // newly-resolved begin time due to this event because
          // this element's begin list might be:
          //    begin="foo.activate+5s; 10s"
          // so a click at 8s means the 10s begin will be next, not
          // the click-based begin that resolves to 13s.

          // /Handle all these cases, where 't' is a time unit and
          // "0t" is now (ulEventTime), 'rE' is resolved event time,
          // i.e., event time plus any offset, "psB" and "psE" are
          // previously-scheduled Begin and End times, respectively:
          //
          // (1) Has never played and is not yet scheduled to,
          //  and resolved event time is negative, 0, or positive
          //  offset from now:
          //  -3t  -2t   -1t     0t    1t    2t    3t
          //   +-----+-----+-----+-----+-----+-----+
          //                   \ | /
          //                     rE
          //
          // (2) Has never played but is scheduled to later, and
          //  resolved event time is prior to previously-scheduled
          //  begin:
          //  -3t  -2t   -1t     0t    1t    2t    3t
          //   +-----+-----+-----+-----+-====+====-+
          //                   \ | /     |       |
          //                     rE     psB     psE
          //
          // (3) Has never played but is scheduled to later, and
          //  resolved event time is same as previously-scheduled
          //  begin:
          //  -3t  -2t   -1t     0t    1t    2t    3t
          //   +-----+-----+-----+--===+=====+==---+
          //                        |          |
          //                      rE,psB      psE
          //
          // (4) Has never played but is scheduled to later, and
          //  resolved event time is after previously-scheduled
          //  begin and prior to previously-scheduled end:
          //  -3t  -2t   -1t     0t    1t    2t    3t
          //   +-----+-----+-----+--===+=====+====-+
          //                        |     |      |
          //                       psB    rE    psE
          //
          // (5) Has never played but is scheduled to later, and
          //  resolved event time is same as previously-scheduled end:
          //  -3t  -2t   -1t     0t    1t    2t    3t
          //   +-----+-----+-----+--===+=====+==---+
          //                        |          |
          //                       psB      psE,rE
          //
          // (6) Has never played but is scheduled to later, and
          //  resolved event time is after previously-scheduled end:
          //  -3t  -2t   -1t     0t    1t    2t    3t
          //   +-----+-----+-----+--===+====-+-----+
          //                        |      |     |
          //                       psB    psE    rE
          //
          // (7a) Is done playing, and resolved event time is negative,
          //  0, or positive offset from now, but not less than prior end:
          //  -3t  -2t   -1t     0t    1t    2t    3t
          //   +-====+====-+-----+-----+-----+-----+
          //     |       |     \ | /
          //    psB     psE      rE
          //
          // (7b) Is done playing, and resolved event time is negative
          //  offset from now and earlier than prior end:
          //  -3t  -2t   -1t     0t    1t    2t    3t
          //   +-====+====-+-----+-----+-----+-----+
          //     |  |    |
          //    psB rE  psE
          //
          // (8) Is playing now, and resolved event time is prior to
          //  previously-scheduled end time:
          //  -3t  -2t   -1t     0t    1t    2t    3t
          //   +---==+=====+=====+=====+==---+-----+
          //       |           \ | /     |
          //      psB            rE     psE
          //
          // (9) Is playing now, and resolved event time is same as
          //  previously-scheduled end time:
          //  -3t  -2t   -1t     0t    1t    2t    3t
          //   +---==+=====+=====+=====+==---+-----+
          //       |                     |
          //      psB                 psE,rE
          //
          // (10) Is playing now, and resolved event time is after
          //  previously-scheduled end time:
          //  -3t  -2t   -1t     0t    1t    2t    3t
          //   +---==+=====+=====+=====+==---+-----+
          //       |                     |      |
          //      psB                   psE     rE
          //
          //
          // Algorithm:
          // - Whenever an element becomes active (i.e., begins
          //  playback), or whenever an element's begin time becomes
          //  resolved, search its begin-time list for next resolved
          //  time and, if restart != "never", add that to the
          //  "watched element" list (in order by begin time), removing
          //  any duplicate of that same element (with a later time).
          // - In every OnTimeSync(), check head of "watched element"
          //  list and see if its time has been reached.  If not,
          //  ignore and keep going.  If so, remove it and all other
          //  elements in that list whose begin times have been reached
          //  and decide whether or not to start/restart the element
          //  based on restart value for that element.

          // /Do a clip-begin for any negative offset that was "clipped"
          // due to being resolved too late.  We can calculate the
          // clip-begin offset as the difference between the effective
          // resolved time (when it actually begins) and the resolved-to
          // time (that includes the entire negative offset):
          INT32 lResolvedToTimeWithoutOffset =
                pTmpVal->getResolvedToTimeWithoutOffset();
          INT32 lEventTimeOffset = pTmpVal->getTimeOffset();
          INT32 lTimeWithFullOffset =
                lResolvedToTimeWithoutOffset + lEventTimeOffset;

          INT32 lClippedOffsetAmount =
                lTimeWithFullOffset - lEffectiveResolvedTime;
          INT32 lEffectiveEventTimeOffset =
                lEventTimeOffset - lClippedOffsetAmount;
          if (lEventTimeOffset < 0  &&  lClippedOffsetAmount < 0)
          {
            // /If clip-begin is invalid, set it otherwise add to it:
            pTmpVal->m_pElement->m_ulClipBegin = ((UINT32)-1 ==
                  pTmpVal->m_pElement->m_ulAuthoredClipBegin?
                  (ULONG32)(-lClippedOffsetAmount) :
                  (ULONG32)(-lClippedOffsetAmount) +
                  pTmpVal->m_pElement->m_ulAuthoredClipBegin);
            // /Set lEventTimeOffset to zero for call to setDelay,
            // below:
            lEventTimeOffset = 0;
          }

          BOOL bOKToKillExclSibling = FALSE;
          BOOL bAddTrack = FALSE;
          BOOL bPrepForRestartNeeded = FALSE;



// /#define XXXEHODGE_DEBUG_CHECKPENDINGBEGINTIMES
#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG_CHECKPENDINGBEGINTIMES)
{     static BOOL bFirstTime = TRUE;
      FILE* f1 = ::fopen("c:\\smil2_checkPending.txt", bFirstTime?"w":"a+");
      ::fprintf(f1, "\npTmpVal->m_idRef= %s, LINE= %lu, "
      "curSMILTimelineStatus==SMILTimelineStatusCurrentlyPlaying?%lu, "
      "SmilRestartAlways==pTmpVal->m_pElement->m_restartBehavior%lu, "
      "lTimeWithFullOffset=%ld, lElementCurBeginTime=%ld, lEffectiveResolvedTime=%ld, "
      "lSyncbaseDelayAddedToResolvedTime=%ld, m_ulRemoveTime=%lu\n",
      (const char*)pTmpVal->m_idRef, (UINT32)__LINE__,
      curSMILTimelineStatus==SMILTimelineStatusCurrentlyPlaying,
      SmilRestartAlways==pTmpVal->m_pElement->m_restartBehavior,
      lTimeWithFullOffset, lElementCurBeginTime, lEffectiveResolvedTime,
      lSyncbaseDelayAddedToResolvedTime, lCurTime, pTmpVal->m_pElement->m_ulRemoveTime);  ::fclose(f1);}
#endif




          if (SMILTimelineStatusNeverScheduled == curSMILTimelineStatus)
          {
            bAddTrack = TRUE;
            bPrepForRestartNeeded = FALSE;
            bOKToKillExclSibling = TRUE;
          }
          // /Since we're starting earlier than when it's been scheduled,
          // restart doesn't come into play, but we have to insert that
          // future time into the pending begin time list:
          else if (SMILTimelineStatusStartScheduledForLater ==
                curSMILTimelineStatus)
          {
            // /XXXEH- OPTIMIZATION TODO: instead of
            // removeTrack+addTrack, just adjust time of
            // already-scheduled one:
            if (NULL == pTmpVal->m_pElement->m_pTimelineElement)
            {
                retval = HXR_UNEXPECTED;
                HX_ASSERT(pTmpVal->m_pElement->m_pTimelineElement);
                goto cleanup;
            }
            else
            {
                if (pTmpVal->m_pElement->m_pHandler  ||
                      bCurElementIsTimeContainer)
                {
                  // /It's scheduled for later so it must have its
                  // delay set:
                  HX_ASSERT((UINT32)-1 !=
                        pTmpVal->m_pElement->m_ulDelay);
                  LONG32 lOldScheduledBegin =
                        lElementCurBeginTime;

                  // /XXXEH- OPTIMIZATION TODO: if
                  // (SMILTimelineStatusStartScheduledForLater ==
                  // curSMILTimelineStatus) then we should not remove
                  // and then add the track back, but rather should
                  // just call begin on the track now.  This requires,
                  // however, that all datatypes can handle having
                  // their time offsets adjusted on the fly:

                  // /See if pID is a time container; if so, call
                  // handleTrackRemoval() on all its descendant media:
                  if (bCurElementIsTimeContainer)
                  {
                      retval = HXR_OK;

                      SMILNode* pChild = pTmpVal->m_pElement->
                            m_pNode->getFirstChild();
                      while (pChild)
                      {
                        CSmilElement* pElement = pChild->m_pElement;
                        if (pElement  &&  pElement->m_pHandler  &&
                              pElement->m_bInsertedIntoTimeline)
                        {
                            HX_RESULT retval2 = pElement->m_pHandler->
                                  handleTrackRemoval((const char*)
                                  pElement->m_pNode->m_id,
                                  (INT32)pElement->m_pNode->m_nGroup);
                            if (HXR_OK == retval2)
                            {
                              // /The duration changed due to an
                              // event, not due to clipping by
                              // our sync-parent.  Set this to
                              // false so elementResolved() will
                              // allow sync-arcs to resolve on
                              // our new end:
                              pElement->m_bCurEndClippedByParent =
                                    FALSE;
                              // /Fixes case where element has
                              // sync arc to this end time and
                              // we need to notify it that
                              // we've ended early:
                                        m_pTimelineElementManager->notify((const char*) pElement->m_pNode->m_id);
                            }
                        }
                        pChild = pTmpVal->m_pElement->m_pNode->
                              getNextChild();
                      }
                  }
                  else
                  {
                      retval = pTmpVal->m_pElement->m_pHandler->
                            handleTrackRemoval(
                            (const char*)pTmpVal->
                            m_pElement->m_pNode->m_id,
                            (INT32)pTmpVal->
                            m_pElement->m_pNode->m_nGroup);
                  }

                  if (HXR_OK == retval)
                  {
                      // /The duration changed due to an event, not due
                      // to clipping by our sync-parent.  Set this to
                      // false so elementResolved() will allow
                      // sync-arcs to resolve on our new end:
                      pTmpVal->m_pElement->m_bCurEndClippedByParent =
                            FALSE;

                      // /Fixes case where element has a sync arc to
                      // this end time and we need to notify it that
                      //  we've ended early:
                      m_pTimelineElementManager->notify((const char*) pTmpVal->m_pElement->m_pNode->m_id);

                      // /Place previously-scheduled future time into
                      // pending-time list since it's been superseded
                      // by pTmpVal:
                      retval = insertElementWithPendingBeginOrEnd(
                            pTmpVal->m_pElement, lOldScheduledBegin,
                            SmilBeginTimeList);
                  }
                }
                else
                {
                        // XXXMEH - removed since we sometimes get multiple
                        // char events, before the events have even been
                        // processed.
//                HX_ASSERT(pTmpVal->m_pElement->m_pHandler);
                  retval = HXR_UNEXPECTED;
                }
            }
            bAddTrack = TRUE;
            bPrepForRestartNeeded = TRUE;
            bOKToKillExclSibling = TRUE;
          }
          // /If we're playing now and restart is "always" and our new
          // begin time is not in the future and is not so far in the
          // past that it was earlier than the currently-playing time,
          // OR if we're done playing and restart is not "never", then
          // remove the track and add it back with new begin/end time:
          else if (((SMILTimelineStatusCurrentlyPlaying ==
                curSMILTimelineStatus  ||
                // /Part of fix for PR 62397:
                SMILTimelineStatusCurrentlyPausedAndRestarting ==
                curSMILTimelineStatus)  &&
                SmilRestartAlways ==
                pTmpVal->m_pElement->m_restartBehavior  &&
                (lTimeWithFullOffset >=
                lElementCurBeginTime  &&
                lEffectiveResolvedTime <= lCurTime))  ||
                (SMILTimelineStatusFinishedPlaying ==
                curSMILTimelineStatus  &&  SmilRestartNever !=
                pTmpVal->m_pElement->m_restartBehavior) )
          {
#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG)
{FILE* f1 = ::fopen("c:\\smil2.txt", "a+"); ::fprintf(f1, "\npTmpVal->m_idRef= %s, LINE= %lu\n", (const char*)pTmpVal->m_idRef, (UINT32)__LINE__);  ::fclose(f1);}
#endif

            // /If we're finished playing, handle killing an excl
            // sibling that's active, if any.  (Note: if we're not
            // finished playing, then we are the active one so no
            // need to shut anyone else down):
            // (..but note: we're not the active one if we're paused)
            if (SMILTimelineStatusFinishedPlaying ==
                  curSMILTimelineStatus)
            {
                bOKToKillExclSibling = TRUE;
            }
            // /Last part of fix for PR 62397:
            else if (SMILTimelineStatusCurrentlyPausedAndRestarting ==
                  curSMILTimelineStatus  &&
                  !pTmpVal->isResumeEvent())
            {
                bOKToKillExclSibling = TRUE;
            }

            // /XXXEH- OPTIMIZATION TODO(?): instead of
            // removeTrack+addTrack, just adjust time of
            // already-playing one (?):
            if (NULL == pTmpVal->m_pElement->m_pTimelineElement)
            {
                retval = HXR_UNEXPECTED;
                HX_ASSERT(pTmpVal->m_pElement->m_pTimelineElement);
                goto cleanup;
            }
            else
            {
                if ((pTmpVal->m_pElement->m_pHandler  ||
                      bCurElementIsTimeContainer)  &&
                      // /(for PR 62688 and others): don't remove
                      // track(s) that are done playing:
                      (SMILTimelineStatusFinishedPlaying !=
                      curSMILTimelineStatus  ||
                      // /Fixes PR 66212, which broke when PR 62688
                      // fix, above, was added: we do need to remove a
                      // track that is finished but *frozen* (and
                      // thus track/renderer is still open):
                      bTrackIsFinishedAndFrozen) )
                {
                  // /See if pID is a time container; if so, call
                  // handleTrackRemoval() on all its descendant media:
                  if (bCurElementIsTimeContainer)
                  {
                      retval = HXR_OK;

                      SMILNode* pChild = pTmpVal->m_pElement->
                            m_pNode->getFirstChild();
                      while (pChild)
                      {
                        CSmilElement* pElement = pChild->m_pElement;
                        if (pElement  &&  pElement->m_pHandler  &&
                              pElement->m_bInsertedIntoTimeline)
                        {
                            HX_RESULT retval2 = pElement->m_pHandler->
                                  handleTrackRemoval((const char*)
                                  pElement->m_pNode->m_id,
                                  (INT32)pElement->m_pNode->m_nGroup);
                            if (HXR_OK == retval2)
                            {
                              // /The duration changed due to an
                              // event, not due to clipping by
                              // our sync-parent.  Set this to
                              // false so elementResolved() will
                              // allow sync-arcs to resolve on
                              // our new end:
                              pElement->m_bCurEndClippedByParent =
                                    FALSE;
                              // /Fixes case where element has
                              // sync arc to this end time and
                              // we need to notify it that
                              // we've ended early:
                              m_pTimelineElementManager->notify((const char*) pElement->m_pNode->m_id);
                            }
                        }
                        pChild = pTmpVal->m_pElement->m_pNode->
                              getNextChild();
                      }
                  }
                  else
                  {
                      retval = pTmpVal->m_pElement->m_pHandler->
                            handleTrackRemoval(
                            (const char*)pTmpVal->
                            m_pElement->m_pNode->m_id,
                            (INT32)pTmpVal->
                            m_pElement->m_pNode->m_nGroup);
                  }

                  // /Fixes case where element has a sync arc to this
                  // end time and we need to notify it that we've
                  // ended early:
                  if (HXR_OK == retval)
                  {
                      // /The duration changed due to an event, not due
                      // to clipping by our sync-parent.  Set this to
                      // false so elementResolved() will allow
                      // sync-arcs to resolve on our new end:
                      pTmpVal->m_pElement->m_bCurEndClippedByParent =
                            FALSE;

                      m_pTimelineElementManager->notify((const char*) pTmpVal->m_pElement->m_pNode->m_id);
                  }
                  retval = HXR_OK;
                  bAddTrack = TRUE;
                  bPrepForRestartNeeded = TRUE;
                }
                // /Fixes elements-won't-restart-in-excl bug introduced by
                // initial PR 62688 fix; if it's done playing, allow it to
                // restart even though we didn't (have to) remove it, above:
                else if (SMILTimelineStatusFinishedPlaying ==
                      curSMILTimelineStatus)
                {
                  retval = HXR_OK;
                  bAddTrack = TRUE;
                  bPrepForRestartNeeded = TRUE;
                }
                else
                {
                        // XXXMEH - removed since we sometimes get multiple
                        // char events, before the events have even been
                        // processed.
//                HX_ASSERT(pTmpVal->m_pElement->m_pHandler);
                  retval = HXR_UNEXPECTED;
                }
            }
          }

          BOOL bTimeValWasDeferred = FALSE;
          if (bOKToKillExclSibling)
          {
            // /This finds the first actively-playing media element of
            // the excl (if there even is an excl), noting that there may
            // be more than one media element playing in the case where
            // they are all children of a par descendant of the excl:
            SMILNode* pActiveSibling = findActiveChildOfAncestorExcl(
                  pTmpVal->m_pElement->m_pNode, lCurTime);
#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG_EXCL_TRACK_REMOVAL)
{
    FILE* f1 = ::fopen("c:\\smil2excl.txt", bFirstExclTrackChangeDebugOut?"w":"a+");
    bFirstExclTrackChangeDebugOut = FALSE;
    ::fprintf(f1, "At %6ld: timeVal's id = %s, "
      "findActiveChildOfAncestorExcl() found: pActiveSibling id=%s  \n",
      lCurTime, (const char*)pTmpVal->m_pElement->m_pNode->m_id, pActiveSibling?
      (const char*)pActiveSibling->m_id:"NULL");
    ::fclose(f1);
}
#endif
            if (pActiveSibling)
            {
                // /Is currently playing (unless it was stopped
                // already by someone else) so kill it:
                if (NULL != pActiveSibling->m_pElement->m_pHandler)
                {
                  SMILPriorityClassPeersHigherLowerVal interruptAction=
                        SMILPriorityClassStop;
                  SMILPriorityClassPauseDisplay
                        activeSiblingPauseDisplay =
                        SMILPriorityClassPauseDisplayShow;
                  // /Decide based on priorityClass values what to do
                  // here; e.g., if this is a peer of the new media that
                  // wants to play, then the peers value will determine
                  // whether we stop, pause, or do nothing to it:
                  SMILNode* pPriorityClassOfActiveSibling =
                        getSpecificAncestor(SMILPriorityClass,
                        pActiveSibling);
                  if (pPriorityClassOfActiveSibling)
                  {
                      // /Find priorityClass of interrupting media:
                      SMILNode* pPriorityClassOfInterrupter =
                            getSpecificAncestor(SMILPriorityClass,
                            pTmpVal->m_pElement->m_pNode);
                      HX_ASSERT(pPriorityClassOfInterrupter);
                      if (!pPriorityClassOfInterrupter  ||
                            !pPriorityClassOfInterrupter->m_pElement)
                      {
                        // /XXXEH- fire off this error: all media in
                        // an excl must be inside a priorityClass if
                        // *any* other media in that excl is inside
                        // a priorityClass:
                        break;
                      }
                      CSmilPriorityClassElement* pPCActiveElem =
                           (CSmilPriorityClassElement*)
                           pPriorityClassOfActiveSibling->m_pElement;
                      HX_ASSERT(pPCActiveElem);
                      activeSiblingPauseDisplay =
                            pPCActiveElem->m_pauseDisplay;
                      // /If they have the same priorityClass, then
                      // look at the peers behavior to see what to do:
                      if (pPriorityClassOfInterrupter ==
                            pPriorityClassOfActiveSibling)
                      {
                        interruptAction = pPCActiveElem->m_peers;
                      }
                      // /Else, if interrupting p.c. is higher (i.e.,
                      // declared (lexically) earlier in the excl),
                      // look at active p.c.'s "higher" val:
                      else if (pPriorityClassOfInterrupter->
                            m_ulTagStartLine <
                            pPriorityClassOfActiveSibling->
                            m_ulTagStartLine  ||
                            (pPriorityClassOfInterrupter->
                            m_ulTagStartLine ==
                            pPriorityClassOfActiveSibling->
                            m_ulTagStartLine  &&
                            pPriorityClassOfInterrupter->
                            m_ulTagStartColumn <
                            pPriorityClassOfActiveSibling->
                            m_ulTagStartColumn) )
                      {
                        interruptAction = pPCActiveElem->m_higher;
                      }
                      // /Else, if interrupting p.c. is lower (i.e.,
                      // declared (lexically) later in the excl), look
                      // at active p.c.'s "lower" val:
                      else
                      {
                        interruptAction = pPCActiveElem->m_lower;
                      }
                  }

                  // /This is active node's "syncbase" element
                  // which may itself be a child of this excl:
                  SMILNode* pTimelineParentOfActiveSibling =
                        getSyncAncestor(pActiveSibling);
                  HX_ASSERT(pTimelineParentOfActiveSibling);
                  // /Is needed to fix PR 57120 (wrapping par version);
                  // in case of a deferral, it's the *interruptor* that
                  // defers, not the active sibling (unlike with pause):
                  SMILNode* pTimelineParentOfInterruptor =
                        getSyncAncestor(pTmpVal->m_pElement->m_pNode);
                  HX_ASSERT(pTimelineParentOfInterruptor);

                  // /Here's where we need to reset m_bIsDeferredInExcl
                  // to FALSE if a deferred element is now getting a
                  // chance to play (and not be further deferred):
                  if (pTmpVal->m_pElement->m_bIsDeferredInExcl  &&
                        interruptAction !=SMILPriorityClassDefer)
                  {
                      pTmpVal->m_pElement->m_bIsDeferredInExcl = FALSE;
                      pTmpVal->m_pElement->m_ulTimeDeferralOccurred = (UINT32)-1;
                  }
                  switch (interruptAction)
                  {
                      case SMILPriorityClassPause:
                      {
                        do
                        {
                            if (SMILPriorityClassPauseDisplayHide ==
                                  activeSiblingPauseDisplay)
                            {
                              // /There are some cases where this
                              // might be NULL, but we shouldn't
                              // get to this part of this function
                              // if it's NULL:
                              HX_ASSERT(pPauseDisplayHideElementList);
                              if (pPauseDisplayHideElementList)
                              {
                                  CHXString* pCStr = new CHXString;
                                  if (pCStr)
                                  {
                                    // /copy it over:
                                    *pCStr = (const char*)
                                          pActiveSibling->m_id;
                                    pPauseDisplayHideElementList->
                                          AddTail(pCStr);
                                  }
                              }
                            }
                            else if (SMILPriorityClassPauseDisplayDisable ==
                                  activeSiblingPauseDisplay)
                            {

                              // /There are some cases where this
                              // might be NULL, but we shouldn't
                              // get to this part of this function
                              // if it's NULL:
                              HX_ASSERT(pPauseDisplayDisableElementList);
                              if (pPauseDisplayDisableElementList)
                              {
                                  CHXString* pCStr = new CHXString;
                                  if (pCStr)
                                  {
                                    // /copy it over:
                                    *pCStr = (const char*)
                                          pActiveSibling->m_id;
                                    pPauseDisplayDisableElementList->
                                          AddTail(pCStr);
                                  }
                              }
                            }

                            LONG32 lDiffBetwnNowAndBegin =
                                  lCurTime-lEffectiveResolvedTime;

                            if (0==lEffectiveResolvedTime  &&
                                  lDiffBetwnNowAndBegin<=10)
                            {
                              // /Fixes SMIL 2.0 Interop Timing #13.15:
                              // allow first track to draw once by
                              // interrupting in a subsequent call to
                              // checkPending...():
                              bTimeValWasDeferred = TRUE;
                              goto doneWithRestartCode;
                            }

                            HX_ASSERT(lDiffBetwnNowAndBegin>0);
                            if (lDiffBetwnNowAndBegin > 0)
                            {
                              // /XXXEH- TODO: if and when
                              // we stop treating scheduled
                              // interrupts as events, this
                              // will fire off and we'll need to
                              // delay the subsequent pause, below:
                              HX_ASSERT(lDiffBetwnNowAndBegin<500);
                            }

                            // /Adding this block helps fix PR 86107.
                            // We need to adjust the duration of this
                            // element to reflect the anticipated time
                            // it will be paused.  That allows other
                            // elements that play after it ends to
                            // adjust accordingly before it's too late:
                                    // /Adding the if() around the block helps
                                    // fix PR 100601 & PR 85896 (=PR 79288):
                                    // /BUT, don't do this if duration of pauser
                                    // isn't resolved:
                                    // [XXXEH- we may need to set up a resolve-
                                    // this-when-trackDurationSet-callback-
                                    // happens construct (as is done in
                                    // elementResolved()) and do the following
                                    // then, when duration becomes resolved,
                                    // but I can't create content that breaks
                                    // without that]:
                                    if ((UINT32)-1 !=
                                            pTmpVal->m_pElement->m_ulDuration)
                            {
                             pActiveSibling->m_pElement->
                                  m_ulAnticipatedPauseDur =
                                  pTmpVal->m_pElement->m_ulDuration;

                             ULONG32 ulAnticipatedDurInclPause =
                                  pActiveSibling->m_pElement->
                                  m_ulAnticipatedPauseDur +
                                  pActiveSibling->m_pElement->
                                  m_ulDuration;

                             if (pActiveSibling->m_pElement->
                                  m_ulAnticipatedPauseDur >=
                                  WAY_IN_THE_FUTURE  ||
                                  pActiveSibling->m_pElement->
                                  m_ulDuration >=
                                  WAY_IN_THE_FUTURE)
                             {
                              ulAnticipatedDurInclPause =
                                    WAY_IN_THE_FUTURE -
                                                // /Helps fix PR 100601 and
                                                // PR 85896 (=PR 79288):
                                    // /Subtract delay so total
                                    // reported to core doesn't
                                    // exceed indef "magic number":
                                    pActiveSibling->m_pElement->m_ulDelay;
                             }

                             durationResolved((const char*)
                                  pActiveSibling->m_pElement->
                                   m_pNode->m_id,
                                  ulAnticipatedDurInclPause,
                                  FALSE /* not being set by parent */,
                                  TRUE /* dur is pause extending */);

                             // /Force smildocrender to HandleElements():
                             bREFSomeScheduleWasChanged = TRUE;

                            } // /End block added to help fix PR 86107.


                            // /This method not only pauses the
                            // element but also creates a new
                            // SmilTimeValue and inserts it in the
                            // pending begin time list; this new
                            // time value watches for the
                            // iterrupting media to finish:
                            SmilTimeValue* pReturnedTimeVal = NULL;
                            retval = pActiveSibling->m_pElement->
                                  m_pHandler->handleTrackPausing(
                                  pActiveSibling,
                                  lEffectiveResolvedTime,
                                  activeSiblingPauseDisplay,
                                  pTmpVal->m_pElement->m_pNode->m_id);

#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG_EXCL_TRACK_REMOVAL)
{
    FILE* f1 = ::fopen("c:\\smil2excl.txt", bFirstExclTrackChangeDebugOut?"w":"a+");
    bFirstExclTrackChangeDebugOut = FALSE;
    ::fprintf(f1, "At %6ld: timeVal's id = %s, "
      "handleTrackPausing(%s) returned: 0x%08x\n",
      lCurTime, (const char*)pTmpVal->m_pElement->m_pNode->m_id,
      (const char*)pActiveSibling->m_id, retval);
    ::fclose(f1);
}
#endif
                            HX_ASSERT(HXR_OK == retval);
                            // /Helps fix PR 53531: this will prevent
                            // this one from being seen as the current
                            // "active" sibling if another excl track
                            // gets added while this one is paused; it
                            // also resets some vars to prolong its
                            // existence during pause past its original
                            // end time if that end is not explicit:
                            if (HXR_OK == retval)
                            {
                              pActiveSibling->m_pElement->
                                    prepForPauseInExcl(
                                    lEffectiveResolvedTime);
                            }

                            if (pTimelineParentOfActiveSibling  &&
                                  SMILExcl !=
                                  pTimelineParentOfActiveSibling->m_tag)
                            {
                              // /Find next timeline sibling:
                              do
                              {
                                  pActiveSibling = findNextSibling(
                                        pActiveSibling);
                              } while (pActiveSibling  &&
                                  // /XXXEH- TODO: handle deeper
                                  // nesting of timed objects:
                                  !isMediaObject(pActiveSibling));
                            }
                            else // /No parent par or seq:
                            {
                              break;
                            }
                        } while (pActiveSibling);
                      }
                      break;

                      case SMILPriorityClassDefer:
                      {
                        // /XXXEH- TODO: determine if there is more
                        // than one currently-playing track:
                        // /We need to set the begin time of the
                        // would-be interrupter to that of the
                        // currently-playing one(s); if there is
                        // more than one media track playing (in a
                        // par) then we want to defer until the
                        // end of the par:

                        SMILNode* pDeferUntilAfterThisNode = pActiveSibling;
                        // /To fix PR 85885 where multiple clips get
                        // deferred while first sibling plays, we need
                        // to defer the second-deferred one to after
                        // its deferred sibling ends, not to after the
                        // first one ends:
                        SMILNode* pLastDeferredInSameExcl =
                              findLastDeferredChildOfAncestorExcl(
                              pTmpVal->m_pElement->m_pNode, lCurTime);

                        ULONG32 ulNewStartTime = pDeferUntilAfterThisNode->
                              m_pElement->m_ulDuration +
                              pDeferUntilAfterThisNode->m_pElement->m_ulDelay;

                        // /Fixes PR 69638: active sib's duration can
                        // include its delay so end is not always
                        // dur+delay; getCurrent...() handles this:
                        ULONG32 ulTotalDelay = 0;
                        if (HXR_OK == pDeferUntilAfterThisNode->m_pElement->
                              getCurrentScheduledStopTime(ulTotalDelay))
                        {
                            ulNewStartTime = ulTotalDelay;
                        }
                        else
                        {
                            HX_ASSERT(0); // /getCurrent...() failed!
                        }

                        // /lEffectiveResolvedTime is our current begin
                        // time, so we need to add the difference;
                        INT32 lDiff = (INT32)ulNewStartTime -
                              lEffectiveResolvedTime;

                        // /For PR 85885, just set defer-to time to indef and
                        // await endEvent of prior-in-queue deferred element:
                        if (pLastDeferredInSameExcl)
                        {
                            HX_ASSERT(1); // /debug only.
                            pDeferUntilAfterThisNode = pLastDeferredInSameExcl;
                            ulNewStartTime = SMILTIME_DEFERRED_INDEFINITELY;

                            // /Helps fix PR 85885:
                            // /Ceate a new SmilTimeValue watching for
                            // "undeferEvent" on prior-in-defer-queue element;
                            SmilTimeValue* pNewTimeVal =
                                  new SmilTimeValue(m_pContext,
                                  /* start line don't care:*/ 0,
                                  pTmpVal->m_pElement);
                            HX_ASSERT(pNewTimeVal);
                            if(pNewTimeVal)
                            {
                              CHXString pStr =
                                    pLastDeferredInSameExcl->m_id;
                              pStr += ".undeferEvent";
                              HX_RESULT pnr1 = pNewTimeVal->parseValue(
                                    pStr, SMILSyncAttrBegin,
                                    (const char*)
                                    pTmpVal->m_pElement->m_pNode->m_id);

                              // /Note: this function gets rid of any
                              // existing resumeEvent on this element
                              // since it can only be in the "defer
                              // stack" once:
                              BOOL bOldUndeferEventWasRemoved = FALSE;
                              HX_RESULT rslt = addUndeferEvent(
                                    pNewTimeVal,
                                    bOldUndeferEventWasRemoved);
                            }
                        }

                        // /If par in excl is deferring, then mark it
                        // as such so it won't be seen as
                        // "currently playing" next time:
                        if (pTimelineParentOfInterruptor  &&
                              SMILExcl !=
                              pTimelineParentOfInterruptor->m_tag  &&
                              pTimelineParentOfInterruptor->m_pElement)
                        {
                            pTimelineParentOfInterruptor->m_pElement->
                                  prepForDeferralInExcl((UINT32)lCurTime);
                        }
                        // /This is needed to fix PR 57120; it's the
                        // *interruptor* that defers, not the active
                        // sibling (unlike with pause):
                        pTmpVal->m_pElement->prepForDeferralInExcl((UINT32)lCurTime);
                        HX_ASSERT(lDiff >=
                              FUDGE_FACTOR_WHEN_DEFERRING_PAST_END_OF_GROUP);
                        if (lDiff >
                              FUDGE_FACTOR_WHEN_DEFERRING_PAST_END_OF_GROUP)
                        {
                            // /Only handle this by the early fudge
                            // factor if needed; if presentation is
                            // already long enough, no need to do this:
                            UINT32 ulCurGroupDuration = 0;
                            pActiveSibling->m_pElement->m_pHandler->
                              GetCurGroupDuration(ulCurGroupDuration);
                            if (ulNewStartTime +
                                  FUDGE_FACTOR_FOR_DEFERRING_PRIOR_TO_END_OF_GROUP
                                  >= ulCurGroupDuration  &&
                                  SMILTIME_DEFERRED_INDEFINITELY !=
                                  ulNewStartTime  &&
                                  // /If it's 0, then presentation dur
                                  // is unresolved (indefinite):
                                  ulCurGroupDuration !=0)
                            {
                              // /Fixes Interop Timing #25.6:
                              // Subtract fudge factor so this has a
                              // chance to begin before player stops:
                              ulNewStartTime -=
                                    FUDGE_FACTOR_WHEN_DEFERRING_PAST_END_OF_GROUP;
                            }
                            else if (SMILTIME_DEFERRED_INDEFINITELY !=
                                  ulNewStartTime)
                            {
                              // /To fix PR 69671, we *add* fudge
                              // factor so any track that gets paused
                              // by the active one can resume before
                              // this deferred (lower) tries to begin:
                              ulNewStartTime +=
                                    FUDGE_FACTOR_FOR_DEFERRING_PRIOR_TO_END_OF_GROUP;
                            }
                            if (pTmpVal->deferUntil(
                                  (INT32)ulNewStartTime))
                            {
                              bTimeValWasDeferred = TRUE;
                              bAddTrack = FALSE;
                            }
                        }
                      }
                      break;

                      case SMILPriorityClassNever:
                      {
                        ; // /Don't need to do diddly, and we should
                        // prevent the new track from playing:
                        bAddTrack = FALSE;
                        bREFSomeScheduleWasChanged = FALSE;
                      }
                      break;

                      case SMILPriorityClassStop:
                      default:
                      {
#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG_EXCL_TRACK_REMOVAL)
{
    FILE* f1 = ::fopen("c:\\smil2excl.txt", bFirstExclTrackChangeDebugOut?"w":"a+");
    bFirstExclTrackChangeDebugOut = FALSE;
    ::fprintf(f1, "At %6ld: timeVal's id = %s, pActiveSibling id=%s, "
          "bPrepForRestartNeeded=%lu  \n",
          lCurTime, (const char*)pTmpVal->m_pElement->m_pNode->m_id,
          (const char*)pActiveSibling->m_id, (UINT32)bPrepForRestartNeeded);
    ::fclose(f1);
}
#endif
                        // /Helps fix PR 56053: if par in excl is
                        // stopping, then mark it as such so it won't
                        // be seen as "currently playing" next time:
                        if (pTimelineParentOfActiveSibling  &&
                              SMILExcl !=
                              pTimelineParentOfActiveSibling->m_tag  &&
                              pTimelineParentOfActiveSibling->m_pElement)
                        {
                            pTimelineParentOfActiveSibling->m_pElement->
                                  prepForStopInExcl(lCurTime);
                        }
                        do
                        {
                            retval = pActiveSibling->m_pElement->
                                  m_pHandler->handleTrackRemoval(
                                  (const char*)pActiveSibling->m_id,
                                  (INT32)pActiveSibling->m_nGroup);
#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG_EXCL_TRACK_REMOVAL)
{
    FILE* f1 = ::fopen("c:\\smil2excl.txt", bFirstExclTrackChangeDebugOut?"w":"a+");
    bFirstExclTrackChangeDebugOut = FALSE;
    ::fprintf(f1, "At %6ld: timeVal's id = %s, pActiveSibling id=%s, "
          "handleTrackRemoval() retval=0x%08x  \n",
          lCurTime, (const char*)pTmpVal->m_pElement->m_pNode->m_id,
          (const char*)pActiveSibling->m_id, retval);
    ::fclose(f1);
}
#endif
                            // /Helps fix PR 56053: if child in excl
                            // is stopping, then mark it as such so
                            // it won't be seen as "currently
                            // playing" next time:
                            pActiveSibling->m_pElement->
                                  prepForStopInExcl(lCurTime);
                            // /Needed for where element has a sync
                            // arc to this end time and we need to
                            // notify it that it has ended early:
                            if (HXR_OK == retval)
                            {
                              // /Set this to false so elementResolved
                              // will allow sync-arcs to resolve on
                              // our new end:
                              pTmpVal->m_pElement->m_bCurEndClippedByParent =
                                    FALSE;

                              m_pTimelineElementManager->notify((const char*) pActiveSibling->m_id);
                            }
                            if (pTimelineParentOfActiveSibling  &&
                                  SMILExcl !=
                                  pTimelineParentOfActiveSibling->m_tag)
                            {
                              // /Find next timeline sibling:
                              do
                              {
                                  pActiveSibling = findNextSibling(
                                        pActiveSibling);
                              } while (pActiveSibling  &&
                                  // /XXXEH- TODO: handle deeper
                                  // nesting of timed objects:
                                  !isMediaObject(pActiveSibling));
                            }
                            else // /No parent par or seq:
                            {
                              break;
                            }
                        } while (pActiveSibling);
                        bREFSomeScheduleWasChanged = TRUE;
                      }
                      break;
                  }

                }

            }
          }
#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG)
{FILE* f1 = ::fopen("c:\\smil2.txt", "a+"); ::fprintf(f1, "\npTmpVal->m_idRef= %s, bAddTrack= %lu\n", (const char*)pTmpVal->m_idRef, (UINT32)bAddTrack);  ::fclose(f1);}
#endif
          if (bAddTrack)
          {
            // /We should never add a track that was deferred:
            HX_ASSERT(!bTimeValWasDeferred);
            // /Start the element @ NOW+offset by inserting into the
            // timeline (via setDelay()) and adding track (via
            // handleNextElement())

            // /First, see if there is a resolved end time
            // that's after this begin time, that might affect the dur;
            // if not, then play until its syncBase ends if
            // it is restarting, otherwise just use its originalDur:
            SmilTimeValue* pNextResolvedEndTimeValue = NULL;
            HX_RESULT rettimeval = HXR_OK;
            rettimeval = pTmpVal->m_pElement->getNextResolvedTimeValue(
                  pNextResolvedEndTimeValue,
                  // /Use this since we're not sure what group time is:
                  SMILTIME_NEGATIVE_INFINITY,
                  lEffectiveResolvedTime,
                  SmilEndTimeList,
                  /* Don't need list of resolved times:*/ NULL);

            LONG32 lNextResolvedEndTime;
            HX_RESULT rtvalx = HXR_FAIL;
            if (SUCCEEDED(rettimeval)  &&
                  NULL != pNextResolvedEndTimeValue)
            {
                rtvalx = pNextResolvedEndTimeValue->
                      getEffectiveResolvedTime(
                      lNextResolvedEndTime);
            }
            // /Fixes PR 50589: check for toggle behavior if
            // begin and end times are the same and pTmpVal has same
            // timeValue values as pNextResolvedEndTimeValue, then
            // mark pNextResolvedEndTimeValue so it won't be used as an
            // end time, and get next-next resolved end
            if (HXR_OK == rtvalx  &&
                  lEffectiveResolvedTime == lNextResolvedEndTime  &&
                  pNextResolvedEndTimeValue  &&
                  pTmpVal->isSameTimeValue(pNextResolvedEndTimeValue))
            {
                pTimeValEndToggleToBeIgnored =
                      pNextResolvedEndTimeValue;
                // /Get next one:
                rettimeval =
                      pTmpVal->m_pElement->getNextResolvedTimeValue(
                      pNextResolvedEndTimeValue,
                      // /Use this since we're not sure what group time is:
                      SMILTIME_NEGATIVE_INFINITY,
                      lEffectiveResolvedTime + 1, SmilEndTimeList,
                      /* Don't need list of resolved times:*/ NULL);
            }

            BOOL bHasFutureResolvedEnd = FALSE;
            // /In case we're restarting, we need to either have an
            // explicit dur or a future end that's either in the future
            // because it's unresolved or is resolved to a future time:
            // If it's playing for the first time, then it has a default
            // dur/end in lieu of an explicit one:
            BOOL bIsntRestartingOrHasExplicitDurOrFutureEnd =
                  !bPrepForRestartNeeded;
            if (SUCCEEDED(rettimeval)  &&
                  NULL != pNextResolvedEndTimeValue)
            {
                HX_RESULT rtval = pNextResolvedEndTimeValue->
                      getEffectiveResolvedTime(
                      lNextResolvedEndTime);
                if (SUCCEEDED(rtval))
                {
                  bHasFutureResolvedEnd = TRUE;
                  bIsntRestartingOrHasExplicitDurOrFutureEnd = TRUE;
                }
            }

            ULONG32 ulNewDuration = (bPrepForRestartNeeded?
                  (UINT32)-1:
                  pTmpVal->m_pElement->m_ulOriginalDuration);
            BOOL bHasExplicitValidDur = FALSE;
            // /If has explicitly authored simple dur, use it instead:
            if (pTmpVal->m_pElement->m_bHasExplicitDur  &&
                  (UINT32)-1 != pTmpVal->m_pElement->m_ulAuthoredDur)
            {
                bHasExplicitValidDur = TRUE;
                ulNewDuration = pTmpVal->m_pElement->m_ulAuthoredDur;
                bIsntRestartingOrHasExplicitDurOrFutureEnd = TRUE;
            }

            SMILNode* pSyncNode = NULL;

            if (bHasFutureResolvedEnd)
            {
                HX_ASSERT(lEffectiveResolvedTime < lNextResolvedEndTime);
                // /We need to use the new end time and ignore the
                // m_ulOriginalDuration:
                if (lEffectiveResolvedTime < lNextResolvedEndTime)
                {
                  ulNewDuration = (UINT32)(lNextResolvedEndTime -
                        lEffectiveResolvedTime);
                }
            }
            // /If not restarting and has no future end time and has no
            // explcitly-authored dur, and has an explicit end time that
            // is now or in the past, then don't insert this.
            // Fixes SMIL 2.0 Interop Timing #18.8 endsync_first_par5_RM:
            else if (!bPrepForRestartNeeded  &&  !bHasExplicitValidDur  &&
                  (pTmpVal->m_pElement->m_bHasExplicitEnd  &&
                  // /Need to make sure explicit end is in the past:
                  pTmpVal->m_pElement->m_lEndOffset <= lCurTime)  &&
                  // /Allow for begin="[some event]" end="indefinite":
                  !pTmpVal->m_pElement->m_bIndefiniteEnd  &&
                  // /Adding this check helps fix PR 52006: if there is an
                  // end value that is not yet resolved, we want to treat
                  // its end as indefinite which is a valid future end time:
                  !pTmpVal->m_pElement->hasUnresolvedEndTime())
            {
                goto doneWithRestartCode;
            }

            // /First, see if we need to look for a future unresolved end
            // in case we're restarting, have no explicit dur, and have
            // an explicit end but none that's in the future.  According
            // to "getNextInterval" in the SMIL 2.0 Timing draft, we
            // should restart only in this case if there's an event-based
            // end:
            if (!bIsntRestartingOrHasExplicitDurOrFutureEnd)
            {
                if (!pTmpVal->m_pElement->m_bHasExplicitDur  &&
                      !pTmpVal->m_pElement->m_bHasExplicitEnd)
                {
                  bIsntRestartingOrHasExplicitDurOrFutureEnd = TRUE;
                }
                else if (pTmpVal->m_pElement->hasUnresolvedEndTime())
                {
                  bIsntRestartingOrHasExplicitDurOrFutureEnd = TRUE;
                }
            }

            // /We shouldn't restart because we're beyond our end time(s)
            if (!bIsntRestartingOrHasExplicitDurOrFutureEnd)
            {
                // /XXXEH- How do we handle the following at 10s?:
                // <par begin="0s;10s" end="5s" endsync="last">,,,</par>.
                // It has no valid end after 10s, but it does have an
                // endsync and the SMIL 2.0 Timing draft doesn't say if
                // that counts like a dur would have.

                goto doneWithRestartCode;
            }

            // /This is needed for PR 63650 fix:
            ULONG32 ulMaxDuration = (UINT32)-1;

            // /This is needed to keep us from extending past our
            // sync-base's end, (if there is an explicit end):
            pSyncNode = getSyncAncestor(pTmpVal->m_pElement->m_pNode);
            ULONG32 ulMaxEndTime = (UINT32)-1;
            if (pSyncNode  &&  pSyncNode->m_pElement)
            {
                ULONG32 ulSyncBaseDuration = (UINT32)-1;
                ULONG32 ulSyncBaseDelay = 0;
                if ((UINT32)-1 != pSyncNode->m_pElement->m_ulDelay)
                {
                  ulSyncBaseDelay =
                        pSyncNode->m_pElement->m_ulDelay;
                }
                if ((UINT32)-1 != pSyncNode->m_pElement->m_ulDuration
                      &&  pSyncNode->m_pElement->
                      m_pTimelineElement->durationSet())
                {
                  ulSyncBaseDuration =
                        pSyncNode->m_pElement->m_ulDuration;
                }

                // /For final PR 62688 fix (par & excl versions):
                BOOL bEndsyncIsAllAndAllAreDone =
                      pSyncNode->m_pElement->m_nEndsyncEventSourceTag ==
                      SMILEventSourceAll  &&
                      pSyncNode->m_pElement->m_pTimelineElement->
                      getNumChildren() >=
                      pSyncNode->m_pElement->m_pTimelineElement->
                      numChildDursAdded()  &&
                      // /Helps fix PR 62408 and PR 50660; if endsync="all" and
                      // all children have weighed in, we still need to allow
                      // children to restart before parent has ended:
                      // Note: PR 62688 is still fixed with this change:
                      lEffectiveResolvedTime >
                      ulSyncBaseDelay+ulSyncBaseDuration;
                // /Calculate the end time constraints, if any,
                // imposed by syncBase:
                // /XXXEH- check delay constraints as well?  I think
                // that's already handled in setDelay:
                if ((UINT32)-1 != ulSyncBaseDuration  &&
                      // /All this logic fixes restarting of element
                      // when parent does *not* have an explicit dur
                      // or end and thus its dur or end can be extended
                      // (but wasn't being extended due to maxEndTime
                      // begin set here):
                      (pSyncNode->m_pElement->m_bHasExplicitDur  ||
                      pSyncNode->m_pElement->m_bHasExplicitEnd   ||
                      // /Final(?) fix for PR 62688 (par & excl
                      // versions): endsync="all" also should constrain
                      // if it has fully resolved:
                      bEndsyncIsAllAndAllAreDone  ||
                      // /Or it has a (resolved) endsync=first:
                      (pSyncNode->m_pElement->m_nEndsyncEventSourceTag ==
                      SMILEventSourceFirst)  ||
                      // /Or it has an (resolved) endsycn=[some ID]:
                      (pSyncNode->m_pElement->m_nEndsyncEventSourceTag ==
                      SMILEventSourceID) ) )
                {
                  ulMaxEndTime = ulSyncBaseDelay + ulSyncBaseDuration;
#if 1 /* XXXEH- this works with PR 59584 and PR 62688 but needs more testing: */
                  if (pSyncNode->m_pElement->
                        m_bDurationIncludesDelayBeyondSyncbase)
                  {
                      // /Fixes PR 56686 case 1 where click extends parent's
                      // set duration:
                      ulMaxEndTime -= pSyncNode->m_pElement->
                            m_ulBeginOffsetFromSyncBase;
                  }
#else /* XXXEH- remove this when above code is better tested: */
                  ulDelayBeyondSyncBase = ulChildDelayBeyondStartOfThis;
                  // /This fixes case where excl has begin offset and child does too:
                  // I'm unsure if this works under all conditions, thus the assert:
                  if (m_pSourceElement->m_bCurBeginIsOffsetFromSyncBase)
                  {
                      if (!m_pSourceElement->m_bDurationIncludesDelayBeyondSyncbase)
                      {
                        ulDelayBeyondSyncBase +=
                              m_pSourceElement->m_ulBeginOffsetFromSyncBase;
                        bNeedToSet_DurationIncludesDelayBeyondSyncbase = TRUE;
                      }
                  }

                  ULONG32 ulActualDurBeyondSyncBase =
                        ulDuration + ulDelayBeyondSyncBase - ulChildDelayBeyondStartOfThis;
#endif
                  BOOL isEndsyncIDrestarting = (SMILEventSourceID ==
                        pSyncNode->m_pElement->m_nEndsyncEventSourceTag  &&
                        pTmpVal->m_pElement->m_pNode->m_id ==
                        pSyncNode->m_pElement->m_EndsyncEventSourceID  &&
                        // /Not really restarting if starting *early*:
                        SMILTimelineStatusStartScheduledForLater !=
                        curSMILTimelineStatus);
                  BOOL isEndsyncFirst = SMILEventSourceFirst ==
                          pSyncNode->m_pElement->m_nEndsyncEventSourceTag;
                  BOOL isEndsyncFirstRestarting = isEndsyncFirst  &&
                        bPrepForRestartNeeded  &&
                        // /Make sure this is the first one restarting:
                        (ulMaxEndTime-
                        CHECKPENDING_TIME_VS_CURTIME_FUDGE_FACTOR <
                        lEffectiveResolvedTime);
                  ULONG32 ulParentHardStop = ulMaxEndTime;
                  // /If ID or first is restarting at end, we should just
                  // end without using a fudge factor):
                  if (!isEndsyncIDrestarting  &&  !isEndsyncFirstRestarting)
                  {
                      ulParentHardStop +=
                            CHECKPENDING_TIME_VS_CURTIME_FUDGE_FACTOR;
                  }
                  else
                  {
                      // /Must be restarting if parent dur is based on us:
                      HX_ASSERT(bPrepForRestartNeeded  &&
                            SMILTimelineStatusNeverScheduled !=
                            pSyncNode->m_pElement->m_nEndsyncEventSourceTag);
                  }
                  // /If endsync is first and restarting of this element
                  // means its prior play ended and thus a *first*
                  // child of parent has ended, parent should end *now*:
                  if ( (ulParentHardStop <= lEffectiveResolvedTime  ||
                        // /If ID or 1st is restarting, parent is done:
                        isEndsyncIDrestarting  ||  isEndsyncFirstRestarting)  &&
                        (isEndsyncFirst  ||
                        // /Fixes PR 59584(endsyncIDwithRestart ver)
                        // where ID'd element restarts, thus ending
                        // itself and its parent in the process:
                        isEndsyncIDrestarting  ||
                        // /For final PR 62688 fix: don't start if
                        // endsync="all" parent has played all kids:
                        bEndsyncIsAllAndAllAreDone) )
                  {
                      goto doneWithRestartCode;
                  }
                }
                else if (SMILExcl == pSyncNode->m_tag  &&
                      pSyncNode->m_pElement->m_pTimelineElement  &&
                      !pSyncNode->m_pElement->m_bHasExplicitDur  &&
                      !pSyncNode->m_pElement->m_bHasExplicitEnd  &&
                      pSyncNode->m_pElement->m_pTimelineElement->
                      numChildDursAdded() == 0                   &&
                      // /Resolved *at* time must be <= excl's delay
                      // otherwise excl has come and gone:
                      (ULONG32)pTmpVal->getWhenTimeWasResolved() >
                      ulSyncBaseDelay  &&
                      // /Fixes case where scheduled begin time >0 wasn't
                      // used until just-in-time & we do want to use it:
                      pTmpVal->getTimeType() == SmilTimeEvent  &&
                      // /In case 1st child begin="5s" and other child
                      // event-begins prior to that, we want it to begin:
                      !pSyncNode->m_pElement->m_pTimelineElement->
                            hasChildWithScheduledBegin()  &&
                      // /However, if it has endsync="all", then it has
                      // unresolved (open) duration, not 0; fixes PR 53527:
                      pSyncNode->m_pElement->m_nEndsyncEventSourceTag !=
                            SMILEventSourceAll)
                {
                  // /Fixes version of PR 56690 where excl has no explicit
                  // end or dur and has no children with already-scheduled
                  // begin time(s); sucn an excl has a 0 duration:
                  goto doneWithRestartCode;
                }
                if ((UINT32)-1 != ulMaxEndTime)
                {
                  UINT32 ulNewEndTime =(UINT32)lEffectiveResolvedTime +
                        ulNewDuration;
                  if ((UINT32)-1 != ulNewDuration)
                  {
                      if (ulNewEndTime > ulMaxEndTime)
                      {
                        // /Reduce it so it ends when parent does:
                        // /XXXEH- need to reverse these --v
                        ulNewDuration -= (ulNewEndTime-ulMaxEndTime);
                      }
                  }
                  else
                  {
                      // /Fixed this logic once and for all so PR 56690
                      // stays fixed & repro for PR 50572 doesn't break
                      // on first click:
                      // /(Using a temp var instead of actually changing
                      // ulNewDuration here fixes PR 62408  and doesn't
                      // break PR 56690 nor PR 50572 (and even fixes post-
                      // 1st restart bug in PR 50572 repro):
                      ULONG32 ulTmpNewDuration = ( (LONG32)ulMaxEndTime >
                            lEffectiveResolvedTime) ?
                            ulMaxEndTime -
                            (ULONG32)lEffectiveResolvedTime : 0;

                      // /This is needed for PR 63650 fix:
                      ulMaxDuration = ulTmpNewDuration;

                      if (0 == ulTmpNewDuration)
                      {
                        // /Fixes version of PR 56690 where parent
                        // has an explicit end or dur and
                        // pTmpVal->m_pElement is a time container
                        // (if it were media, instertTimelineElem...()
                        // would handle the out-of-bounds begin):
                        goto doneWithRestartCode;
                      }
                  }
                }
            }

#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG)
{FILE* f1 = ::fopen("c:\\smil2.txt", "a+"); ::fprintf(f1, "\npTmpVal->m_idRef= %s, ulNewDuration=%lu, lEffectiveResolvedTime=%ld ulMaxEndTime=%lu, bPrepForRestartNeeded=%lu  \n", (const char*)pTmpVal->m_idRef, ulNewDuration, lEffectiveResolvedTime, ulMaxEndTime, (UINT32)bPrepForRestartNeeded);  ::fclose(f1);}
#endif

            // /Now, add the track in:
            HX_ASSERT(pTmpVal->m_pElement->m_pTimelineElement);
            pTmpVal->m_pElement->m_pTimelineElement->
                  setDelayEventResolved(TRUE);
            if (bPrepForRestartNeeded)
            {
                // /FALSE means restart is not due to parent restart:
                pTmpVal->m_pElement->prepForRestart(FALSE,
                      lEffectiveResolvedTime);
            }

            if (ulMaxDuration == (UINT32)-1)
            {
                // /Makes sure <excl end="30s"><ref end="40s"/></excl>
                // only plays for 30s (along with fix for PR 63650):
                ulMaxDuration = ulNewDuration;
            }

            // /If we're restarting, we need to reset the duration
            if (bPrepForRestartNeeded)
            {
                // /XXXEH- need to account for min/max constraints here:
                // /If we're restarting and we're not being constrained
                // by our time container and we don't have an explicitly-
                // set duration, then we need to reset these and play
                // indefinitely:
                if ((UINT32)-1 == ulNewDuration)
                {
#if defined(XXXEH_20010620_OLD_SMIL2_DRAFT_SEMANTICS_FOR_RESTART_DUR_CALCULATION)
                  HX_ASSERT(0  &&  "ehodge note to self: check spec on restart dur.");
                  pTmpVal->m_pElement->m_ulOriginalDuration =
                  pTmpVal->m_pElement->m_ulMaxDuration = (UINT32)-1;
                  /* XXXEH- need to reset this to FALSE via new method:
                  pTmpVal->m_pElement->m_pTimelineElement->
                        m_bMaxDurationSet = FALSE;
                  */
                  // /This should be *unresolved*, not  *intrinsic dur
                  // of media*, if this has no resolved end time after
                  // lEffectiveResolvedTime and is being restarted but
                  // *does* have an event-based end time.  (See
                  // "// if all ends are before the begin, bad interval"
                  // in SMIL 2.0 Timing Spec):

                  // /We need to do this otherwise it will only play
                  // for the media's intrinsic duration:
                  ulNewDuration = WAY_IN_THE_FUTURE;
#endif
                }
                // /XXXEH- need to account for min/max constraints here:
                pTmpVal->m_pElement->m_pTimelineElement->resetDuration(
                      ulNewDuration);
                // /Fixes restarting an element whose parent has a set
                // dur; prior to this, it would override the parent dur:
                pTmpVal->m_pElement->m_pTimelineElement->setMaxDuration(
                      ulMaxDuration);
            }
            // /we just want to set the max duration to avoid affecting
            // our parent's end time:
            else
            {
                pTmpVal->m_pElement->m_pTimelineElement->setMaxDuration(
                      // /Using this instead of ulNewDuration fixes
                      // PR 63650 (which broke when fix for PR 62408,
                      // above, was put in):
                      ulMaxDuration);
            }

            // /While fixing PR 55253, a par that restarts was imposing
            // old duration on its children when setDelay() was called
            // before resetDuration(), above, so that's why setDelay()
            // is now being called later, here:
            pTmpVal->m_pElement->m_pTimelineElement->setDelay(
                  lEffectiveResolvedTime, FALSE);

            bREFSomeScheduleWasChanged = TRUE;

            // /iF there are two children that start at time 0, and the
            // first should play but pause immediately while the second
            // plays, then we must add the first before playback and then
            // let the second interrupt the first during blackback:
            if (bDoHandleExclBeforePlaybackStarts  &&  bIsInExcl)
            {
                HX_ASSERT(!((*pExclChildAddedBeforePlayback)[(const char*)
                      pExclAncestor->m_id]));
                if (pExclChildAddedBeforePlayback  &&  pExclAncestor)
                {
                  (*pExclChildAddedBeforePlayback)[(const char*)
                        pExclAncestor->m_id] = (void*)TRUE;
                }
            }
          } // /End if(bAddTrack).

doneWithRestartCode:

          // /We need to be sure to remove a time that is in the past (or
          // is otherwise used herein) from the pendingList; this also
          // advances the lPos to the next one:
          // /But wait, there's more!:  We don't want to remove a time if
          // we just updated its time to later due to a priorityClass
          // deferral (via peers or higher="defer"):
          if (!bTimeValWasDeferred)
          {
            HX_ASSERT(m_pPendingBeginTimeList->GetCount());
            // /Checking the count before deleting fixes a random crash
            // in "BUG-20010611_firstExclChild...".  I can't figure out
            // where else it might have been deleted during lPos's life:
            if (m_pPendingBeginTimeList->GetCount())
            {
                m_pPendingBeginTimeList->RemoveAt(lPosOfCurTmpVal);
            }

            // /Post-PR 85885-fix cleanup fix: need to remove undeferEvent
            // from pTmpVal->m_pElement's m_pBeginTimeList if resume just
            // happened, otherwise a subsequent play-to-end of the media
            // that pTmpVal's element originally deferred past would result
            // in that element erroneously playing again:
            if (pTmpVal->isUndeferEvent())
            {
                HX_ASSERT(pTmpVal->m_pElement->m_pBeginTimeList);
                if (pTmpVal->m_pElement->m_pBeginTimeList)
                {
                  CHXSimpleList* pBeginList =
                        pTmpVal->m_pElement->m_pBeginTimeList;
                  // /First, remove any resume events in the list:
                  lPos = pBeginList->GetHeadPosition();

                  while (lPos)
                  {
                      LISTPOSITION lPosOfCurTmpVal = lPos;
                      // /Gets val @ lPos then moves it to next node:
                      SmilTimeValue* pCurListValue =
                            (SmilTimeValue*)pBeginList->GetNext(lPos);
                      if (!pCurListValue  ||  !pCurListValue->m_pElement)
                      {
                        HX_ASSERT(pCurListValue  &&
                              pCurListValue->m_pElement);
                        continue;
                      }
                      if (pCurListValue->isUndeferEvent())
                      {
                        HX_ASSERT(pTmpVal == pCurListValue);
                        // /Get rid of it from the list:
                        pBeginList->RemoveAt(lPosOfCurTmpVal);
                        break; // /Done; only 1 defer can be in list.
                      }
                  }
                }
            }
          }

      }
      else // / i.e., !(lEffectiveResolvedTime <= lCurTime)
      {
          // /Since the list is sorted by time, we know we're done handling
          // times that are resolved to lCurTime or earlier:

          // /XXXEH- revisit this (and make sure the @%$#@! list is
          // temporally sorted; as of 20010307, if you have
          // <ref1 begin="ref2.beginEvent"/> and <ref2 begin="0s;7s"/>,
          // the following break will happen before the event-arc time
          // gets seen because ref2's begin of 7s is ahead in the list:
#if defined(XXXEH_NEED_TEMPORAL_SORTING_FOR_THIS_OPTIMIZATION)
          break;
#endif /* XXXEH_NEED_TEMPORAL_SORTING_FOR_THIS_OPTIMIZATION. */
      }
    } // /End while (lPos...)  [end handling pending begin times].

    // /Go through m_pPendingEndTimeList now:
    lPos = NULL;
    if (m_pPendingEndTimeList)
    {
      lPos = m_pPendingEndTimeList->GetHeadPosition();
    }

    if (bDoHandleExclBeforePlaybackStarts)
    {
      goto cleanup;
    }

    while (lPos  &&  HXR_OK == retval)
    {
      LISTPOSITION lPosOfCurTmpVal = lPos;
      // /Gets val at lPos and then moves lPos to next node in list:
      SmilTimeValue* pTmpVal =
            (SmilTimeValue*)m_pPendingEndTimeList->GetNext(lPos);
      if (!pTmpVal  ||  !pTmpVal->m_pElement)
      {
          // /List shouldn't have an empty node or a node w/NULL element:
          HX_ASSERT(pTmpVal  &&  pTmpVal->m_pElement);
          // /Get rid of it from the list:
          m_pPendingEndTimeList->RemoveAt(lPosOfCurTmpVal);
          continue;
      }

      LONG32 lEffectiveResolvedTime = 0;
      HX_RESULT tmprslt =
            pTmpVal->getEffectiveResolvedTime(lEffectiveResolvedTime);

      if (HXR_OK!=tmprslt)
      {
          // /No element should ever have been inserted in this list if it
          // didn't have a resolved time so we shouldn't get here!
          HX_ASSERT(HXR_OK==tmprslt);
          // /Get rid of it from the list:
          m_pPendingEndTimeList->RemoveAt(lPosOfCurTmpVal);
          continue;
      }

      if (pTimeValEndToggleToBeIgnored  &&
            pTmpVal->isSameTimeValue(pTimeValEndToggleToBeIgnored))
      {
          // /Ignore it since it was used, above, to begin the element and
          // shouldn't also be used to end it:
          m_pPendingEndTimeList->RemoveAt(lPosOfCurTmpVal);
          continue;
      }

      ULONG32 ulActualStartTime = 0;
      ULONG32 ulActualStopTime = 0;
      HX_RESULT pnrStart =
            pTmpVal->m_pElement->getCurrentScheduledStartTime(
            ulActualStartTime);
      LONG32 lElementCurBeginTime = (LONG32)ulActualStartTime;

      HX_RESULT pnrStop =
            pTmpVal->m_pElement->getCurrentScheduledStopTime(
            ulActualStopTime);
      LONG32 lElementCurEndTime = (LONG32)ulActualStopTime;

      BOOL bCurElementIsTimeContainer =
            isTimeContainerObject(pTmpVal->m_pElement->m_pNode);

      // /NOTE:we need to recalculate all these here, even though they were
      // calculated above for begin-time-list handling, because that begin-
      // time-handling code may have altered some of the values used here:
      SMILTimelineStatus curSMILTimelineStatus = SMILTimelineStatusUnknown;
      if (!pTmpVal->m_pElement->m_bInsertedIntoTimeline)
      {
          curSMILTimelineStatus = SMILTimelineStatusNeverScheduled;
      }
      else
      {
          HX_ASSERT(HXR_OK == pnrStart  &&
                (UINT32)-1 != lElementCurBeginTime  &&
                SMILTIME_INFINITY != lElementCurBeginTime);

          if (lElementCurBeginTime > lCurTime)
          {
            curSMILTimelineStatus =
                  SMILTimelineStatusStartScheduledForLater;
          }
          else
          {
            if (lElementCurEndTime==(-1)  ||
                  lElementCurEndTime > lCurTime)
            {
                curSMILTimelineStatus =
                      SMILTimelineStatusCurrentlyPlaying;
            }
            else
            {
                curSMILTimelineStatus =
                      SMILTimelineStatusFinishedPlaying;
            }
          }
      }

      // /We should be in one of these four states; anything else means
      // there's an inconsistency somewhere:
      HX_ASSERT (SMILTimelineStatusUnknown != curSMILTimelineStatus);

      INT32 lResolvedToTimeWithoutOffset =
            pTmpVal->getResolvedToTimeWithoutOffset();
      INT32 lEventTimeOffset = pTmpVal->getTimeOffset();
      INT32 lTimeWithFullOffset =
            lResolvedToTimeWithoutOffset + lEventTimeOffset;

      // /Now, see if this pending end time is ready to be used:
      if (lEffectiveResolvedTime <= lCurTime)
      {
          // /Unless this element is currently playing, we don't need to
          // do anything until a valid begin time gets resolved:
          if (SMILTimelineStatusCurrentlyPlaying == curSMILTimelineStatus)
          {
            if (!pTmpVal->m_pElement->m_pTimelineElement)
            {
                retval = HXR_UNEXPECTED;
                HX_ASSERT(pTmpVal->m_pElement->m_pTimelineElement);
                goto cleanup;
            }
            else
            {
                // /Just call RemoveTrack() at the time we want it gone,
                // and this function we're in has waited for any positive
                // offset to an event before trying to remove the track,
                // so we've managed the scheduling part so, unlike with
                // AddTrack(), the core hasn't had to:
                if (NULL != pTmpVal->m_pElement->m_pHandler  ||
                      bCurElementIsTimeContainer)
                {
                  // /See if pID is a time container; if so, call
                  // handleTrackRemoval() on all its descendant media:
                  if (bCurElementIsTimeContainer)
                  {
                      retval = HXR_OK;

                      SMILNode* pChild = pTmpVal->m_pElement->
                            m_pNode->getFirstChild();
                      while (pChild)
                      {
                        CSmilElement* pElement = pChild->m_pElement;
                        if (pElement  &&  pElement->m_pHandler  &&
                              pElement->m_bInsertedIntoTimeline)
                        {
                            HX_RESULT retval2 = pElement->m_pHandler->
                                  handleTrackRemoval((const char*)
                                  pElement->m_pNode->m_id,
                                  (INT32)pElement->m_pNode->m_nGroup);
                            if (HXR_OK == retval2)
                            {
                              // /The duration changed due to an
                              // event, not due to clipping by
                              // our sync-parent.  Set this to
                              // false so elementResolved() will
                              // allow sync-arcs to resolve on
                              // our new end:
                              pElement->m_bCurEndClippedByParent =
                                    FALSE;
                              // /Fixes case where element has
                              // sync arc to this end time and
                              // we need to notify it that
                              // we've ended early:
                              m_pTimelineElementManager->notify((const char*) pElement->m_pNode->m_id);
                            }
                        }
                        pChild = pTmpVal->m_pElement->m_pNode->
                              getNextChild();
                      }
                  }
                  else
                  {
                      retval = pTmpVal->m_pElement->m_pHandler->
                            handleTrackRemoval(
                            (const char*)pTmpVal->
                            m_pElement->m_pNode->m_id,
                            (INT32)pTmpVal->
                            m_pElement->m_pNode->m_nGroup);
                  }

                  // /Fixes case where element has a sync arc to this
                  // end time and we need to notify it that we've
                  // ended early:
                  if (HXR_OK == retval)
                  {
                      // /The duration changed due to an event, not due
                      // to clipping by our sync-parent.  Set this to
                      // false so elementResolved() will allow
                      // sync-arcs to resolve on our new end:
                      pTmpVal->m_pElement->m_bCurEndClippedByParent =
                            FALSE;

                      // /Fixes PR 50676 part 5: we need to notify our
                      // parent that the duration has changed since our
                      // end may determine our parent's end (e.g.,
                      // parent endsync="[our ID]" or endsync="first"):
                      pTmpVal->m_pElement->m_pTimelineElement->resetDuration(
                            pTmpVal->m_pElement->m_ulDuration);

                      m_pTimelineElementManager->notify((const char*) pTmpVal->m_pElement->m_pNode->m_id);
                  }
                  bREFSomeScheduleWasChanged = TRUE;
                }
                else
                {
                        // XXXMEH - removed since we sometimes get multiple
                        // char events, before the events have even been
                        // processed.
//                HX_ASSERT(pTmpVal->m_pElement->m_pHandler);
                  retval = HXR_UNEXPECTED;
                  goto cleanup;
                }
            }
          }
          // /We need to be sure to remove a time that we use from the
          // pendingList; this also advances the lPos to the next one:
          m_pPendingEndTimeList->RemoveAt(lPosOfCurTmpVal);
      }
      else // /effective resolved time is in the future:
      {
          // /Note: if no earlier begin time has yet been resolved then
          // there's no reason to set the end time, even if it is
          // resolved:
          if (SMILTimelineStatusStartScheduledForLater ==
                curSMILTimelineStatus  &&
                lElementCurEndTime > lEffectiveResolvedTime )
          {
            // /If element is scheduled to play later and this new
            // end time is prior to that, then we should just ignore
            // this end time for now.  Note: if this end time is in
            // the future but still earlier than any resolved begin,
            // we still may need this end time if another begin
            // gets resolved to a time that is prior to this end time:
            if (lEffectiveResolvedTime > // /XXXEH- TODO: ">=" instead?!?
                  lElementCurBeginTime)
            {
                // /OK, so this new end time is later than the currently-
                // scheduled begin time and is earlier than the currently-
                // scheduled end time (if any), so let's use it as the
                // end time of the next "interval":
                retval = pTmpVal->m_pElement->setEndTime(this);
                bREFSomeScheduleWasChanged = TRUE;
                // /XXXEH- do we want to call resetDuration()?!?
                // ASSERT for debugging purposes:
                HX_ASSERT(0);
                // /XXXEH- use m_ulDelay+m_ulDuration in
                // comparisons below if m_bEndOffsetSet is false?!?:
                // otherwise, use that sum PLUS m_lEndOffset?????
                // /Recalculate cur end time now that it's been
                // set, above:
                lElementCurEndTime =
                      // /XXXEH-??:
                      // (pTmpVal->m_pElement->m_bEndOffsetSet?
                      // pTmpVal->m_pElement->m_lEndOffset:0) +
                      pTmpVal->m_pElement->m_ulDelay +
                      pTmpVal->m_pElement->m_ulDuration;

                // /XXXEH- TODO: is this all and/or what we want to do?:
                pTmpVal->m_pElement->m_pTimelineElement->resetDuration(
                      lElementCurEndTime -
                      lElementCurBeginTime);
            }
          }
          // /Since the list is sorted by time, we know we're done handling
          // times that are resolved to lCurTime or earlier:
          break;
      } // /End "else // /effective resolved time is in the future".
    } // /End handling pending end times.

#if 0 // /XXXEH- TODO: enable this code and test it!!
    if (bREFSomeScheduleWasChanged)
    {

      // /XXXEH- TODO: prevent circular references from causing
      // this to spin out of control, e.g.,
      //   <ref id="ref1" begin="0s; ref2.begin" />
      //   <ref id="ref2" begin="ref1.begin" />

      // /Now, call this function in case some dependent
      // element's timing became resolved to now due to the update
      // to pTmpVal->m_pElement's timing, above:
      BOOL bSomeDependentElementScheduleChanged = FALSE;
      HX_RESULT pnr = HXR_OK;
      do
      {
          pnr = checkPendingBeginAndEndTimes(lCurTime, iCurrentGroupIndex,
                bSomeDependentElementScheduleChanged,
                ++lRecursionCount,
                pPauseDisplayHideElementList,
                pPauseDisplayDisableElementList, FALSE);
      }while (lRecursionCount<MAX_PENDING_CHECK_CALLDEPTH  &&
          HXR_OK==pnr  &&  bSomeDependentElementScheduleChanged);
    }
#endif

cleanup:
    HX_DELETE(pExclChildAddedBeforePlayback);

    return retval;
}


/* This looks to see if any stream events in the list have a begin time
 * that is now at or earlier than lCurTime.  If any such are found, then
 * the associated URL is hurled.
 */
CHXMapStringToOb*
CSmilParser::findNextPendingOnLoadURL(UINT32 ulCurTime)
{
    // /Look through the list of "onLoad" URLs and fire off any whose time
    // has been exceeded:
    // /Handle begin times that are ready to go:
    LISTPOSITION lPos = NULL;
    if (m_pOnLoadURLList)
    {
      lPos = m_pOnLoadURLList->GetHeadPosition();
    }

    // /Helps Fix PR 67170: this now handles removing all but the last of
    // simultaneous hurls to the same rn:sendTo+target, so a long seek back
    // or a long seek forward will skip all but the last of each rn-sendTo
    // link whose time is less than the seeked-to time.  Otherwise we'd be
    // simultaneously hurling possibly many links that would be immediately
    // replaced.  The end result is a better user experience, but will not
    // necessarily leave the sendTo app in the state it would have been had
    // normal (seek-less) playback arrived at that time in the presentation
    // (especially if a "back" button exists in the app).
    // /Keep a map of rn:sendTo+target names, each entry containing the most
    // temporally-recent-but-not-exceeding-ulCurTime one, and a matching map
    // for their start times, calculated below:
    CHXMapStringToOb* pSendToTargetMapOfAnchorsToBeHurledNow = NULL;
    CHXMapStringToOb* pMapStartTimeToSendToPlusTarget = NULL;

    // /Go through and see if anybody has a begin time that's ready to become
    // their active begin time:
    while (lPos)
    {
      LISTPOSITION lPosOfCurAnchor = lPos;
      // /Gets val at lPos and then moves lPos to next node in list:
      CSmilAAnchorElement* pCurAnchor =
            (CSmilAAnchorElement*)m_pOnLoadURLList->GetNext(lPos);
      if (!pCurAnchor)
      {
          // /List shouldn't have an empty node:
          HX_ASSERT(pCurAnchor);
          // /Get rid of it from the list:
          m_pOnLoadURLList->RemoveAt(lPosOfCurAnchor);
          continue;
      }

      BOOL bLaunchHyperlink = FALSE;
      // /First, we need to get ulCurTime into local, sync-base time
      // coordinates because isLinkActiveAtTime() expects a time relative
      // to the anchor's sync-base, not to the start of the group:
      ULONG32 ulCurSyncBaseTime = ulCurTime;
      SMILNode* pSyncAncestor = getSyncAncestor(
            pCurAnchor->m_pNode);
      if (pSyncAncestor  &&  pSyncAncestor->m_pElement  &&
            (UINT32)-1 != pSyncAncestor->m_pElement->m_ulDelay)
      {
          if (ulCurTime < pSyncAncestor->m_pElement->m_ulDelay)
          {
            continue; // /Parent not even active yet.
          }
          else
          {
            ulCurSyncBaseTime =
                  ulCurTime - pSyncAncestor->m_pElement->m_ulDelay;
          }
      }
      else
      {
          continue;  // /Has no sync-base or has an unresolved one.
      }

      ULONG32 ulAnchorBeginOffset = 0;
      ULONG32 ulAnchorStartTime = 0;

      ULONG32 ulBeginRelativeToSyncBase = (UINT32)-1;

      // /If delay isn't set, then fall through to else, below, which
      // calculates the anchor's delay based on its sync-base time (which
      // is its sync-parent's begin time unless its sync-parent is a seq,
      // in which case the sync-base time is the end of the prev timeline
      // element child in the seq):
      if ((UINT32)-1 != pCurAnchor->m_ulDelay  &&
            pCurAnchor->m_bTimeValueSet)
      {
            // /Moving this out of the above if() and inside this block fixes
            // PR 99597; before, if isLinkActiveAtTime() returned FALSE, even
            // though there was explicit timing on the linke, the else, below,
            // would get executed, often setting bLaunchHyperlink to TRUE when
            // it should have just been left FALSE:
            if (pCurAnchor->isLinkActiveAtTime(ulCurSyncBaseTime,
                    ulBeginRelativeToSyncBase))
            {
                ulAnchorStartTime = ulBeginRelativeToSyncBase +
                    // /Get it into group time, not local time:
                    (ulCurTime - ulCurSyncBaseTime);
              bLaunchHyperlink = TRUE;
            }
            // /else link no longer or not yet active, so ignore it.
      }
      // /no explicit timing was set on the anchor, so see if it's
      // got a delay set &/or a begin offset set; if not, use its sync-
      // parent's begin (or use end of its prev seq sibling if in a seq):
      else
      {
          ulAnchorStartTime = pCurAnchor->m_ulDelay;
          if ((UINT32)-1 == ulAnchorStartTime)
          {
            if (pCurAnchor->m_bAnchorBeginOffsetSet)
            {
                ulAnchorBeginOffset = pCurAnchor->m_lAnchorBeginOffset;
            }
            else if (pCurAnchor->m_bBeginOffsetSet)
            {
                ulAnchorBeginOffset = (UINT32)pCurAnchor->m_lBeginOffset;
                if (pCurAnchor->m_lBeginOffset < 0)
                {
                  ulAnchorBeginOffset = 0;
                }
            }
            // /Be sure to add on parent's delay, if any:
            if (pCurAnchor->m_pNode  &&
                  pCurAnchor->m_pNode->m_pParent  &&
                  pCurAnchor->m_pNode->m_pParent->m_pElement)
            {
                SMILNode* pSyncAncestor =
                      getSyncAncestor(pCurAnchor->m_pNode);
                if (pSyncAncestor  &&
                      SMILSeq == pSyncAncestor->m_tag)
                {
                  CSmilElement* pSyncBaseElement =
                        pSyncAncestor->m_pElement;
                  // /Fixes PR 53363: syncBase of seq child is
                  // end of prior sibling, not parent's begin:
                  SMILNode* pPriorSibling =
                        getPrevNode(pCurAnchor->m_pNode);
                  if (!pPriorSibling)
                  {
                      // /No prev sibling so sync base is seq parent:
                      pSyncBaseElement = pSyncAncestor->m_pElement;
                  }
                  else if (pPriorSibling->m_pElement  &&  (UINT32)-1 !=
                        pPriorSibling->m_pElement->m_ulDelay  &&
                        (UINT32)-1 !=
                        pPriorSibling->m_pElement->m_ulDuration)
                  {
                      pSyncBaseElement = pPriorSibling->m_pElement;
                  }
                  // /Prior sibling should not be a timeline object
                  // otherwise its end should be known:
                  else if (!isTimelineObject(pPriorSibling))
                  {
                      SMILNode* pPriorSiblingFirstChild =
                            pPriorSibling->getFirstChild();
                      if (!pPriorSiblingFirstChild)
                      {
                        // /XXXEH- TODO: get prior-prior sibling and
                        // try again:
                        continue;
                      }
                      pSyncBaseElement =
                            pPriorSiblingFirstChild->m_pElement;
                      BOOL bPriorSibIsTimelineObject =
                            isTimelineObject(pPriorSiblingFirstChild);
                      HX_ASSERT(bPriorSibIsTimelineObject);
                  }
                  // /Skip this link because prior timeline object does
                  // not yet have a resolved end time (which it should):
                  else
                  {
                      HX_ASSERT(0);
                      continue;
                  }
                  if (pSyncBaseElement)
                  {
                      HX_RESULT pnrStart = HXR_OK;
                      // /Fixes PR 58429: if syncBaseElement is actually
                      // the parent seq, then use its begin time, not
                      // its end time, as the sync base:
                      if (pSyncBaseElement == pSyncAncestor->m_pElement)
                      {
                        pnrStart = pSyncBaseElement->
                              getCurrentScheduledStartTime(
                              ulAnchorStartTime);
                      }
                      else // /Use end of prior seq sibling as sync base:
                      {
                        pnrStart = pSyncBaseElement->
                              getCurrentScheduledStopTime(
                              ulAnchorStartTime);
                      }
                      // /Now, add begin offset, if any, to
                      // sync base's end time (but not if end time
                      // is not valid or is indefinite):
                      if (HXR_OK == pnrStart  &&
                            ulAnchorStartTime < WAY_IN_THE_FUTURE)
                      {
                        ulAnchorStartTime += ulAnchorBeginOffset
                              // /Add fudge factor in case
                              // syncBaseElement ends when parent
                              // does, in which case we don't want
                              // to hurl because we're past the end:
                              +100;
                      }
                  }
                }
                else if (pSyncAncestor  &&  pSyncAncestor->m_pElement)
                {
#if XXXEH_HOLD_UNTIL_I_FIX_BUG_WHEN_PAR_OR_EXCL_PARENT_HAS_DELAY
                  ulAnchorStartTime = (UINT32)-1; // /Start unresolved...
                  if ((UINT32)-1 != pSyncAncestor->m_pElement->m_ulDelay)
                  {
                      ulAnchorStartTime =
                            pSyncAncestor->m_pElement->m_ulDelay;
                  }
                  if (pSyncAncestor->m_pElement->m_bBeginOffsetSet  &&
                        WAY_IN_THE_FUTURE !=
                        pSyncAncestor->m_pElement->m_ulDelay)
                  {
                      ulAnchorStartTime += (UINT32)
                            (pSyncAncestor->m_pElement->m_lBeginOffset);
                  }
#else
                  HX_RESULT pnrStart = pSyncAncestor->m_pElement->
                        getCurrentScheduledStartTime(ulAnchorStartTime);
                  // /Now, add begin offset, if any, to
                  // sync base's end time:
                  ulAnchorStartTime += ulAnchorBeginOffset;
#endif
#if defined(_DEBUG)
                  ULONG32 ulStartTime2 = (UINT32)
                        (pSyncAncestor->m_pElement->m_lBeginOffset >= 0?
                        pSyncAncestor->m_pElement->m_lBeginOffset : 0);
                  ulStartTime2 += ulAnchorBeginOffset;
                  HX_ASSERT(ulAnchorStartTime == ulStartTime2);
#endif
                }
                else
                {
                  ulAnchorStartTime = (UINT32)-1; // /Unresolved time.
                }
            }
          }
          if (ulAnchorStartTime <= ulCurTime)
          {
            // /XXXEH: check if ulAnchorEndTime >= ulCurTime as well (see:
            // "BUG-20011113_letOnLoadURLhurlAt10sThenStopItAndResumeMainThenSeek..."):
            bLaunchHyperlink = TRUE;
          }
      }

      if (!bLaunchHyperlink)
      {
          /* // /XXXEH- TODO: pre-sort this temporally:
          // But, NOTE: we *can't* sort these due to not-yet-knowable
          parental timing, e.g.,  excl pause & defer, and par begin=[event]:
          // /This one's begin time has not yet been reached, and this
          // list is temporally sorted so we're done for now:
          break;
          */
          continue; // /do this until list is temporally sorted.
      }
      else
      {
          // /Remove: it'll be hurled now or be determined to be ignored:
          m_pOnLoadURLList->RemoveAt(lPosOfCurAnchor);

          // /Helps fix PR 67170: don't return until we've found the last
          // of the ones ready to go for the associated "sendTo" app,
          // ignoring the others since they'll be replaced immediately
          // anyway:
          if (!pSendToTargetMapOfAnchorsToBeHurledNow)
          {
            pSendToTargetMapOfAnchorsToBeHurledNow = new CHXMapStringToOb;
            if (!pSendToTargetMapOfAnchorsToBeHurledNow)
            {
                goto cleanup; // /Do nothing if system is out of memory.
            }
          }

          // /If no sendTo specified, then defaults to "_rpbrowser" if
          // external=TRUE, otherwise defaults to "_rpengine":
          CHXString keyStr;
          if (pCurAnchor->m_sendTo.IsEmpty()  ||
                // /If external is FALSE, sendTo is ignored:
                !pCurAnchor->m_bExternal)
          {
            if (pCurAnchor->m_bExternal)
            {
                keyStr = "_rpbrowser";
            }
            else
            {
                keyStr = "_rpengine";
            }
          }
          else // /use sendTo value as the key:
          {
            keyStr = (const char*)pCurAnchor->m_sendTo;
          }

          // /If link "target" is specified, append it to the key since a
          // target is a specific instance of the sendTo app:
          if (!pCurAnchor->m_target.IsEmpty())
          {
            keyStr += pCurAnchor->m_target;
          }

          HX_ASSERT(!keyStr.IsEmpty());
          if (!keyStr.IsEmpty())
          {
            // /If we didn't just seek, then just return the first one found
            // that's time-valid and let the next, if any, be hurled at the
            // next onTimeSync:
            if (!m_bHandlePostSeekOnLoadURLs)
            {
                (*pSendToTargetMapOfAnchorsToBeHurledNow)[keyStr] =
                      (void*)pCurAnchor;
                goto cleanup;
            }

            if (!pMapStartTimeToSendToPlusTarget)
            {
                pMapStartTimeToSendToPlusTarget = new CHXMapStringToOb;
                if (!pMapStartTimeToSendToPlusTarget)
                {
                  goto cleanup; // /Do nothing if system is out of memory.
                }
            }

            // /Now, see if map[key] time is earlier than current; if so,
            // replace it with current anchor start time:
            UINT32 ulCurrentlyMappedAnchorStartTime = (UINT32)-1;
            BOOL bCurrentlyMappedAnchorStartTimeIsValid = FALSE;
            if (pMapStartTimeToSendToPlusTarget->Lookup(
                  (const char*)keyStr, (void*&)ulCurrentlyMappedAnchorStartTime))
            {
                bCurrentlyMappedAnchorStartTimeIsValid = TRUE;
            }

            // /If map entry didn't already exist or it did but new time
            // is same or more recent, then replace it with the new value:
            if (!bCurrentlyMappedAnchorStartTimeIsValid  ||
                  // /Note: do ">=", not just ">", to make sure lexically-
                  // last of a same-timed pair wins:
                  ulAnchorStartTime >= ulCurrentlyMappedAnchorStartTime)
            {
                (*pSendToTargetMapOfAnchorsToBeHurledNow)[(const char*)
                      keyStr] = (void*)pCurAnchor;
                (*pMapStartTimeToSendToPlusTarget)[(const char*)keyStr] =
                      (void*)ulAnchorStartTime;
            }
          }
          else
          {
            goto cleanup; // /can't recover from error.
          }
      }
    }

cleanup:
    m_bHandlePostSeekOnLoadURLs = FALSE;

    HX_DELETE(pMapStartTimeToSendToPlusTarget);

    // /NOTE: May be NULL; if not, caller must delete when done:
    return (pSendToTargetMapOfAnchorsToBeHurledNow);
}


// /Returns TRUE if any other element is awaiting an activateEvent (click or
// other form of user activation) to begin or end (fixes PR 56503):
BOOL
CSmilParser::hasActivateEventListener(const char* pMediaID,
                              INT16 iCurrentGroupIndex)
{
    BOOL bSomeoneIsListeningForActivateEvent = FALSE;

    // /Look for begin times that are awaiting pMediaID user activation:
    LISTPOSITION lPos = NULL;

    CHXSimpleList* pCurList = m_pBeginEventList;
    for (INT16 iList=0; iList<2  &&  !bSomeoneIsListeningForActivateEvent; iList++)
    {
      if (1==iList)
      {
          pCurList = m_pEndEventList;
      }

      if (pCurList)
      {
          lPos = pCurList->GetHeadPosition();
      }
      else
      {
          continue;
      }

      // /Go through and see if anybody has a begin time that's ready to
      // become their active begin time:
      while (lPos)
      {
          LISTPOSITION lPosOfCurTmpVal = lPos;
          // /Gets val at lPos and then moves lPos to next node in list:
          SmilTimeValue* pTmpVal =
                (SmilTimeValue*)pCurList->GetNext(lPos);
          if (!pTmpVal  ||  !pTmpVal->m_pElement  ||
                !pTmpVal->m_pElement->m_pNode)
          {
            // /List shouldn't have empty node or node w/NULL element:
            HX_ASSERT(pTmpVal  &&  pTmpVal->m_pElement  &&
                  pTmpVal->m_pElement->m_pNode);
            // /Get rid of it from the list:
            pCurList->RemoveAt(lPosOfCurTmpVal);
            continue;
          }

          // Ignore this timeValue if it's element is not in the current
          // group:
          if (pTmpVal->m_pElement->m_pNode->m_nGroup != iCurrentGroupIndex)
          {
            continue;
          }

          const char* pEventName = pTmpVal->getEventName();
          const char* pIDRef = pTmpVal->getIdRef();

          if (pEventName  &&  pIDRef  &&
                0==strcmp("activateEvent", pEventName)  &&
                0==strcmp(pMediaID, pIDRef))
          {
            // /XXXEH- optimize for the following cases; if any evaluate
            // to TRUE, then don't return TRUE from here:
            // (1) If pTmpVal->m_pElement's restart="never" & m_pElement
            //     has played already.
            // (2) If pTmpVal->m_pElement's restart="whenNotActive" and
            //     m_pElement is currently playing
            // (3) If pTmpVal->m_pElement's parent begin is too late for
            //     m_pElement to play based on this activateEvent
            // (4) if pTmpVal->m_pElement's parent end is too early for
            //     m_pElement to play based on this activateEvent
            // (5) if pTmpVal is going to *end* on this activateEvent and
            //     its parent's end time is earlier than the computed end
            //     based on this activateEvent::
            bSomeoneIsListeningForActivateEvent = TRUE;
            break; // /We only need to know if *any* is listening.
          }
      } // end while().
    } // end for().

    return bSomeoneIsListeningForActivateEvent;
}




// /This function checks to see if there is already a time value with the
// exact same characteristics as the one being passed in:
BOOL
CSmilParser::isDuplicateEntry(CHXSimpleList* pList,
                        SmilTimeValue* pTimeValue)
{
    BOOL bHasEvilTwin = FALSE;
    if (pList  &&  pTimeValue)
    {
      LISTPOSITION lPos = NULL;
      if (pList)
      {
          lPos = pList->GetHeadPosition();
      }

      while (lPos)
      {
          // /Gets val at lPos and then moves lPos to next node in list:
          SmilTimeValue* pTmpValue = (SmilTimeValue*)pList->GetNext(lPos);
          if (!pTmpValue  ||  !pTmpValue->m_pElement)
          {
            // /List shouldn't have an empty node or a node w/NULL element:
            HX_ASSERT(pTmpValue  &&  pTmpValue->m_pElement);
            continue;
          }
          if (pTimeValue->isSameTimeValue(pTmpValue))
          {
            bHasEvilTwin = TRUE;
            break;
          }
      }
    }

    return bHasEvilTwin;
}

HX_RESULT CSmilParser::computeRemoveTime(const char* pszID,
                                         REF(UINT32) rulRemoveTime)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pszID)
    {
        // Get this element
        CSmilElement* pElement = findElement(pszID);
        if (pElement)
        {
            // Check our erase value
            if (pElement->m_eErase == EraseWhenDone)
            {
                // Get our fill value
                FillType eOurFill = pElement->m_eActualFill;
                // See if our parent is resolved
                BOOL        bKnowParentEnd    = FALSE;
                BOOL        bHasParentElement = FALSE;
                FillType    eParentFill       = FillDefault;
                const char* pszParentID       = NULL;
                UINT32      ulParentEnd       = 0;
                if (pElement->m_pNode)
                {
                    SMILNode* pSANode = getSyncAncestor(pElement->m_pNode);
                    if (pSANode && pSANode->m_pElement)
                    {
                        UINT32    ulStop  = 0;
                        HX_RESULT rv      = pSANode->m_pElement->getCurrentScheduledStopTime(ulStop);
                        if (SUCCEEDED(rv))
                        {
                            bKnowParentEnd = TRUE;
                            ulParentEnd    = ulStop;
                        }
                        // Get the parent's fill value
                        eParentFill = pSANode->m_pElement->m_eActualFill;
                        // Save the parent id
                        pszParentID = (const char*) pSANode->m_id;
                        // Do we have a parent CSmilElement?
                        CSmilElement* pTmpEl = findElement(pszParentID);
                        if (pTmpEl)
                        {
                            // Set the flag
                            bHasParentElement = TRUE;
                        }
                    }
                }
                // Get the type of time container we're in
                SMILNodeTag eTimeContTag = getSyncTag(pElement->m_pNode);
                // If we are in a <seq>, then get the delay of the
                // next element in the <seq>.
                BOOL   bKnowNextDelay = FALSE;
                BOOL   bLastInSeq     = FALSE;
                UINT32 ulNextDelay    = 0;
                if (eTimeContTag == SMILSeq &&
                    pElement->m_pTimelineElement)
                {
                    CSmilTimelineElement* pDep = pElement->m_pTimelineElement->getDependent();
                    if (pDep)
                    {
                        CSmilElement* pSrcEl = pDep->getSourceElement();
                        if (pSrcEl)
                        {
                            UINT32    ulStart = 0;
                            HX_RESULT rv      = pSrcEl->getCurrentScheduledStartTime(ulStart);
                            if (SUCCEEDED(rv))
                            {
                                bKnowNextDelay = TRUE;
                                ulNextDelay    = ulStart;
                            }
                        }
//                        if (pDep->initialDelaySet())
//                        {
//                            bKnowNextDelay = TRUE;
//                            // XXXMEH - DELAY_DUR_FIX
//                            ulNextDelay    = pDep->getDelay();
//                        }
                    }
                    else
                    {
                        // If we have no dependent timeline element,
                        // then we are the last element in the <seq>
                        bLastInSeq = TRUE;
                    }
                }
                else if (eTimeContTag == SMILExcl)
                {
                    // Get our currently scheduled stop time
                    UINT32    ulOurStopTime = 0;
                    HX_RESULT rv            = pElement->getCurrentScheduledStopTime(ulOurStopTime);
                    if (SUCCEEDED(rv))
                    {
                        // Now we need to look for all our siblings
                        // which have a currently scheduled start time
                        // after our stop time.
                        //
                        // Get our excl parent
                        SMILNode* pExclParentNode = getSyncAncestor(pElement->m_pNode);
                        if (pExclParentNode)
                        {
                            BOOL      bValidMin  = FALSE;
                            UINT32    ulMinStart = 0;
                            SMILNode* pSibling   = getTimelineDescendent(pExclParentNode);
                            while (pSibling)
                            {
                                if (pSibling->m_pElement &&
                                    pSibling->m_pElement != pElement)
                                {
                                    UINT32 ulSibStart = 0;
                                    rv = pSibling->m_pElement->getCurrentScheduledStartTime(ulSibStart);
                                    if (SUCCEEDED(rv))
                                    {
                                        if (ulSibStart > ulOurStopTime)
                                        {
                                            if (bValidMin)
                                            {
                                                if (ulSibStart < ulMinStart)
                                                {
                                                    ulMinStart = ulSibStart;
                                                }
                                            }
                                            else
                                            {
                                                ulMinStart = ulSibStart;
                                                bValidMin  = TRUE;
                                            }
                                        }
                              // /Fixes PR 53175 (& PR 54540):
                              // if sibling start time is earlier
                              // *but* end time is later, then it
                              // resumed after we ended and we
                              // should be removed if fill="freeze"
                              // as if another sibling just started:
                              else
                              {
#if XXXEH_2010419_secondPlayOfFirstInExclNotPlaying_LEGAL_BUT_HITS_THIS_AT_3s
#error: need to find content that trips this assert and is broken:
                                  HX_ASSERT(ulSibStart != ulOurStopTime  &&
                                        "same begins in excl; pls email ehodge");
#endif
                                  UINT32 ulSibEnd = 0;
                                  HX_RESULT rcSibEnd =
                                        pSibling->m_pElement->
                                        getCurrentScheduledStopTime(
                                        ulSibEnd);
                                  HX_ASSERT(HXR_OK == rcSibEnd);
                                  if (HXR_OK == rcSibEnd  &&
                                        ulSibEnd >= ulOurStopTime)
                                  {
                                    // /Guaranteed to be lower
                                    // than ulMinStart so far:
                                                ulMinStart = ulOurStopTime;
                                                bValidMin  = TRUE;
                                    // /All we need is one:
                                    break;
                                  }
                              }
                                    }
                                }
                                pSibling = getTimelineDescendent(pExclParentNode, pSibling);
                            }
                            // Did we get a valid next start?
                      // or a valid resume after our end?
                            if (bValidMin)
                            {
                                bKnowNextDelay = TRUE;
                                ulNextDelay    = ulMinStart;
                            }
                        }
                    }
                }
                // Switch based on our fill value
                // Note that we use "actual" fill here - that is
                // the "resolved" fill value after all fillDefault and
                // auto are taken into consideration.
                switch (eOurFill)
                {
                    // If we don't support transitions, but for some
                    // reason have a fill="transition", then we treat
                    // it as fill="remove"
#if !defined(HELIX_FEATURE_SMIL2_TRANSITIONS)
                    case FillTransition:
#endif /* #if !defined(HELIX_FEATURE_SMIL2_TRANSITIONS) */
                    case FillRemove:
                        {
                            // SPEC
                            // Specifies that the element will not extend past the end
                            // of the last instance of the simple duration
                            //
                            // Our remove time will be the end of our duration
//                            // XXXMEH - DELAY_DUR_FIX
//                            rulRemoveTime = pElement->m_ulDelay + pElement->m_ulDuration -
//                                            (pElement->m_bBeginOffsetSet ? pElement->m_lBeginOffset : 0);
                            UINT32 ulStop = 0;
                            retVal        = pElement->getCurrentScheduledStopTime(ulStop);
                            if (SUCCEEDED(retVal))
                            {
                                rulRemoveTime = ulStop;
                                // Do we know our parent's end?
                                if (bKnowParentEnd)
                                {
                                    // We know our parent's end, so we need to check if
                                    // our parent cuts us off. Note that we intentionally
                                    // check for ">" instead of ">=".
                                    if (rulRemoveTime > ulParentEnd)
                                    {
                                        // Do we actually have a parent?
                                        if (bHasParentElement)
                                        {
                                            // Our parent cuts us off, so we are still active
                                            // when our parent ends. Therefore, if our parent
                                            // has a fill value of "freeze" or "hold", then
                                            // we will have to look at remove time of our
                                            // parent to know when we need to remove.
                                            retVal = computeRemoveTime(pszParentID,
                                                                       rulRemoveTime);
                                        }
                                    }
                                }
                            }
                        }
                        break;
                    case FillFreeze:
                        {
                            // SPEC
                            // Specifies that the element will extend past the end of the
                            // last instance of the simple duration by "freezing" the element
                            // state at that point. An element with "freeze" behavior is
                            // extended according to the parent time container:
                            // - In a par, the element is frozen to extend to the end of the simple
                            //   duration of the par. In this case, fill="freeze" is equivalent to
                            //   fill="hold".
                            // - In a seq, the element is frozen to extend to the begin of the next
                            //   element in the seq or until the end of the simple duration of the
                            //   seq.This will fill any gap in the presentation (although it may have
                            //   no effect if the next element begins immediately). ]
                            // - In an excl, the element is frozen to extend to the begin of the next
                            //   element to be activated in the excl or until the end of the simple
                            //   duration of the excl. This will fill any gap in the presentation
                            //   (although it may have no effect if the next element interrupts the
                            //   current element). Note that if an element is paused, the active
                            //   duration has not ended, and so the fill attribute does not (yet)
                            //   apply.
                            //
                            // What kind of time container are we in?
                            if (eTimeContTag == SMILPar)
                            {
                                // Do we know our parent's end time? If we
                                // don't, then we can't set the remove time
                                if (bKnowParentEnd)
                                {
                                    // Is our parent frozen or held?
                                    if (eParentFill == FillFreeze || eParentFill == FillHold)
                                    {
                                        // Do we have a parent CSmilElement?
                                        if (bHasParentElement)
                                        {
                                            // We have a parent CSmilElement and
                                            // our parent is frozen or held, so we need
                                            // to check the remove time for our parent
                                            retVal = computeRemoveTime(pszParentID,
                                                                       rulRemoveTime);
                                        }
                                        else
                                        {
                                            // We don't have a parent CSmilElement,
                                            // so we must simply take our parent's end time
                                            rulRemoveTime = ulParentEnd;
                                            retVal        = HXR_OK;
                                        }
                                    }
                                    else
                                    {
                                        // Our parent is not frozen or held, so our
                                        // remove time is the end of our parent
                                        rulRemoveTime = ulParentEnd;
                                        retVal        = HXR_OK;
                                    }
                                }
                            }
                            else if (eTimeContTag == SMILSeq)
                            {
                                // Are we the last element in the <seq>?
                                if (!bLastInSeq)
                                {
                                    // We are not the last element in the <seq>,
                                    // so do we know the delay of the next element
                                    // in the <seq>?
                                    if (bKnowNextDelay)
                                    {
                                        // If we are in a <seq> and we have fill="freeze",
                                        // then we should remove at the start of the
                                        // next element in the <seq>, taking into account
                                        // any begin offset of that next element.
                                        rulRemoveTime = ulNextDelay;
                                        retVal        = HXR_OK;
                                    }
                                }
                                else
                                {
                                    // We are the last element in the <seq>, so
                                    // we will freeze until the end of our time
                                    // container.
                                    if (bKnowParentEnd)
                                    {
                                        // Is our parent frozen or held?
                                        if (eParentFill == FillFreeze || eParentFill == FillHold)
                                        {
                                            // Do we have a parent CSmilElement?
                                            if (bHasParentElement)
                                            {
                                                // We have a parent CSmilElement, and
                                                // our parent is frozen or held, so we need
                                                // to check the remove time for our parent
                                                retVal = computeRemoveTime(pszParentID,
                                                                           rulRemoveTime);
                                            }
                                            else
                                            {
                                                // We don't have a parent CSmilElement,
                                                // so we simply take our parent's end time
                                                // as our remove time.
                                                rulRemoveTime = ulParentEnd;
                                                retVal        = HXR_OK;
                                            }
                                        }
                                        else
                                        {
                                            // Our parent is not frozen or held, so our
                                            // remove time is the end of our parent
                                            rulRemoveTime = ulParentEnd;
                                            retVal        = HXR_OK;
                                        }
                                    }
                                }
                            }
                            else if (eTimeContTag == SMILExcl)
                            {
                                // Do we know the delay of the next element
                                // in the <excl>?
                                if (bKnowNextDelay)
                                {
                                    // If we are in a <seq> and we have fill="freeze",
                                    // then we should remove at the start of the
                                    // next element in the <seq>, taking into account
                                    // any begin offset of that next element.
                                    rulRemoveTime = ulNextDelay;
                                    retVal        = HXR_OK;
                                }
                            }
                        }
                        break;
                    case FillHold:
                        {
                            // SPEC:
                            // Setting this to "hold" has the same effect as setting to
                            // "freeze", except that the element is always frozen to extend to the end
                            // of the simple duration of the parent time container of the element
                            // (independent of the type of time container). For profiles that support
                            // a layered layout model (e.g., SMIL 2.0 Language Profile), held elements
                            // (elements with fill="hold") will refresh their display area when a layer
                            // is added on top then later removed.
                            //
                            // Do we know our parent's end time? If we
                            // don't, then we can't set the remove time
                            if (bKnowParentEnd)
                            {
                                // Is our parent frozen or held?
                                if (eParentFill == FillFreeze || eParentFill == FillHold)
                                {
                                    // Do we have a parent CSmilElement?
                                    if (bHasParentElement)
                                    {
                                        // We have a parent CSmilElement, and
                                        // our parent is frozen or held, so we need
                                        // to check the remove time for our parent
                                        retVal = computeRemoveTime(pszParentID,
                                                                   rulRemoveTime);
                                    }
                                    else
                                    {
                                        // We don't have a parent CSmilElement,
                                        // so we just take our parent's end time
                                        rulRemoveTime = ulParentEnd;
                                        retVal        = HXR_OK;
                                    }
                                }
                                else
                                {
                                    // Our parent is not frozen or held, so our
                                    // remove time is the end of our parent
                                    rulRemoveTime = ulParentEnd;
                                    retVal        = HXR_OK;
                                }
                            }
                        }
                        break;
#if defined(HELIX_FEATURE_SMIL2_TRANSITIONS)
                    case FillTransition:
                        {
                            // XXXMEH - temporary fix for fill="transition"
                            // XXXMEH - TODO
                            // The correct implementation for fill="transition" is to
                            // look through all currently scheduled transitions and find the
                            // next transition which begins after the end of the active
                            // duration of this element covering any part of the screen in
                            // which this element is playing. This transition could be
                            // on a media element which is outside of this element's
                            // time container. However, in most cases, the element will
                            // be a sibling of this element. Therefore, temporarily, we
                            // will simply look at the elements which are children
                            // of our time container. If we don't find a transIn for one
                            // on one of these elements, then we treat this as fill="remove".
                            UINT32    ulTime = 0;
                            HX_RESULT rv     = getNextTransitionEnd(pElement, ulTime);
                            if (SUCCEEDED(rv))
                            {
                                rulRemoveTime = ulTime;
                                retVal        = HXR_OK;
                            }
                            else
                            {
                                // We didn't find a transition inside our time
                                // time container, so treat this as fill="remove".
                                UINT32 ulStop = 0;
                                retVal        = pElement->getCurrentScheduledStopTime(ulStop);
                                if (SUCCEEDED(retVal))
                                {
                                    rulRemoveTime = ulStop;
                                }
//                                // XXXMEH - DELAY_DUR_FIX
//                                rulRemoveTime = pElement->m_ulDelay + pElement->m_ulDuration -
//                                                (pElement->m_bBeginOffsetSet ? pElement->m_lBeginOffset : 0);
//                                retVal        = HXR_OK;
                            }
                        }
                        break;
#endif /* #if defined(HELIX_FEATURE_SMIL2_TRANSITIONS) */
                }
            }
            else
            {
                // XXXMEH - TODO - final fix for erase="never". Spec says:
                // "The effects of erase=never apply after the active duration
                //  of the media object and any fill period (defined by SMIL
                //  Timing and Synchronization), and only until other media
                //  plays to the region targeted by the media object, or until
                //  the same media object restarts."
                //
                // We will implement erase="never" by setting the
                // remove time for erase="never" elements as WAY_IN_THE_FUTURE.
                rulRemoveTime = WAY_IN_THE_FUTURE;
                // Clear the return value
                retVal = HXR_OK;
            }
        }
    }

    return retVal;
}

void
CSmilParser::InitPersistent(UINT32  ulPersistentComponentID,
                      IHXValues*    pProperties)
{
    m_ulPersistentComponentID = ulPersistentComponentID;

    if (pProperties)
    {
      pProperties->GetPropertyULONG32("Delay", m_ulPersistentComponentDelay);
      pProperties->GetPropertyULONG32("Duration", m_ulPersistentComponentDuration);
      UINT32 ulElementWithinTag = 0;
      pProperties->GetPropertyULONG32("ElementWithinTag", ulElementWithinTag);
      m_elementWithinTag = (ElementWithinTag)ulElementWithinTag;
    }
}



HX_RESULT
CSmilParser::addResumeEvent(SmilTimeValue* pTimeValue,
                          REF(BOOL) bOldResumeEventWasRemoved)
{
    return addResumeOrUndeferEvent(pTimeValue, bOldResumeEventWasRemoved,
          TRUE);
}

HX_RESULT
CSmilParser::addUndeferEvent(SmilTimeValue* pTimeValue,
                          REF(BOOL) bOldDeferEventWasRemoved)
{
    return addResumeOrUndeferEvent(pTimeValue, bOldDeferEventWasRemoved,
          FALSE);
}

// /This function inserts the time value into the pending list, but first
// checks to see if there is already a pending time value for this element;
// if so, it removes the old one.  It returns TRUE in
// bOldResumeEventWasRemoved if existing one was removed herein:
HX_RESULT
CSmilParser::addResumeOrUndeferEvent(SmilTimeValue* pTimeValue,
                          REF(BOOL) bOldEventWasRemoved,
                          BOOL bIsResumeEvent)
{
#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG_EXCL_TRACK_REMOVAL)
{
    FILE* f1 = ::fopen("c:\\smil2excl.txt", bFirstExclTrackChangeDebugOut?"w":"a+");
    bFirstExclTrackChangeDebugOut = FALSE;
    ::fprintf(f1, "add%sEvent(timeVal's = %s)\n", bIsResumeEvent?"Resume":"Undefer",
      (const char*)pTimeValue->m_pElement->m_pNode->m_id);
    ::fclose(f1);
}
#endif
    HX_RESULT pnr = HXR_OK;
    bOldEventWasRemoved = FALSE;
    if (pTimeValue)
    {
      LISTPOSITION lPos = NULL;
      if (!m_pBeginEventList)
      {
          m_pBeginEventList = new CHXSimpleList;
      }
      if (!m_pBeginEventList)
      {
          pnr = HXR_OUTOFMEMORY;
      }
      else
      {
          lPos = m_pBeginEventList->GetHeadPosition();
      }

      while (lPos)
      {
          LISTPOSITION lPosOfCurTmpVal = lPos;
          // /Gets val at lPos and then moves lPos to next node in list:
          SmilTimeValue* pTmpValue =
                (SmilTimeValue*)m_pBeginEventList->GetNext(lPos);
          if (!pTmpValue  ||  !pTmpValue->m_pElement)
          {
            // /List shouldn't have an empty node or one w/NULL element:
            HX_ASSERT(pTmpValue  &&  pTmpValue->m_pElement);
            continue;
          }
          if (pTimeValue->m_pElement == pTmpValue->m_pElement)
          {
            if ((bIsResumeEvent  &&  pTmpValue->isResumeEvent())  ||
                  (!bIsResumeEvent  &&  pTmpValue->isUndeferEvent()) )
            {
#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG_EXCL_TRACK_REMOVAL)
{
    FILE* f1 = ::fopen("c:\\smil2excl.txt", bFirstExclTrackChangeDebugOut?"w":"a+");
    bFirstExclTrackChangeDebugOut = FALSE;
    ::fprintf(f1, "\tadd%sEvent(timeVal's = %s): removing %s %sEvent from event list\n",
      bIsResumeEvent? "Resume":"Undefer",
      (const char*)pTimeValue->m_pElement->m_pNode->m_id,
      (const char*)pTmpValue->m_pElement->m_pNode->m_id);
      bIsResumeEvent? "resume":"undefer",
    ::fclose(f1);
}
#endif
                // /Get rid of it from the pending list:
                m_pBeginEventList->RemoveAt(lPosOfCurTmpVal);
                    // Get rid of it from the map
                    removeFromBeginOrEndTimeMap(pTmpValue, SmilBeginTimeList);
                bOldEventWasRemoved = TRUE;
                break;
            }
          }
      }
      // /XXXEH- TODO: add this in temporal order; break out the bottom
      // part of CSmilParser::insertElementWithPendingBeginOrEnd() into
      // its own function 'insertPendingInTemporalOrder()' or the like:
      // /OK to insert the new one now:
      pnr = addBeginEventElement(pTimeValue);

      // /Next, we have to add this as a begin time to the element's
      // begin-time list so it can get resolved the normal way when the
      // resume|defer event happens, which is when the interrupting sibling ends:
      if (HXR_OK == pnr  &&  pTimeValue->m_pElement)
      {
          if (!pTimeValue->m_pElement->m_pBeginTimeList)
          {
            pTimeValue->m_pElement->m_pBeginTimeList = new CHXSimpleList;
          }
          if (!pTimeValue->m_pElement->m_pBeginTimeList)
          {
            pnr = HXR_OUTOFMEMORY;
          }
          else
          {
            CHXSimpleList* pBeginList =
                  pTimeValue->m_pElement->m_pBeginTimeList;
            // /First, remove any resume events in the list:
            lPos = pBeginList->GetHeadPosition();

            while (lPos)
            {
                LISTPOSITION lPosOfCurTmpVal = lPos;
                // /Gets val at lPos and then moves lPos to next node in list:
                SmilTimeValue* pTmpValue =
                      (SmilTimeValue*)pBeginList->GetNext(lPos);
                if (!pTmpValue  ||  !pTmpValue->m_pElement)
                {
                  HX_ASSERT(pTmpValue  &&  pTmpValue->m_pElement);
                  continue;
                }
                if ((bIsResumeEvent  &&  pTmpValue->isResumeEvent())  ||
                  (!bIsResumeEvent  &&  pTmpValue->isUndeferEvent()) )
                {
                  // /Get rid of it from the list:
                  pBeginList->RemoveAt(lPosOfCurTmpVal);
                  bOldEventWasRemoved = TRUE;
                  break;
                }
            }
            // /Now it's OK to stick this new one in:
            pBeginList->AddTail(pTimeValue);
          }
      }
    }

    return pnr;
}


HX_RESULT
CSmilParser::parseDuration(const char* pDuration, CSmilElement* 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)
    {
      rc = parseSmil1SyncbaseValue(pCh, pElement, nTag);
      if (SMILSyncAttrEnd == nTag)
      {
          // /This is needed to trick the core into letting the
          // media play when we give it a yet-unresolved end:
          pElement->m_ulDuration = WAY_IN_THE_FUTURE;
      }
    }
    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, "all") == 0)
    {
      if(nTag == SMILSyncAttrEndsync)
      {
          pElement->m_nEndsyncEventSourceTag = SMILEventSourceAll;
      }
    }
    else if(strcmp(pCh, "indefinite") == 0)
    {
      // /Fixes PR-28674: dur="indefinite" should be allowed on time
      // containers, too:
#if defined(DONT_ALLOW_INDEFINITE_ON_TIME_CONTAINERS)
      // /XXXEH- TODO: find out from Henry Ping why we're limiting this:
      // this is contrary to the SMIL 1 and SMIL 2 specs:
      if (pElement->m_pNode->m_tag == SMILSeq ||
          pElement->m_pNode->m_tag == SMILExcl  ||
          pElement->m_pNode->m_tag == SMILPar)
      {
          rc = HXR_FAIL;
          CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
          errHandler.ReportError(SMILErrorIndefiniteNotSupported, NULL,
            pElement->m_pNode->m_ulTagStartLine);
      }
      else
#endif
      if (nTag == SMILSyncAttrMax)
      {
          ; // /Nothing to do;"indefinite" is equivalent to ignoring max value
      }
      else if (nTag == SMILSyncAttrEnd  ||
            // /end AND dur can each have the value "indefinite":
            nTag == SMILSyncAttrDur)
      {
          // /NOT setting the following to TRUE helps fix
          // SMIL 2.0 Interop Timing #18.8 where setDuration()
          // wasn't being called on indef-dur element in
          // CSmilParser::durationResolved() so parent never
          // got a chance to properly calculate its endsync behavior.
          // (The player won't go from "live" to a duration once
          // a stream that's still playing is declared to be "live"
          // via the setProperty...("indefiniteduration"...) in
          // handleSource() in SMIL doc renderer:
#if defined(XXXEH_OLD_SPECIAL_CASE_HANDLING_OF_INDEF_DUR)
          pElement->m_bIndefiniteDuration = TRUE;
#endif
          // /XXXEH- this is needed to trick the core into letting the
          // media play when we give it an indefinite end:
          pElement->m_ulDuration = WAY_IN_THE_FUTURE;
      }
    }
    else if (strncmp(pCh, "marker=", 7) == 0 &&
             (nTag == SMILSyncAttrClipBegin ||
              nTag == SMILSyncAttrClipEnd))
    {
        // Advance past the "marker="
        pCh += 7;
        // Parse the marker
        if (nTag == SMILSyncAttrClipBegin)
        {
            rc = parseMarkerClipBeginEnd(pCh,
                                         pElement->m_pszClipBeginMarkerName,
                                         pElement->m_pszClipBeginExternalMarkerFileName);
            if (SUCCEEDED(rc))
            {
                pElement->m_bClipBeginUsesMarker = TRUE;
                if (pElement->m_pszClipBeginExternalMarkerFileName)
                {
                    pElement->m_bUsesExternalMediaMarkerFile = TRUE;
                    // Add this element to the list
                    if (!m_pClipBeginMarkerList)
                    {
                        m_pClipBeginMarkerList = new CHXSimpleList();
                    }
                    if (m_pClipBeginMarkerList)
                    {
                        m_pClipBeginMarkerList->AddTail((void*) pElement);
                    }
                }
            }
        }
        else
        {
            rc = parseMarkerClipBeginEnd(pCh,
                                         pElement->m_pszClipEndMarkerName,
                                         pElement->m_pszClipEndExternalMarkerFileName);
            if (SUCCEEDED(rc))
            {
                pElement->m_bClipEndUsesMarker = TRUE;
                if (pElement->m_pszClipEndExternalMarkerFileName)
                {
                    pElement->m_bUsesExternalMediaMarkerFile = TRUE;
                    // Add this element to the list
                    if (!m_pClipEndMarkerList)
                    {
                        m_pClipEndMarkerList = new CHXSimpleList();
                    }
                    if (m_pClipEndMarkerList)
                    {
                        m_pClipEndMarkerList->AddTail((void*) pElement);
                    }
                }
            }
        }
    }
    else if (strcmp(pCh, "media") == 0)
    {
      if(nTag == SMILSyncAttrMin)
      {
          pElement->m_bUseMediaDurForMinDur = TRUE;
      }
      else if(nTag == SMILSyncAttrMax)
      {
          pElement->m_bUseMediaDurForMaxDur = TRUE;
      }
    }
    else
    {
      UINT32 ulClockValue = 0;

      // /First, see if this is pointing to a child id:
      // Don't waste time if it doesn't start with alpha char, '_', or ':'
      BOOL bHandled = FALSE;
        if (':' == *pCh  ||  '_' == *pCh  ||  isalpha(*pCh) )
      {
          if (HXR_OK == parseSmil1SyncbaseValue(pCh, pElement, nTag))
          {
            bHandled = TRUE;
          }

      }

      if(!bHandled)
      {
          if (HXR_OK == parseClockValue(pCh, ulClockValue))
          {
            switch(nTag)
            {
                case SMILSyncAttrBegin:
                {
                  pElement->m_lBeginOffset = (INT32)ulClockValue;
                  pElement->m_bBeginOffsetSet = TRUE;
                  // /This needs to be handled here for time containers:
                  pElement->m_bCurBeginIsOffsetFromSyncBase = TRUE;
                  pElement->m_ulBeginOffsetFromSyncBase =
                        pElement->m_lBeginOffset;
                }
                break;

                case SMILSyncAttrEnd:
                {
                  pElement->m_lEndOffset = (INT32)ulClockValue;
                  pElement->m_bEndOffsetSet = TRUE;
                }
                break;

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

                case SMILSyncAttrMin:
                {
                  // /Spec says that, if min>max, we should ignore
                  // both of their values:
                  if (pElement->m_ulMaxActiveDur != ((UINT32)-1)  &&
                        pElement->m_ulMaxActiveDur < ulClockValue)
                  {
                      // /Reset max to essentially indefinite:
                      pElement->m_ulMaxActiveDur = ((UINT32)-1);
                  }
                  else // /OK to set min:
                  {
                      pElement->m_ulMinActiveDur = ulClockValue;
                  }
                }
                break;

                case SMILSyncAttrMax:
                {
                  // /Spec says that, if min>max, we should ignore
                  // both of their values:
                  if (pElement->m_ulMinActiveDur != 0  &&
                        pElement->m_ulMinActiveDur > ulClockValue)
                  {
                      // /Reset min to 0:
                      pElement->m_ulMinActiveDur = 0;
                  }
                  else  // /OK to set max:
                  {
                      pElement->m_ulMaxActiveDur = ulClockValue;
                  }
                }
                break;

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

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

                case SMILSyncAttrClipEnd:
                {
                  pElement->m_ulClipEnd = ulClockValue;
                }
                break;

                case SMILSyncAttrSyncTolerance:
                {
                  pElement->m_ulSyncTolerance = ulClockValue;
                }
                break;

                case SMILSyncAttrSyncToleranceDefault:
                {
                  pElement->m_ulSyncToleranceDefault = ulClockValue;
                }
                break;

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

HX_RESULT CSmilParser::parseMarkerClipBeginEnd(const char* pszStr,
                                               REF(char*)  rpszMarkerName,
                                               REF(char*)  rpszExtFileName)
{
    HX_RESULT retVal = HXR_OK;

    if (pszStr)
    {
        // Parse the marker
        CHXString cMarker;
        CHXString cExtFileName;
        BOOL      bIsExternal = FALSE;
        retVal = parseMarkerURI(pszStr, cMarker, bIsExternal, cExtFileName);
        if (SUCCEEDED(retVal))
        {
            // Copy the marker name
            HX_VECTOR_DELETE(rpszMarkerName);
            rpszMarkerName = new char [cMarker.GetLength() + 1];
            if (rpszMarkerName)
            {
                strcpy(rpszMarkerName, cMarker); /* Flawfinder: ignore */
                // If the marker is external, then copy the external file name
                if (bIsExternal)
                {
                    HX_VECTOR_DELETE(rpszExtFileName);
                    rpszExtFileName = new char [cExtFileName.GetLength() + 1];
                    if (rpszExtFileName)
                    {
                        // Copy the string
                        strcpy(rpszExtFileName, cExtFileName); /* Flawfinder: ignore */
                    }
                    else
                    {
                        retVal = HXR_OUTOFMEMORY;
                    }
                }
            }
            else
            {
                retVal = HXR_OUTOFMEMORY;
            }
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT
CSmilParser::adjustDuration(CSmilElement* pElement)
{
    HX_RESULT rc = HXR_OK;

    // check for duration errors
    if(pElement->m_bEndOffsetSet)
    {
      // /We weren't taking MIN(end-b, dur) as we should in the case where
      // there is no begin offset set.  So, <ref dur="5s" end="8s"/> now
      // plays for 5s but used to play for 8s, while adding begin="0s" to
      // that *did* work before (and still does):
      LONG32 lBeginOffset = 0;
      if(pElement->m_bBeginOffsetSet)
      {
          lBeginOffset = pElement->m_lBeginOffset;
      }
      if (!pElement->m_bIndefiniteBegin) // /Else all this is moot.
      {
          if(pElement->m_lEndOffset <
            pElement->m_lBeginOffset)
          {
            pElement->m_ulDuration = 0;
            goto exit;
          }
          if(pElement->m_ulDuration != (UINT32)-1)
          {
            if((INT32)pElement->m_ulDuration !=
                pElement->m_lEndOffset -
                pElement->m_lBeginOffset)
            {
                // 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_lBeginOffset + (INT32)pElement->m_ulDuration >
                  pElement->m_lEndOffset)
                {
                  // we want to override the duration, because it is
                  // greater than the end offset.
                  INT32 offset = pElement->m_lEndOffset -
                      pElement->m_lBeginOffset;
                  pElement->m_ulDuration = (offset >= 0 ? (UINT32)offset: 0);

                }
                else
                {
                  // else we want to use the current duration,
                  // and override the end offset.
                  pElement->m_lEndOffset = pElement->m_lBeginOffset
                      + pElement->m_ulDuration;
                }

                goto exit;
            }
          }
      }
      else // /No valid begin offset
      {
          HX_ASSERT(0  &&  "ehodge: can't adjust dur when begin is indef");
#if defined(XXXEH_REMOVE_THIS_IF_TESTING_PROVES_OK_TO)
          if(pElement->m_ulDuration != (UINT32)-1)
          {
            if(pElement->m_lEndOffset <
                (INT32)pElement->m_ulDuration)
            {
                pElement->m_ulDuration = (pElement->m_lEndOffset >= 0 ?
                  (UINT32)pElement->m_lEndOffset: 0);
                goto exit;
            }
          }
#endif
      }
    }

    // adjust for begin/end/dur attributes
    if(pElement->m_ulDuration == (UINT32)-1) // duration not set
    {
      if(pElement->m_bEndOffsetSet)
      {
          // has an end but no duration
          if(pElement->m_bBeginOffsetSet)
          {
            // /We need to do the following to protect from the case
            // where (end-begin) overflows LONG32 val and otherwise
            // results in a bad duration value:
            double dEndOffset = (double)
                  ((float)pElement->m_lEndOffset);
            double dBeginOffset = (double)
                  ((float)pElement->m_lBeginOffset);
            double diff = dEndOffset - dBeginOffset;
            BOOL bDurationTooLarge = (diff > 0x7FFFFFFF);

            if (bDurationTooLarge)
            {
                // /XXXEH- TODO: figure out a way to have a longer
                // duration than 23 days (as in SMIL 2.0 Interop
                // Timing test #26.1):
                if (dBeginOffset < 0  &&
                      pElement->m_lEndOffset<WAY_IN_THE_FUTURE)
                {
                  pElement->m_ulDuration = pElement->m_lEndOffset;
                  // /Now, we'll use negative begin as our clip-begin
                  // and then set the begin to 0:
                  if ((UINT32)-1 == pElement->m_ulClipBegin)
                  {
                      pElement->m_ulClipBegin =
                            (UINT32)(-pElement->m_lBeginOffset);
                  }
                  else // /Add to it:
                  {
                      pElement->m_ulClipBegin -=
                            pElement->m_lBeginOffset;
                  }
                  pElement->m_lBeginOffset = 0;
                }
                else
                {
                  pElement->m_ulDuration = WAY_IN_THE_FUTURE;
                }
            }
            else
            {
                pElement->m_ulDuration = (pElement->m_lEndOffset -
                              pElement->m_lBeginOffset >= 0 ?
                              (UINT32)(pElement->m_lEndOffset -
                              pElement->m_lBeginOffset): 0);
            }
          }
          else
          {
            pElement->m_ulDuration = (pElement->m_lEndOffset >= 0 ?
                              (UINT32)pElement->m_lEndOffset: 0);
          }
      }
    }
    else    // explicit duration set
    {
      if(pElement->m_bEndOffsetSet)
      {
          // has a duration and an end
          UINT32 ulDur = 0;
          if(pElement->m_bBeginOffsetSet)
          {
            ulDur = (pElement->m_lEndOffset -
                     pElement->m_lBeginOffset >= 0 ?
                  (UINT32)(pElement->m_lEndOffset -
                     pElement->m_lBeginOffset): 0);
          }
          else
          {
            ulDur = (pElement->m_lEndOffset >= 0 ?
                  (UINT32)pElement->m_lEndOffset: 0);
          }
          pElement->m_ulDuration = ulDur;
      }
    }

exit:
    return rc;
}

void
CSmilParser::badAttributeError(SMILNodeTag tag, const char* pAttrName,
                         UINT32 ulLineNumber, BOOL bJustStore)
{
    const char* pTagName = "unknown";
    // get tag name from table
    int i = 0;
    SMILNodeTag thisTag = SmilTagTable[i].m_tag;
    while(thisTag != SMILUnknown)
    {
      if(thisTag == tag)
      {
          pTagName = SmilTagTable[i].m_name;
          break;
      }
      ++i;
      thisTag = SmilTagTable[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)
    {
      CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
      errHandler.ReportError(SMILErrorUnrecognizedAttribute, tmpBuf, ulLineNumber);
    }
}

CSmilMeta*
CSmilParser::makeMeta(SMILNode* pNode)
{
    CSmilMeta* pMeta = new CSmilMeta(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)
      {
            SMIL2Attribute eAttr = getSMIL2Attribute(pName);
            switch(eAttr)
            {
                case SMIL2AttrName:
                  {
                    pMeta->m_name = (const char*)pBuf->GetBuffer();
                  }
                    break;
                case SMIL2AttrContent:
                  {
                    pMeta->m_content = (const char*)pBuf->GetBuffer();
                  }
                    break;
            }
          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;
}

CSmilMetadata*
CSmilParser::makeMetadata(SMILNode* pNode)
{
    CSmilMetadata* pMetadata = new CSmilMetadata(pNode);
    return pMetadata;
}

CSmilRendererPreFetch*
CSmilParser::makeRendererPreFetch(SMILNode* pNode)
{
    CSmilRendererPreFetch* pRenderer =
      new CSmilRendererPreFetch(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;
}

CSmilRootLayout*
CSmilParser::makeRootLayout(SMILNode* pNode)
{
    CSmilRootLayout* pRoot  = NULL;
    HX_RESULT        retVal = HXR_OK;

    if (pNode && pNode->m_pValues)
    {
        if (!m_bSMILRootLayoutAlreadyFound)
        {
            pRoot = new CSmilRootLayout(pNode);
            if (pRoot)
            {
                BOOL        bFoundBGColor = FALSE;
                const char* pName         = NULL;
                IHXBuffer* pBuf          = NULL;
                HX_RESULT rc = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);
                while(SUCCEEDED(rc) && SUCCEEDED(retVal))
                {
                const char* pszValue =
                      NULL!=pBuf?(const char*) pBuf->GetBuffer():NULL;

                    // Check if the attribute is a legal namespaced attribute
                    const char*     pszNSAttr = NULL;
                    CNamespaceInfo* pNSInfo = getNamespaceInfo(pName, pszNSAttr);

                    SMIL2Attribute eAttr = getSMIL2Attribute(pName);
                    switch (eAttr)
                    {
                        case SMIL2AttrWidth:
                            {
                                retVal = parseRegionDimension(pszValue,
                                                              pRoot->m_dWidth,
                                                              pRoot->m_eWidthType);
                            }
                            break;
                        case SMIL2AttrHeight:
                            {
                                retVal = parseRegionDimension(pszValue,
                                                              pRoot->m_dHeight,
                                                              pRoot->m_eHeightType);
                            }
                            break;
                        case SMIL2AttrBackgroundColor:
                            {
                                bFoundBGColor = TRUE;
                                retVal = parseColor(pszValue,
                                                    pRoot->m_ulBackgroundColor,
                                                    pRoot->m_eBackgroundColorType);
                            }
                            break;
                        default:
                            {
                                if (!strcmp(pName, "background-color"))
                                {
                                    // This check makes the parse prefer backgroundColor over
                                    // background-color if both are present
                                    if (!bFoundBGColor)
                                    {
                                        retVal = parseColor(pszValue,
                                                            pRoot->m_ulBackgroundColor,
                                                            pRoot->m_eBackgroundColorType);
                                    }
                                }
                                else if (pNSInfo && pszNSAttr &&
                                         (pNSInfo->m_eNamespace == NamespaceSizeControl ||
                                          pNSInfo->m_eNamespace == NamespaceSendTo ||
                                          pNSInfo->m_eNamespace == NamespaceAllSMIL2Extensions))
                                {
                                    if (!strcmp(pszNSAttr, "resizeBehavior"))
                                    {
                                        if (!strcmp(pszValue, "zoom"))
                                        {
                                            pRoot->m_eResizeBehavior = ResizeZoom;
                                        }
                                        else if (!strcmp(pszValue, "percentOnly"))
                                        {
                                            pRoot->m_eResizeBehavior = ResizePercentOnly;
                                        }
                                        else
                                        {
                                            retVal = HXR_FAIL;
                                        }
                                    }
                                    else if (!strcmp(pszNSAttr, "contextWindow"))
                                    {
                                        if (!strcmp(pszValue, "auto"))
                                        {
                                            pRoot->m_eContextWindow = ContextWindowAuto;
                                        }
                                        else if (!strcmp(pszValue, "openAtStart"))
                                        {
                                            pRoot->m_eContextWindow = ContextWindowOpenAtStart;
                                        }
                                        else
                                        {
                                            retVal = HXR_FAIL;
                                        }
                                    }
                                }
                            }
                    }

                    if (FAILED(retVal))
                    {
                        CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                        errHandler.ReportError(SMILErrorBadAttribute,
                                               pszValue,
                                               pNode->m_ulTagStartLine);
                    }

                    HX_RELEASE(pBuf);

                    if (SUCCEEDED(retVal))
                    {
                        rc = pNode->m_pValues->GetNextPropertyCString(pName, pBuf);
                    }
                }
            }
            else
            {
                retVal = HXR_OUTOFMEMORY;
            }
        }
        else
        {
            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
            errHandler.ReportError(SMILErrorUnexpectedTag,
                                   (const char*) pNode->m_name,
                                   pNode->m_ulTagStartLine);
            retVal = HXR_FAIL;
        }
    }

    if (SUCCEEDED(retVal))
    {
        m_bSMILRootLayoutAlreadyFound = TRUE;
    }
    else
    {
        HX_DELETE(pRoot);
    }

    return pRoot;
}

CSmilRegion*
CSmilParser::makeRegion(SMILNode* pNode)
{
    HX_RESULT    retVal  = HXR_OK;
    CSmilRegion* pRegion = NULL;

    if (pNode && pNode->m_pValues)
    {
        pRegion = new CSmilRegion(pNode);
        if (pRegion)
        {
            BOOL        bParsedBGColor = FALSE;
            BOOL        bParsedOpacity = FALSE;
            BOOL        bGotNewStyleBG = FALSE;
            UINT32      ulOpacity      = 0;
            const char* pName          = NULL;
            IHXBuffer* pBuf           = NULL;
            HX_RESULT   rc             = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);
            while(SUCCEEDED(rc) && SUCCEEDED(retVal))
            {
                // Get the attribute value
            const char* pszValue =
                  NULL!=pBuf?(const char*) pBuf->GetBuffer():NULL;
                // Map the attribute name
                SMIL2Attribute eAttr = getSMIL2Attribute(pName);
                switch (eAttr)
                {
                    case SMIL2AttrLeft:
                        {
                            retVal = parseRegionDimension(pszValue,
                                                          pRegion->m_Rect.m_dLeft,
                                                          pRegion->m_Rect.m_eLeftType);
                        }
                        break;
                    case SMIL2AttrTop:
                        {
                            retVal = parseRegionDimension(pszValue,
                                                          pRegion->m_Rect.m_dTop,
                                                          pRegion->m_Rect.m_eTopType);
                        }
                        break;
                    case SMIL2AttrRight:
                        {
                            retVal = parseRegionDimension(pszValue,
                                                          pRegion->m_Rect.m_dRight,
                                                          pRegion->m_Rect.m_eRightType);
                        }
                        break;
                    case SMIL2AttrBottom:
                        {
                            retVal = parseRegionDimension(pszValue,
                                                          pRegion->m_Rect.m_dBottom,
                                                          pRegion->m_Rect.m_eBottomType);
                        }
                        break;
                    case SMIL2AttrWidth:
                        {
                            retVal = parseRegionDimension(pszValue,
                                                          pRegion->m_Rect.m_dWidth,
                                                          pRegion->m_Rect.m_eWidthType);
                        }
                        break;
                    case SMIL2AttrHeight:
                        {
                            retVal = parseRegionDimension(pszValue,
                                                          pRegion->m_Rect.m_dHeight,
                                                          pRegion->m_Rect.m_eHeightType);
                        }
                        break;
                    case SMIL2AttrZ_Index:
                        {
                            retVal = parseZIndex(pszValue,
                                                 pRegion->m_lZIndex,
                                                 pRegion->m_eZIndexType);
                        }
                        break;
                    case SMIL2AttrFit:
                        {
                            retVal = parseFit(pszValue, pRegion->m_eFit);
                        }
                        break;
                    case SMIL2AttrShowBackground:
                        {
                            if (!strcmp(pszValue, "always"))
                            {
                                pRegion->m_eShowBackground = ShowBackgroundAlways;
                            }
                            else if (!strcmp(pszValue, "whenActive"))
                            {
                                pRegion->m_eShowBackground = ShowBackgroundWhenActive;
                            }
                            else
                            {
                                retVal = HXR_FAIL;
                            }
                        }
                        break;
                    case SMIL2AttrSoundLevel:
                        {
                            char*  pEndPtr = NULL;
                            double dVal    = strtod(pszValue, &pEndPtr);
                            if (pEndPtr && *pEndPtr == '%')
                            {
                                pRegion->m_dSoundLevel = dVal;
                            }
                            else
                            {
                                retVal = HXR_FAIL;
                            }
                        }
                        break;
                    case SMIL2AttrRegionName:
                        {
                            pRegion->m_RegionName           = pszValue;
                            pRegion->m_bRegionNameSpecified = TRUE;
                        }
                        break;
                    case SMIL2AttrBackgroundColor:
                        {
                            // Get the background color
                            retVal = parseColor(pszValue,
                                                pRegion->m_ulBackgroundColor,
                                                pRegion->m_eBackgroundColorType);
                            if (SUCCEEDED(retVal))
                            {
                                bParsedBGColor = TRUE;
                                bGotNewStyleBG = TRUE;
                            }
                        }
                        break;
                    default:
                        {
                            if (strcmp(pName, "background-color") == 0 &&
                                !bGotNewStyleBG)
                            {
                                // Get the background color
                                retVal = parseColor(pszValue,
                                                    pRegion->m_ulBackgroundColor,
                                                    pRegion->m_eBackgroundColorType);
                                if (SUCCEEDED(retVal))
                                {
                                    bParsedBGColor = TRUE;
                                }
                            }
                            else
                            {
                                // Check if the attribute is a legal namespaced attribute
                                const char*     pszNSAttr = NULL;
                                CNamespaceInfo* pNSInfo = getNamespaceInfo(pName, pszNSAttr);

                                if (pNSInfo)
                                {
                                    if (pNSInfo->m_eNamespace == NamespaceAlphaControl ||
                                        pNSInfo->m_eNamespace == NamespaceAllSMIL2Extensions)
                                    {
                                        if(!strcmp(pszNSAttr, "opacity"))
                                        {
                                            // Parse the opacity
                                            retVal = parseOpacity(pszValue, ulOpacity);
                                            if (SUCCEEDED(retVal))
                                            {
                                                bParsedOpacity = TRUE;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                }

                HX_RELEASE(pBuf);

                if (SUCCEEDED(retVal))
                {
                    rc = pNode->m_pValues->GetNextPropertyCString(pName, pBuf);
                }
                else
                {
                    CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                    errHandler.ReportError(SMILErrorBadAttribute,
                                           pName,
                                           pRegion->m_pNode->m_ulTagStartLine);
                }
            }
            if (SUCCEEDED(retVal))
            {
                // Compute alpha from opacity - the value
                // coming out of parseOpacity() is guaranteed to
                // be in the range [0,255]
                UINT32 ulAlpha = 255 - ulOpacity;
                // Process the opacity and background color. By default,
                // m_ulBackgroundColor = 0xFF000000 (transparent). Here are
                // the four cases:
                //
                // 1) Both backgroundColor and rn:opacity are specified - we
                //    should use the RRGGBB from parseColor and the opacity
                //    value from rn:opacity
                // 2) backgroundColor is specified, but rn:opacity is not.
                //    The resulting color must be opaque in order to be
                //    backwards-compatible. Since parseColor() puts in 0x00
                //    for the alpha channel (opaque), then we don't have to
                //    do anything here.
                // 3) rn:opacity was specified, but backgroundColor was not.
                //    Kinda unclear what to do here, since the backgroundColor
                //    default is "transparent", but I will take this to mean
                //    that the author wants this to be "partially transparent".
                //    So we will use the alpha from opacity and put "#000000"
                //    (black) for the color. We just as easily chould use
                //    "#FFFFFF" (white).
                // 4) Neither backgroundColor nor rn:opacity was specified.
                //    We have to go with the default of backgroundColor, which
                //    is "transparent".
                if (bParsedBGColor)
                {
                    if (bParsedOpacity)
                    {
                        // Both backgroundColor and rn:opacity are specified
                        pRegion->m_ulBackgroundColor = (pRegion->m_ulBackgroundColor & 0x00FFFFFF) |
                                                       (ulAlpha << 24);
                    }
                    else
                    {
                        // backgroundColor is specified but not rn:opacity. So for
                        // backwards-compatibility, the resulting color must be
                        // opaque. So zero out the alpha channel of the color.
                        // We really shouldn't have to do this, since parseColor
                        // generally puts 0x00 in the alpha, but we force it here
                        // "just to be sure".
                        // The only exception to this rule is if the
                        // backgroundColor is explicity specified as
                        // backgroundColor="transparent"
                        if (pRegion->m_eBackgroundColorType != CSS2TypeTransparent)
                        {
                            pRegion->m_ulBackgroundColor &= 0x00FFFFFF;
                        }
                    }
                }
                else
                {
                    if (bParsedOpacity)
                    {
                        // rn:opacity was specified, but not backgroundColor, so
                        // we need to insert the alpha value into the backgroundColor
                        // We will let the color be #000000.
                        pRegion->m_ulBackgroundColor = (ulAlpha << 24);
                    }
                    else
                    {
                        // Neither rn:opacity was specified nor backgroundColor, so
                        // we must take the backgroundColor default, which is "transparent".
                        // We really should not have to do anything here, since the
                        // default of m_ulBackgroundColor in the constructor is 0xFF000000.
                        // However, we set it here "just to be sure".
                        pRegion->m_ulBackgroundColor = 0xFF000000;
                    }
                }
            }
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    if (FAILED(retVal))
    {
        HX_DELETE(pRegion);
    }

    return pRegion;
}

CSmilRegPoint*
CSmilParser::makeRegPoint(SMILNode* pNode)
{
    HX_RESULT      retVal = HXR_OK;
    CSmilRegPoint* pRegPt = NULL;

    if (pNode && pNode->m_pValues)
    {
        pRegPt = new CSmilRegPoint(pNode);
        if (pRegPt)
        {
            const char* pName = NULL;
            IHXBuffer* pBuf  = NULL;
            HX_RESULT   rc    = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);
            while(SUCCEEDED(rc) && SUCCEEDED(retVal))
            {
                // Get the attribute value
            const char* pszValue =
                  NULL!=pBuf?(const char*) pBuf->GetBuffer():NULL;
                // Get the SMIL2Attribute
                SMIL2Attribute eAttr = getSMIL2Attribute(pName);
                switch (eAttr)
                {
                    case SMIL2AttrLeft:
                        {
                            retVal = parseRegionDimension(pszValue,
                                                          pRegPt->m_RegPoint.m_dLeft,
                                                          pRegPt->m_RegPoint.m_eLeftType);
                        }
                        break;
                    case SMIL2AttrTop:
                        {
                            retVal = parseRegionDimension(pszValue,
                                                          pRegPt->m_RegPoint.m_dTop,
                                                          pRegPt->m_RegPoint.m_eTopType);
                        }
                        break;
                    case SMIL2AttrRight:
                        {
                            retVal = parseRegionDimension(pszValue,
                                                          pRegPt->m_RegPoint.m_dRight,
                                                          pRegPt->m_RegPoint.m_eRightType);
                        }
                        break;
                    case SMIL2AttrBottom:
                        {
                            retVal = parseRegionDimension(pszValue,
                                                          pRegPt->m_RegPoint.m_dBottom,
                                                          pRegPt->m_RegPoint.m_eBottomType);
                        }
                        break;
                    case SMIL2AttrRegAlign:
                        {
                            retVal = parseRegAlign(pszValue, pRegPt->m_RegPoint.m_eRegAlign);
                        }
                        break;
                }

                HX_RELEASE(pBuf);

                if (SUCCEEDED(retVal))
                {
                    rc = pNode->m_pValues->GetNextPropertyCString(pName, pBuf);
                }
                else
                {
                    CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                    errHandler.ReportError(SMILErrorBadAttribute,
                                           pName,
                                           pRegPt->m_pNode->m_ulTagStartLine);
                }
            }
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    if (FAILED(retVal))
    {
        HX_DELETE(pRegPt);
    }

    return pRegPt;
}

#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)

CSmilViewport*
CSmilParser::makeViewport(SMILNode* pNode)
{
    HX_RESULT      retVal = HXR_OK;
    CSmilViewport* pView  = NULL;

    if (pNode && pNode->m_pValues)
    {
        pView = new CSmilViewport(pNode);
        if (pView)
        {
            BOOL        bOldStyleBG = FALSE;
            const char* pName       = NULL;
            IHXBuffer* pBuf        = NULL;
            HX_RESULT   rc          = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);
            while(SUCCEEDED(rc) && SUCCEEDED(retVal))
            {
                // Get the attribute value
            const char* pszValue =
                  NULL!=pBuf?(const char*) pBuf->GetBuffer():NULL;

                // Check if the attribute is a legal namespaced attribute
                const char*     pszNSAttr = NULL;
                CNamespaceInfo* pNSInfo = getNamespaceInfo(pName, pszNSAttr);

                // Get the SMIL2Attribute
                SMIL2Attribute eAttr = getSMIL2Attribute(pName);
                switch (eAttr)
                {
                    case SMIL2AttrWidth:
                        {
                            retVal = parseRegionDimension(pszValue,
                                                          pView->m_dWidth,
                                                          pView->m_eWidthType);
                        }
                        break;
                    case SMIL2AttrHeight:
                        {
                            retVal = parseRegionDimension(pszValue,
                                                          pView->m_dHeight,
                                                          pView->m_eHeightType);
                        }
                        break;
                    case SMIL2AttrBackgroundColor:
                        {
                            retVal = parseColor(pszValue,
                                                pView->m_ulBackgroundColor,
                                                pView->m_eBackgroundColorType);
                        }
                        break;
                    case SMIL2AttrOpen:
                        {
                            if (!strcmp(pszValue, "onStart"))
                            {
                                pView->m_eOpen = ViewportOpenOnStart;
                            }
                            else if (!strcmp(pszValue, "whenActive"))
                            {
                                pView->m_eOpen = ViewportOpenWhenActive;
                            }
                            else
                            {
                                retVal = HXR_FAIL;
                            }
                        }
                        break;
                    case SMIL2AttrClose:
                        {
                            if (!strcmp(pszValue, "onRequest"))
                            {
                                pView->m_eClose = ViewportCloseOnRequest;
                            }
                            else if(!strcmp(pszValue, "whenNotActive"))
                            {
                                pView->m_eClose = ViewportCloseWhenNotActive;
                            }
                            else
                            {
                                retVal = HXR_FAIL;
                            }
                        }
                        break;
                    default:
                        {
                            if (!strcmp(pName, "background-color") &&
                                !bOldStyleBG)
                            {
                                retVal = parseColor(pszValue,
                                                    pView->m_ulBackgroundColor,
                                                    pView->m_eBackgroundColorType);
                            }
                            else if (pNSInfo && pszNSAttr &&
                                     (pNSInfo->m_eNamespace == NamespaceSizeControl ||
                                      pNSInfo->m_eNamespace == NamespaceSendTo ||
                                      pNSInfo->m_eNamespace == NamespaceAllSMIL2Extensions))
                            {
                                if (!strcmp(pszNSAttr, "resizeBehavior"))
                                {
                                    if (!strcmp(pszValue, "zoom"))
                                    {
                                        pView->m_eResizeBehavior = ResizeZoom;
                                    }
                                    else if (!strcmp(pszValue, "percentOnly"))
                                    {
                                        pView->m_eResizeBehavior = ResizePercentOnly;
                                    }
                                    else
                                    {
                                        retVal = HXR_FAIL;
                                    }
                                }
                                else if (!strcmp(pszNSAttr, "contextWindow"))
                                {
                                    if (!strcmp(pszValue, "auto"))
                                    {
                                        pView->m_eContextWindow = ContextWindowAuto;
                                    }
                                    else if (!strcmp(pszValue, "openAtStart"))
                                    {
                                        pView->m_eContextWindow = ContextWindowOpenAtStart;
                                    }
                                    else
                                    {
                                        retVal = HXR_FAIL;
                                    }
                                }
                            }
                        }
                }

                HX_RELEASE(pBuf);

                if (SUCCEEDED(retVal))
                {
                    rc = pNode->m_pValues->GetNextPropertyCString(pName, pBuf);
                }
                else
                {
                    CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                    errHandler.ReportError(SMILErrorBadAttribute,
                                           pName,
                                           pView->m_pNode->m_ulTagStartLine);
                }
            }
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    if (FAILED(retVal))
    {
        HX_DELETE(pView);
    }

    return pView;
}

#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */

CSmilCustomTest*
CSmilParser::makeCustomTest(SMILNode* pNode, REF(HX_RESULT) retVal)
{
    retVal = HXR_OK;
    CSmilCustomTest* pCustomTest  = NULL;

    if (pNode  &&  pNode->m_pValues)
    {
        pCustomTest = new CSmilCustomTest(pNode);
        if (pCustomTest)
        {
            const char* pName = NULL;
            IHXBuffer* pBuf  = NULL;
            HX_RESULT   rc    = pNode->m_pValues->GetFirstPropertyCString(
                pName, pBuf);
            while(SUCCEEDED(rc) && SUCCEEDED(retVal))
            {
                // Get the attribute value
            const char* pszValue =
                  NULL!=pBuf?(const char*) pBuf->GetBuffer():NULL;
            if (pszValue)
            {
                const char* pWhitespaceRemoved =
                      removeSurroundingWhitespace(pszValue);
                // Parse the attribute value
                if (0 == strcmp(pName, "defaultState"))
                {
                  if (!strcmp(pWhitespaceRemoved, "true"))
                  {
                      pCustomTest->m_bDefaultState = TRUE;
                  }
                  else if (!strcmp(pWhitespaceRemoved, "false"))
                  {
                      pCustomTest->m_bDefaultState = FALSE;
                  }
                  else
                  {
                      retVal = HXR_INVALID_PARAMETER;
                  }
                }
                else if (0 == strcmp(pName, "override"))
                {
                  if (!strcmp(pWhitespaceRemoved, "visible"))
                  {
                      pCustomTest->m_bOverrideVisible = TRUE;
                  }
                  else if (!strcmp(pWhitespaceRemoved, "hidden"))
                  {
                      pCustomTest->m_bOverrideVisible = FALSE;
                  }
                  else
                  {
                      retVal = HXR_INVALID_PARAMETER;
                  }
                }
                else if (0 == strcmp(pName, "uid"))
                {
                  pCustomTest->m_uid = pWhitespaceRemoved;
                }

                HX_RELEASE(pBuf);

                if (SUCCEEDED(retVal))
                {
                  rc = pNode->m_pValues->GetNextPropertyCString(
                        pName, pBuf);
                }
                else
                {
                  CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                  errHandler.ReportError(SMILErrorBadAttribute,
                        pName,
                        pCustomTest->m_pNode->m_ulTagStartLine);
                }
            }
            }
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    if (FAILED(retVal))
    {
        HX_DELETE(pCustomTest);
    }

    return pCustomTest;
}


HX_RESULT
CSmilParser::parseCoord(IHXBuffer* pBuf, REF(float) f)
{
    HX_RESULT ret = HXR_OK;
    double d = 0.0;
    ret = HXParseDouble((const char*)pBuf->GetBuffer(), d);
    if (d < -2.0 ||
      d > 2.0)
    {
      ret = HXR_INVALID_PARAMETER;
    }
    else
    {
      f = (float)d;
    }
    return ret;
}

HX_RESULT
CSmilParser::parseDigit(IHXBuffer* pBuf, REF(UINT32) ul)
{
    HX_RESULT ret = HXR_OK;
    INT32 num = 0;
    ret = HXParseDigit((const char*)pBuf->GetBuffer(), num);
    if (num < 0)
    {
      ret = HXR_INVALID_PARAMETER;
    }
    else
    {
      ul = (UINT32)num;
    }
    return ret;
}

#if defined(HELIX_FEATURE_SMIL2_TRANSITIONS)

CSmilTransition* CSmilParser::makeTransition(SMILNode* pNode, REF(HX_RESULT) retVal)
{
    // Initialize the error out parameter
    retVal = HXR_OK;
    // Initialize the return value
    CSmilTransition* pTrans = NULL;
    // Initialize our flags
    BOOL bTypePresent    = FALSE;
    BOOL bTypeLegal      = FALSE;
    BOOL bSubTypePresent = FALSE;
    BOOL bSubTypeLegal   = FALSE;
    // Make sure we have a node
    if (pNode && pNode->m_pValues)
    {
        // Create a CSmilTransition class
        pTrans = new CSmilTransition(pNode);
        if (pTrans)
        {
            // Loop through the attributes
            BOOL        bDuration       = FALSE;
            const char* pszName         = NULL;
            IHXBuffer* pValue          = NULL;
            HX_RESULT   rv              = pNode->m_pValues->GetFirstPropertyCString(pszName, pValue);
            while (SUCCEEDED(rv) && SUCCEEDED(retVal))
            {
                // Get the attribute value as a string
            const char* pszValue =
                  NULL!=pValue?(const char*) pValue->GetBuffer():NULL;
                // Get the SMIL2Attribute
                SMIL2Attribute eAttr = getSMIL2Attribute(pszName);
                // Switch based on attribute
                switch (eAttr)
                {
                    case SMIL2AttrDur:
                        {
                            retVal = parseDuration(pszValue, pTrans, SMILSyncAttrDur);
                            if (SUCCEEDED(retVal))
                            {
                                bDuration = TRUE;
                            }
                        }
                        break;
                    case SMIL2AttrType:
                        {
                            // Set the flag that there WAS a type attribute
                            bTypePresent = TRUE;
                            // Check for a legal type
                            if (isLegalTransitionType(pszValue))
                            {
                                // Assign the transition type
                                pTrans->m_Type = pszValue;
                                bTypeLegal     = TRUE;
                            }
                        }
                        break;
                    case SMIL2AttrSubtype:
                        {
                            pTrans->m_SubType = pszValue;
                            bSubTypePresent   = TRUE;
                        }
                        break;
                    case SMIL2AttrStartProgress:
                        {
                            retVal = HXParseDouble(pszValue, pTrans->m_dStartProgress);
                            if (SUCCEEDED(retVal))
                            {
                                if (pTrans->m_dStartProgress < 0.0 ||
                                    pTrans->m_dStartProgress > 1.0 ||
                                    pTrans->m_dStartProgress > pTrans->m_dEndProgress)
                                {
                                    // Illegal value for startProgress
                                    retVal = HXR_FAIL;
                                }
                            }
                        }
                        break;
                    case SMIL2AttrEndProgress:
                        {
                            retVal = HXParseDouble(pszValue, pTrans->m_dEndProgress);
                            if (SUCCEEDED(retVal))
                            {
                                if (pTrans->m_dEndProgress < 0.0 ||
                                    pTrans->m_dEndProgress > 1.0 ||
                                    pTrans->m_dEndProgress < pTrans->m_dStartProgress)
                                {
                                    // Illegal value for endProgress
                                    retVal = HXR_FAIL;
                                }
                            }
                        }
                        break;
                    case SMIL2AttrDirection:
                        {
                            if (!strcmp(pszValue, "forward"))
                            {
                                pTrans->m_eDirection = TransitionDirectionForward;
                            }
                            else if (!strcmp(pszValue, "reverse"))
                            {
                                pTrans->m_eDirection = TransitionDirectionReverse;
                            }
                            else
                            {
                                // Illegal value for direction
                                retVal = HXR_FAIL;
                            }
                        }
                        break;
                    case SMIL2AttrFadeColor:
                        {
                            retVal = HXParseColorUINT32(pszValue, pTrans->m_ulFadeColor);
                        }
                        break;
                    case SMIL2AttrHorzRepeat:
                        {
                            retVal = HXParseUINT32(pszValue, pTrans->m_ulHorzRepeat);
                        }
                        break;
                    case SMIL2AttrVertRepeat:
                        {
                            retVal = HXParseUINT32(pszValue, pTrans->m_ulVertRepeat);
                        }
                        break;
                    case SMIL2AttrBorderWidth:
                        {
                            retVal = HXParseUINT32(pszValue, pTrans->m_ulBorderWidth);
                        }
                        break;
                    case SMIL2AttrBorderColor:
                        {
                            if (!strcmp(pszValue, "blend"))
                            {
                                pTrans->m_bBlendBorder = TRUE;
                            }
                            else
                            {
                                retVal = HXParseColorUINT32(pszValue, pTrans->m_ulBorderColor);
                            }
                        }
                        break;
                }

                // Release the current attribute value buffer
                HX_RELEASE(pValue);
                // Get the next attribute
                if (SUCCEEDED(retVal))
                {
                    rv = pNode->m_pValues->GetNextPropertyCString(pszName, pValue);
                }
                else
                {
                    CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                    errHandler.ReportError(SMILErrorBadAttribute,
                                           pszName,
                                           pTrans->m_pNode->m_ulTagStartLine);
                }
            }
            if (SUCCEEDED(retVal))
            {
                // Check for required "type" attribute
                //
                // If "type" is present AND legal we process it.
                // If "type" is present but not legal, then this is probably
                //      some namespaced-extended attribute type, so we shouldn't
                //      fail, we should just not do the transition
                // If "type" is not present, then that's an error.
                if (bTypePresent && bTypeLegal)
                {
                    // Assign default duration if not specified
                    if (!bDuration)
                    {
                        pTrans->m_ulDuration = 1000;
                    }
                    // Has the "subtype" been specified?
                    if (!bSubTypePresent)
                    {
                        // No subtype was specified, so we must assign a default
                        getDefaultTransitionSubType((const char*) pTrans->m_Type,
                                                    pTrans->m_SubType);
                    }
                    else
                    {
                        // Subtype was specified, so check if it's legal
                        if (isLegalTransitionSubType((const char*) pTrans->m_Type,
                                                     (const char*) pTrans->m_SubType))
                        {
                            bSubTypeLegal = TRUE;
                        }
                        else
                        {
                            retVal = HXR_FAIL;
                        }
                    }
                    if (SUCCEEDED(retVal))
                    {
                        // Put this transition in the transition map
                        if (!m_pTransitionMap)
                        {
                            m_pTransitionMap = new CHXMapStringToOb();
                        }
                        if (m_pTransitionMap)
                        {
                            m_pTransitionMap->SetAt((const char*) pNode->m_id,
                                                    (void*) pTrans);
                        }
                    }
                }
                else if (bTypePresent && !bTypeLegal)
                {
                    // Fail, but don't pop up error
                    retVal = HXR_FAIL;
                }
                else if (!bTypePresent)
                {
                    // "type" attribute is required. This is an error
                    retVal = HXR_FAIL;
                    CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                    errHandler.ReportError(SMILErrorRequiredAttributeMissing,
                                           "type", pNode->m_ulTagStartLine);
                }
            }
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    if (FAILED(retVal))
    {
        HX_DELETE(pTrans);
        // In certain cases, we will return NULL, but we don't
        // want to return HXR_FAIL for the error out parameter.
        // This indicates that we failed on this transition,
        // but that we should continue parsing the rest of the
        // document. This could happen, for instance, when this
        // transition uses a namespaced-extended "type" that we
        // don't support.
        if ((bTypePresent && !bTypeLegal) ||
            (bTypePresent && bTypeLegal &&
             bSubTypePresent && !bSubTypeLegal))
        {
            // Clear the return value
            retVal = HXR_OK;
        }
    }

    return pTrans;
}

#endif /* #if defined(HELIX_FEATURE_SMIL2_TRANSITIONS) */

CSmilPriorityClassElement*
CSmilParser::makePriorityClassElement(SMILNode* pNode)
{
    CSmilPriorityClassElement* pPriorityClass =
          new CSmilPriorityClassElement(pNode);
    HX_RESULT rc = HXR_OK;
    if(pNode->m_pValues)
    {
      const char* pName = 0;
      IHXBuffer* pBuf = 0;
      rc = pNode->m_pValues->GetFirstPropertyCString(pName,pBuf);
      while(HXR_OK == rc)
      {
          const char* pVal = (const char*)pBuf->GetBuffer();
            // Get the SMIL2Attribute
            SMIL2Attribute eAttr = getSMIL2Attribute(pName);
            // Switch on attribute name
            switch (eAttr)
            {
                case SMIL2AttrPeers:
                  {
                    rc = parsePeersHigherLower(pVal, pPriorityClass,
                          SMILPriorityClassPeers);
                  }
                    break;
                case SMIL2AttrHigher:
                  {
                    rc = parsePeersHigherLower(pVal, pPriorityClass,
                          SMILPriorityClassHigher);
                  }
                    break;
                case SMIL2AttrLower:
                  {
                    rc = parsePeersHigherLower(pVal, pPriorityClass,
                          SMILPriorityClassLower);
                  }
                    break;
                case SMIL2AttrPauseDisplay:
                  {
                    rc = parsePauseDisplay(pVal, pPriorityClass);
                  }
                    break;
                case SMIL2AttrRestartDefault:
                  {
                    rc = parseRestartDefault(pVal, pPriorityClass);
                  }
                    break;
                case SMIL2AttrSyncToleranceDefault:
                  {
                    rc = parseDuration(pVal, pPriorityClass,
                          SMILSyncAttrSyncToleranceDefault);
                  }
                    break;
                case SMIL2AttrSyncBehaviorDefault:
                  {
                    rc = parseSyncBehaviorVal(pVal, pPriorityClass,
                          SMILSyncAttrSyncBehaviorDefault);
                  }
                    break;
            }

          pBuf->Release();
          if (HXR_OK != rc)
          {
            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
            errHandler.ReportError(SMILErrorBadAttribute,
                  pVal, pNode->m_ulTagStartLine);
            goto exit;
          }
          rc = pNode->m_pValues->GetNextPropertyCString(pName, pBuf);
      }
    }
exit:
    return (pPriorityClass);
}

CSmilAAnchorElement*
CSmilParser::makeAAnchorElement(SMILNode* pNode)
{
    HX_RESULT rc = HXR_OK;
    CSmilAAnchorElement* pAnchor = new CSmilAAnchorElement(pNode);
    BOOL bMinSet, bMaxSet, bDurSet, bEndSet, bBeginSet;
    bMinSet = bMaxSet = bDurSet = bEndSet = bBeginSet = FALSE;
    if(pNode->m_pValues)
    {
      const char* pName = 0;
      IHXBuffer* pBuf = 0;
      rc = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);
      BOOL bSourcePlaystateWasSet = FALSE;
      BOOL bTargetWasSet = FALSE;
      // /XXXEH- TODO: put the following code in its own function and have
      // both CSmilAnchorElement and CSmilAAnchorElement call it, with the
      // catch that the former has several attributes that should not be
      // allowed in the latter (e.g., "coords", "fragment"), but note:
      // <a>'s timing doesn't constrain its children, but <area>'s
      // may:

      // /Handle href first in case target="[some region]" is lexically
      // before href; this type of target needs the href value: (PR 52891)
      while(HXR_OK == rc)
      {
          // /XXXEH- TODO: make sure to strip all leading and trailing
          // whitespace chars first, then check for syntax errors in what's
          // left:

          const char* pszValue =
                NULL!=pBuf?(const char*) pBuf->GetBuffer():NULL;
          if(strcmp(pName, "href") == 0)
          {
            pAnchor->m_href = pszValue;
            break;
          }

          pBuf->Release();
          rc = pNode->m_pValues->GetNextPropertyCString(pName, pBuf);
          if (HXR_OK != rc) // /No more props, so quit w/OK status:
          {
            rc = HXR_OK;
            break;
          }
      } // /end while.
      rc = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);
      while(HXR_OK == rc)
      {
          // /XXXEH- TODO: make sure to strip all leading and trailing
          // whitespace chars first, then check for syntax errors in what's
          // left:

          const char* pszValue =
                NULL!=pBuf?(const char*) pBuf->GetBuffer():NULL;
            // Get the SMIL2Attribute
            SMIL2Attribute eAttr = getSMIL2Attribute(pName);
            // Switch on attribute name
            switch (eAttr)
            {
                case SMIL2AttrAlt:
                    {
                        pAnchor->m_alt = pszValue;
                    }
                    break;
                case SMIL2AttrLongdesc:
                    {
                        pAnchor->m_longdesc = pszValue;
                    }
                    break;
                case SMIL2AttrShow:
                    {
                        pAnchor->m_show = pszValue;
                        if (strcmp(pszValue,"new")!=0  &&
                                strcmp(pszValue,"replace")!=0  &&
                                strcmp(pszValue,"pause")!=0 )
                        {
                            rc = HXR_INVALID_PARAMETER;
                        }
                    }
                    break;
                case SMIL2AttrSourceLevel:
                case SMIL2AttrDestinationLevel:
                    {
                        char* pTmp = (char*)pszValue;
                        BOOL bFoundNumber=FALSE;
                        BOOL bFoundPercent=FALSE;
                        ULONG32 ulNum = 0;
                        // /Go past all whitespace chars:
                        while (*pTmp  &&  isspace(*pTmp))
                        {
                            ++pTmp;
                        }
                        // /Then, add up the numbers:
                        while (*pTmp  &&  (*pTmp>='0'  &&  *pTmp<='9'))
                        {
                            ulNum *= 10;
                            ulNum += *pTmp-'0';
                            ++pTmp;
                            bFoundNumber=TRUE;
                        }
                        // /Next, verify # was found, followed by '%':
                        if (!bFoundNumber  ||  *pTmp != '%')
                        {
                            rc = HXR_INVALID_PARAMETER;
                        }
                        else
                        {
                            ++pTmp; // /Go past '%'.
                            // /And, lastly, only spaces or nothing should remain:
                            while (*pTmp  &&  isspace(*pTmp))
                            {
                                ++pTmp;
                            }
                            if (*pTmp) // /Something other than spaces after '%':
                            {
                                rc = HXR_INVALID_PARAMETER;
                            }
                            else
                            {
                                if (eAttr == SMIL2AttrSourceLevel)
                                {
                                    pAnchor->m_ulSourceLevel_pct = ulNum;
                                }
                                else // *pName should == 'd':
                                {
                                    pAnchor->m_ulDestinationLevel_pct = ulNum;
                                }
                            }
                        }
                    }
                    break;
                case SMIL2AttrSourcePlaystate:
                case SMIL2AttrDestinationPlaystate:
                    {
                        if (strcmp(pszValue, "play") == 0)
                        {
                            if ('s' == *pName)
                            {
                                bSourcePlaystateWasSet = TRUE;
                                pAnchor->m_sourcePlaystate = SMILLinkPlaystatePlay;
                            }
                            else // *pName should == 'd':
                            {
                                pAnchor->m_destinationPlaystate = SMILLinkPlaystatePlay;
                            }
                        }
                        else if (strcmp(pszValue, "pause") == 0)
                        {
                            if ('s' == *pName)
                            {
                                bSourcePlaystateWasSet = TRUE;
                                pAnchor->m_sourcePlaystate = SMILLinkPlaystatePause;
                            }
                            else // *pName should == 'd':
                            {
                                pAnchor->m_destinationPlaystate = SMILLinkPlaystatePause;
                            }
                        }
                        else if (strcmp(pszValue, "stop") == 0)
                        {
                            if ('s' == *pName)
                            {
                                bSourcePlaystateWasSet = TRUE;
                                pAnchor->m_sourcePlaystate = SMILLinkPlaystateStop;
                            }
                            else // *pName should == 'd':
                            {
                                // /Destination can only be "play" and "pause":
                                rc = HXR_INVALID_PARAMETER;
                            }
                        }
                        else
                        {
                            rc = HXR_INVALID_PARAMETER;
                        }
                    }
                    break;
                case SMIL2AttrExternal:
                    {
                        if (strcmp(pszValue, "true") == 0)
                        {
                            pAnchor->m_bExternal = TRUE;
                        }
                        else if (strcmp(pszValue, "false") == 0)
                        {
                            pAnchor->m_bExternal = FALSE;
                        }
                        else
                        {
                            rc = HXR_INVALID_PARAMETER;
                        }
                    }
                    break;
                case SMIL2AttrActuate:
                    {
                        BOOL bOnLoad = strcmp(pszValue, "onLoad") == 0;
                        if (bOnLoad  ||  strcmp(pszValue, "onRequest") == 0)
                        {
                            pAnchor->m_actuate = pszValue;
                            if (bOnLoad)
                            {
                                if (!m_pOnLoadURLList)
                                {
                                    m_pOnLoadURLList = new CHXSimpleList;
                                }
                                if (!m_pOnLoadURLListCopyForPostSeek)
                                {
                                    m_pOnLoadURLListCopyForPostSeek =
                                  new CHXSimpleList;
                                }
                                if (!m_pOnLoadURLList  ||
                              !m_pOnLoadURLListCopyForPostSeek)
                                {
                                    rc = HXR_OUTOFMEMORY;
                                }
                                else
                                {
                                    // /XXXEH- TODO: pre-sort this temporally,
                                    // and use that to optimize use in
                                    // findNextPendingOnLoadURL(), above, and in
                                    // checkPendingBegin...(),above.  Grep for
                            // "temporal":  NOTE: we *can't* sort these due
                            // to not-yet-knowable parental timing, e.g.,
                            // excl pause & defer, and par begin=[event].

                            // /AddTail, not head, so lexically-last is last
                            // in list, better approximating temporal order:
                            m_pOnLoadURLList->AddTail(pAnchor);
                            // /Helps fix PR 67170:
                            m_pOnLoadURLListCopyForPostSeek->AddTail(pAnchor);

                                }
                            }
                        }
                        else
                        {
                            rc = HXR_INVALID_PARAMETER;
                        }
                    }
                    break;
                case SMIL2AttrAccesskey:
                    {
                        // /Can be any ISO10646 (UNICODE) character:
                        pAnchor->m_accesskey = pszValue;
                    }
                    break;
                case SMIL2AttrTabindex:
                    {
                        rc = HXParseUINT32(pszValue, /*REF*/pAnchor->m_ulTabindex);
                    }
                    break;
                case SMIL2AttrTarget:
                    {
                        pAnchor->m_target = pszValue;
                        bTargetWasSet = (*pszValue != 0);
                        // /Enables target="[some existing region]" (fixes PR 52891):
                        // Find out if region with this name exists;
                        SMILNode* pTargetNode = NULL;
                        if(m_pIDMap  &&  bTargetWasSet)
                        {
                            m_pIDMap->Lookup(pAnchor->m_target, (void*&)pTargetNode);
                            if (pTargetNode  &&  SMILRegion == pTargetNode->m_tag)
                            {
                                // /Insert a sibling node of pAnchor into the
                                // ancestor par:
                                SMILNode* pSyncAncestorNode = getSyncAncestor(pNode);
                                while (pSyncAncestorNode  &&
                                        SMILPar != pSyncAncestorNode->m_tag  &&
                                        SMILBody != pSyncAncestorNode->m_tag)
                                {
                                    pSyncAncestorNode = getSyncAncestor(pSyncAncestorNode);
                                }
                                HX_ASSERT(pSyncAncestorNode  &&
                                        "ehodge: no par for href media targetting region");
                                if (pSyncAncestorNode)
                                {
                            HX_RESULT pnrs =
                                  createElementForAnchorTarget(pAnchor,
                                  pSyncAncestorNode->m_pNodeList);
                                }
                            }
                        }
                    }
                    break;
                case SMIL2AttrRestartDefault:
                    {
                        rc = parseRestartDefault(pszValue, pAnchor);
                    }
                    break;
                case SMIL2AttrSyncToleranceDefault:
                    {
                        rc = parseDuration(pszValue, pAnchor,
                            SMILSyncAttrSyncToleranceDefault);
                    }
                    break;
                case SMIL2AttrSyncBehaviorDefault:
                    {
                        rc = parseSyncBehaviorVal(pszValue,
                                pAnchor, SMILSyncAttrSyncBehaviorDefault);
                    }
                    break;
                case SMIL2AttrBegin:
                    {
                        rc = parseBeginEnd(pszValue, pAnchor, SMILSyncAttrBegin);
                        if(HXR_OK == rc)
                        {
                            if (pAnchor->m_bBeginOffsetSet)
                            {
                                // /We don't want to constrain our children as a time
                                // container would, so we have to reset our begin time to
                                // undefined and set the AAnchor-specific begin-time var:
                                pAnchor->m_lAnchorBeginOffset = pAnchor->m_lBeginOffset;
                                pAnchor->m_bAnchorBeginOffsetSet = TRUE;
                            }
                            // /Reset these so they won't affect children's timing:
                            pAnchor->m_lBeginOffset = 0;
                            pAnchor->m_bBeginOffsetSet = FALSE;

                            pAnchor->m_bTimeValueSet = TRUE;
                            bBeginSet = TRUE;
                        }
                    }
                    break;
                case SMIL2AttrEnd:
                    {
                        rc = parseBeginEnd(pszValue, pAnchor, SMILSyncAttrEnd);
                        if(HXR_OK == rc)
                        {
                            pAnchor->m_bHasExplicitEnd = TRUE;
                            if (pAnchor->m_bEndOffsetSet)
                            {
                                // /We don't want to constrain our children as a time
                                // container would, so we have to reset our end time to
                                // undefined and set the anchor-specific end-time var:
                                pAnchor->m_lAnchorEndOffset = pAnchor->m_lEndOffset;
                                pAnchor->m_bAnchorEndOffsetSet = TRUE;
                            }
                            // /Reset these so they won't affect children's timing:
                            pAnchor->m_lEndOffset = 0;
                            pAnchor->m_bEndOffsetSet = FALSE;

                            pAnchor->m_bTimeValueSet = TRUE;
                            bEndSet = TRUE;
                        }
                    }
                    break;
                case SMIL2AttrDur:
                    {
                        rc = parseDuration(pszValue, pAnchor, SMILSyncAttrDur);
                        if(HXR_OK == rc)
                        {
                            pAnchor->m_bHasExplicitDur = TRUE;
                            // /We don't want to constrain our children as a time
                            // container would, so we have to reset our dur to
                            // undefined and set the anchor-specific dur var:
                            pAnchor->m_ulAnchorDuration = pAnchor->m_ulDuration;
                            // /Reset this so it won't affect children's timing:
                            pAnchor->m_ulDuration = (UINT32)-1;

                            pAnchor->m_bTimeValueSet = TRUE;
                            bDurSet = TRUE;
                        }
                    }
                    break;
                case SMIL2AttrMin:
                    {
                        rc = parseDuration(pszValue, pAnchor, SMILSyncAttrMin);
                        if (HXR_OK == rc)
                        {
                            // /We don't want to constrain our children as a time
                            // container would, so we have to reset our min dur
                            // and set just the anchor-specific min dur var:
                            pAnchor->m_ulAnchorMinActiveDur =
                                    pAnchor->m_ulMinActiveDur;
                            // /Reset this so it won't affect children's timing:
                            pAnchor->m_ulMinActiveDur = 0;

                            pAnchor->m_bTimeValueSet = TRUE;
                            bMinSet = TRUE;
                        }
                    }
                    break;
                case SMIL2AttrMax:
                    {
                        rc = parseDuration(pszValue, pAnchor, SMILSyncAttrMax);
                        if (HXR_OK == rc)
                        {
                            // /We don't want to constrain our children as a time
                            // container would, so we have to reset our max dur
                            // and set just the anchor-specific max dur var:
                            pAnchor->m_ulAnchorMaxActiveDur =
                                    pAnchor->m_ulMaxActiveDur;
                            // /Reset this so it won't affect children's timing:
                            pAnchor->m_ulMaxActiveDur = 0;

                            pAnchor->m_bTimeValueSet = TRUE;
                            bMaxSet = TRUE;
                        }
                    }
                    break;
                default:
                    {
                        // Find out if this attributeName is namespace-prefixed.
                        HX_RESULT       rv            = HXR_FAIL;
                        const char*     pszNSAttrName = NULL;
                        CNamespaceInfo* pNSInfo       = getNamespaceInfo(pName, pszNSAttrName);
                        if (pNSInfo)
                        {
                            // This attributeName IS namespace-prefixed.
                            //
                            // If this is the sendTo namespace, then we
                            // we should parse for the "sendTo" attribute
                            if (pNSInfo->m_eNamespace == NamespaceSendTo ||
                                pNSInfo->m_eNamespace == NamespaceAllSMIL2Extensions)
                            {
                                if(strcmp(pszNSAttrName, "sendTo") == 0)
                                {
                                    pAnchor->m_sendTo = pszValue;
                                    rv                = HXR_OK;
                                }
                            }
                            else
                            {
                                // This is some other namespace that we don't
                                // know or don't care about
                                rv = HXR_OK;
                            }
                        }
                        else
                        {
                            // We assume the document has already been validated,
                            // we would only be here if there is a legal element
                            // that is just not being parsed above.
                            rv = HXR_OK;
                        }
                        if (FAILED(rv))
                        {
                            rc = HXR_FAIL;
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute,
                                                   pName,
                                                   pAnchor->m_pNode->m_ulTagStartLine);
                        }
                    }
            }

          pBuf->Release();
          if (HXR_OK != rc)
          {
            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
            errHandler.ReportError(SMILErrorBadAttribute,
                  pszValue, pNode->m_ulTagStartLine);
            break;
          }

          rc = pNode->m_pValues->GetNextPropertyCString(pName, pBuf);
          if (HXR_OK != rc) // /No more props, so quit w/OK status:
          {
            rc = HXR_OK;
            break;
          }
      } // /end while.
      if (!bSourcePlaystateWasSet)
      {
          // /If show="new", this sourcePlaystate must default to "play":
          if (strcmp(pAnchor->m_show, "new") == 0)
          {
            pAnchor->m_sourcePlaystate = SMILLinkPlaystatePlay;
          }
      }
      if (bTargetWasSet)
      {
          // /Make sure that, if target was specified, show is ignored:
          pAnchor->m_show += "(ignored)";
      }
    }

    // /Fixes a-tag version of PR 60111: : if this a didn't have a "begin"
    // attribute, then set the following to TRUE because default begin is a
    // clock value (if our syncBase isn't an excl):
    if (!pAnchor->m_pBeginTimeList)
    {
      SMILNode* pSyncBase = getSyncAncestor(pAnchor->m_pNode);
      if (pSyncBase  &&  SMILExcl != pSyncBase->m_tag)
      {
          pAnchor->m_bHasAtLeastOneNonEventBasedBegin = TRUE;
      }
    }

    // /Now, adjust duration in case both "dur" and "end" were set
    // and in case either "dur" or "end" was set and "min" and/or "max"
    // were set (since we don't have a chance to do this later as this is
    // not a child media element so durationResolved() never gets called):
    ULONG32 ulLesserOfDurOrEndMinusBegin = pAnchor->m_ulAnchorDuration;
    LONG32 lEndMinusBegin = bEndSet? (pAnchor->m_lAnchorEndOffset -
          (bBeginSet? pAnchor->m_lAnchorBeginOffset : 0)) :
          (SMILTIME_NEGATIVE_INFINITY);

    if ((UINT32)-1 == ulLesserOfDurOrEndMinusBegin  ||
          (INT32)ulLesserOfDurOrEndMinusBegin > lEndMinusBegin)
    {
      if (SMILTIME_NEGATIVE_INFINITY != lEndMinusBegin)
      {
          ulLesserOfDurOrEndMinusBegin= lEndMinusBegin<0? 0:lEndMinusBegin;
      }
    }
    if (bDurSet  &&  bEndSet)
    {
      pAnchor->m_ulAnchorDuration = ulLesserOfDurOrEndMinusBegin;
      pAnchor->m_bAnchorEndOffsetSet = FALSE;
    }
    if (bMinSet)
    {
      if ((UINT32)-1 == ulLesserOfDurOrEndMinusBegin  ||
            ulLesserOfDurOrEndMinusBegin < pAnchor->m_ulAnchorMinActiveDur)
      {
          pAnchor->m_ulAnchorDuration = pAnchor->m_ulAnchorMinActiveDur;
          pAnchor->m_bAnchorEndOffsetSet = FALSE;
      }
    }
    if (bMaxSet)
    {
      // /Note: min is already guaranteed to be less than or equal to max
      // so we don't have to worry about min>max here:
      if ((UINT32)-1 == ulLesserOfDurOrEndMinusBegin  ||
            ulLesserOfDurOrEndMinusBegin > pAnchor->m_ulAnchorMaxActiveDur)
      {
          pAnchor->m_ulAnchorDuration = pAnchor->m_ulAnchorMaxActiveDur;
          pAnchor->m_bAnchorEndOffsetSet = FALSE;
      }
    }

    if (HXR_OK == rc)
    {
      return pAnchor;
    }
    HX_DELETE(pAnchor);
    return NULL;
}

CSmilAnchorElement*
CSmilParser::makeAnchorElement(SMILNode* pNode)
{
    CSmilAnchorElement* pAnchor = new CSmilAnchorElement(pNode);
    BOOL bMinSet, bMaxSet, bDurSet, bEndSet, bBeginSet;
    bMinSet = bMaxSet = bDurSet = bEndSet = bBeginSet = FALSE;
    if(pNode->m_pValues)
    {
      BOOL bFoundFGID = FALSE;
      const char* pName = 0;
      IHXBuffer* pBuf = 0;
      HX_RESULT rc = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);
      BOOL bSourcePlaystateWasSet = FALSE;
      BOOL bTargetWasSet = FALSE;

      // /Get sync ancestor in case it's a wrapper seq for repeating:
      SMILNode* pSyncAncestor = getSyncAncestor(pNode);
      BOOL bSyncAncestorIsRepeatWrapperSeq = pSyncAncestor?
            pSyncAncestor->m_bIsSeqWrapperForRepeatElement : FALSE;
      BOOL bAttributeHandledByWrappingParent = FALSE;

      // /XXXEH- TODO: put the following code in its own function and have
      // both CSmilAnchorElement and CSmilAAnchorElement call it, with the
      // catch that the former has several attributes that should not be
      // allowed in the latter (e.g., "coords", "fragment"), but note:
      // <a>'s timing doesn't constrain its children, but <area>'s
      // may:

      // /Handle href first in case target="[some region]" is lexically
      // before href; this type of target needs the href value: (PR 52891)
      while(HXR_OK == rc)
      {
          // /XXXEH- TODO: make sure to strip all leading and trailing
          // whitespace chars first, then check for syntax errors in what's
          // left:

          const char* pszValue =
                NULL!=pBuf?(const char*) pBuf->GetBuffer():NULL;
          if(strcmp(pName, "href") == 0)
          {
            pAnchor->m_href = pszValue;
            break;
          }

          pBuf->Release();
          rc = pNode->m_pValues->GetNextPropertyCString(pName, pBuf);
          if (HXR_OK != rc) // /No more props, so quit w/OK status:
          {
            rc = HXR_OK;
            break;
          }
      } // /end while.
      rc = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);
      while(HXR_OK == rc)
      {
          const char* pszValue =
                NULL!=pBuf?(const char*) pBuf->GetBuffer():NULL;
          HX_ASSERT(pszValue);
          if (!pszValue)
          {
            // /XXXEH- TODO: handle error below:
            rc = HXR_UNEXPECTED;
            break;
          }

            // Get the SMIL2Attribute
            SMIL2Attribute eAttr = getSMIL2Attribute(pName);
            // Switch on attribute name
            switch (eAttr)
            {
                case SMIL2AttrAlt:
                    {
                        pAnchor->m_alt = pszValue;
                    }
                    break;
                case SMIL2AttrLongdesc:
                    {
                        pAnchor->m_longdesc = pszValue;
                    }
                    break;
                case SMIL2AttrShow:
                    {
                        pAnchor->m_show = pszValue;
                    }
                    break;
                case SMIL2AttrSourceLevel:
                case SMIL2AttrDestinationLevel:
                    {
                        char* pTmp = (char*)pszValue;
                        BOOL bFoundNumber=FALSE;
                        BOOL bFoundPercent=FALSE;
                        ULONG32 ulNum = 0;
                        // /Go past all whitespace chars:
                        while (*pTmp  &&  isspace(*pTmp))
                        {
                            ++pTmp;
                        }
                        // /Then, add up the numbers:
                        while (*pTmp  &&  (*pTmp>='0'  &&  *pTmp<='9'))
                        {
                            ulNum *= 10;
                            ulNum += *pTmp-'0';
                            ++pTmp;
                            bFoundNumber=TRUE;
                        }
                        // /Next, verify # was found, followed by '%':
                        if (!bFoundNumber  ||  *pTmp != '%')
                        {
                            rc = HXR_INVALID_PARAMETER;
                        }
                        else
                        {
                            ++pTmp; // /Go past '%'.
                            // /And, lastly, only spaces or nothing should remain:
                            while (*pTmp  &&  isspace(*pTmp))
                            {
                                ++pTmp;
                            }
                            if (*pTmp) // /Something other than spaces after '%':
                            {
                                rc = HXR_INVALID_PARAMETER;
                            }
                            else
                            {
                                if (eAttr == SMIL2AttrSourceLevel)
                                {
                                    pAnchor->m_ulSourceLevel_pct = ulNum;
                                }
                                else // *pName should == 'd':
                                {
                                    pAnchor->m_ulDestinationLevel_pct = ulNum;
                                }
                            }
                        }
                    }
                    break;
                case SMIL2AttrSourcePlaystate:
                case SMIL2AttrDestinationPlaystate:
                    {
                        if (strcmp(pszValue, "play") == 0)
                        {
                            if ('s' == *pName)
                            {
                                bSourcePlaystateWasSet = TRUE;
                                pAnchor->m_sourcePlaystate = SMILLinkPlaystatePlay;
                            }
                            else // *pName should == 'd':
                            {
                                pAnchor->m_destinationPlaystate = SMILLinkPlaystatePlay;
                            }
                        }
                        else if (strcmp(pszValue, "pause") == 0)
                        {
                            if ('s' == *pName)
                            {
                                bSourcePlaystateWasSet = TRUE;
                                pAnchor->m_sourcePlaystate = SMILLinkPlaystatePause;
                            }
                            else // *pName should == 'd':
                            {
                                pAnchor->m_destinationPlaystate = SMILLinkPlaystatePause;
                            }
                        }
                        else if (strcmp(pszValue, "stop") == 0)
                        {
                            if ('s' == *pName)
                            {
                                bSourcePlaystateWasSet = TRUE;
                                pAnchor->m_sourcePlaystate = SMILLinkPlaystateStop;
                            }
                            else // *pName should == 'd':
                            {
                                // /Destination can only be "play" and "pause":
                                rc = HXR_INVALID_PARAMETER;
                            }
                        }
                        else
                        {
                            rc = HXR_INVALID_PARAMETER;
                        }
                    }
                    break;
                case SMIL2AttrExternal:
                    {
                        if (strcmp(pszValue, "true") == 0)
                        {
                            pAnchor->m_bExternal = TRUE;
                        }
                        else if (strcmp(pszValue, "false") == 0)
                        {
                            pAnchor->m_bExternal = FALSE;
                        }
                        else
                        {
                            rc = HXR_INVALID_PARAMETER;
                        }
                    }
                    break;
                case SMIL2AttrActuate:
                    {
                        BOOL bOnLoad = strcmp(pszValue, "onLoad") == 0;
                        if (bOnLoad  ||  strcmp(pszValue, "onRequest") == 0)
                        {
                            pAnchor->m_actuate = pszValue;
                            if (bOnLoad)
                            {
                                if (!m_pOnLoadURLList)
                                {
                                    m_pOnLoadURLList = new CHXSimpleList;
                                }
                                if (!m_pOnLoadURLListCopyForPostSeek)
                                {
                                    m_pOnLoadURLListCopyForPostSeek =
                                  new CHXSimpleList;
                                }
                                if (!m_pOnLoadURLList  ||
                              !m_pOnLoadURLListCopyForPostSeek)
                                {
                                    rc = HXR_OUTOFMEMORY;
                                }
                                else
                                {
                                    // /XXXEH- TODO: pre-sort this temporally,
                                    // and use that to optimize use in
                                    // findNextPendingOnLoadURL(), above, and in
                                    // checkPendingBegin...(),above.  Grep for
                            // "temporal":  NOTE: we *can't* sort these due
                            // to not-yet-knowable parental timing, e.g.,
                            // excl pause & defer, and par begin=[event].

                            // /AddTail, not head, so lexically-last is last
                            // in list, better approximating temporal order:
                            m_pOnLoadURLList->AddTail(pAnchor);
                            // /Helps fix PR 67170:
                            m_pOnLoadURLListCopyForPostSeek->AddTail(pAnchor);
                                }
                            }
                        }
                        else
                        {
                            rc = HXR_INVALID_PARAMETER;
                        }
                    }
                    break;
                case SMIL2AttrAccesskey:
                    {
                        // /Can be any ISO10646 (UNICODE) character:
                        pAnchor->m_accesskey = pszValue;
                    }
                    break; // /Added missing break, here.  No bug was filed.
                case SMIL2AttrTabindex:
                    {
                        rc = HXParseUINT32(pszValue, /*REF*/pAnchor->m_ulTabindex);
                    }
                    break;
                case SMIL2AttrTarget:
                    {
                        pAnchor->m_target = pszValue;
                        bTargetWasSet = (*pszValue != 0);
                        // /Enables target="[some existing region]" (fixes PR 52891):
                        // Find out if region with this name exists;
                        SMILNode* pTargetNode = NULL;
                        if(m_pIDMap  &&  bTargetWasSet)
                        {
                            m_pIDMap->Lookup(pAnchor->m_target, (void*&)pTargetNode);
                            if (pTargetNode  &&  SMILRegion == pTargetNode->m_tag)
                            {
                                // /Insert a sibling node of pAnchor into the
                                // ancestor par:
                                SMILNode* pSyncAncestorNode = getSyncAncestor(pNode);
                                while (pSyncAncestorNode  &&
                                        SMILPar != pSyncAncestorNode->m_tag  &&
                                        SMILBody != pSyncAncestorNode->m_tag)
                                {
                                    pSyncAncestorNode = getSyncAncestor(pSyncAncestorNode);
                                }
                                HX_ASSERT(pSyncAncestorNode  &&
                                        "ehodge: no par for href media targetting region");
                                if (pSyncAncestorNode)
                                {
                            HX_RESULT pnrs =
                                  createElementForAnchorTarget(pAnchor,
                                  pSyncAncestorNode->m_pNodeList);
                                }
                            }
                        }
                    }
                    break;
                case SMIL2AttrRestartDefault:
                    {
                        rc = parseRestartDefault(pszValue, pAnchor);
                    }
                    break;
                case SMIL2AttrSyncToleranceDefault:
                    {
                        rc = parseDuration(pszValue, pAnchor,
                            SMILSyncAttrSyncToleranceDefault);
                    }
                    break;
                case SMIL2AttrSyncBehaviorDefault:
                    {
                        rc = parseSyncBehaviorVal(pszValue,
                                pAnchor, SMILSyncAttrSyncBehaviorDefault);
                    }
                    break;
                case SMIL2AttrBegin:
                    {
                       rc = parseBeginEnd(pszValue, pAnchor, SMILSyncAttrBegin);
                        if(HXR_OK == rc)
                        {
                            pAnchor->m_bTimeValueSet = TRUE;
                            bBeginSet = TRUE;
                        }
                    }
                    break;
                case SMIL2AttrEnd:
                    {
                        rc = parseBeginEnd(pszValue, pAnchor,
                            SMILSyncAttrEnd);
                        if(HXR_OK == rc)
                        {
                            pAnchor->m_bHasExplicitEnd = TRUE;
                            pAnchor->m_bTimeValueSet = TRUE;
                            bEndSet = TRUE;
                        }
                    }
                    break;
                case SMIL2AttrDur:
                    {
                        rc = parseDuration(pszValue, pAnchor, SMILSyncAttrDur);
                        if(HXR_OK == rc)
                        {
                            pAnchor->m_bHasExplicitDur = TRUE;
                            pAnchor->m_bTimeValueSet = TRUE;
                            bDurSet = TRUE;
                        }
                    }
                    break;
                case SMIL2AttrMin:
                    {
                        rc = parseDuration(pszValue, pAnchor, SMILSyncAttrMin);
                        bMinSet = (HXR_OK == rc);
                    }
                    break;
                case SMIL2AttrMax:
                    {
                        rc = parseDuration(pszValue, pAnchor, SMILSyncAttrMax);
                        bMaxSet = (HXR_OK == rc);
                    }
                    break;
                case SMIL2AttrCoords:
                    {
                        IHXBuffer* pBufSh = NULL;
                        if (pAnchor->m_shape.GetLength()==0)
                        {
                            if (HXR_OK == pNode->m_pValues->GetPropertyCString(
                                    "shape",pBufSh))
                            {
                                pAnchor->m_shape = (const char*)pBufSh->GetBuffer();
                                HX_RELEASE(pBufSh);
                            }
                            else // /Default to "rect":
                            {
                                pAnchor->m_shape = "rect";
                            }
                        }
                        rc = parseAnchorCoords(pszValue, pAnchor);
                    }
                    break;
                case SMIL2AttrShape:
                    {
                        pAnchor->m_shape = pszValue;
                    }
                    break;
                case SMIL2AttrFragment:
                    {
                        bFoundFGID = TRUE;
                        pAnchor->m_fragmentID = pszValue;
                    }
                    break;
                case SMIL2AttrZ_Index:
                    {
                        pAnchor->m_zIndex = atol(pszValue);
                    }
                    break;
                default:
                    {
                        if(!bFoundFGID && strcmp(pName, "fragment-id") == 0)
                        {
                            pAnchor->m_fragmentID = pszValue;
                        }
                        else
                        {
                            // Find out if this attributeName is namespace-prefixed.
                            HX_RESULT       rv            = HXR_FAIL;
                            const char*     pszNSAttrName = NULL;
                            CNamespaceInfo* pNSInfo       = getNamespaceInfo(pName, pszNSAttrName);
                            if (pNSInfo)
                            {
                                // This attributeName IS namespace-prefixed.
                                //
                                // If this is the sendTo namespace, then we
                                // we should parse for the "sendTo" attribute
                                if (pNSInfo->m_eNamespace == NamespaceSendTo ||
                                    pNSInfo->m_eNamespace == NamespaceAllSMIL2Extensions)
                                {
                                    if(strcmp(pszNSAttrName, "sendTo") == 0)
                                    {
                                        pAnchor->m_sendTo = pszValue;
                                        rv                = HXR_OK;
                                    }
                                }
                                else
                                {
                                    // This is some other namespace that we don't
                                    // know or don't care about
                                    rv = HXR_OK;
                                }
                            }
                            else
                            {
                                // We assume the document has already been validated,
                                // we would only be here if there is a legal element
                                // that is just not being parsed above.
                                rv = HXR_OK;
                            }
                            if (FAILED(rv))
                            {
                                rc = HXR_FAIL;
                                CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                                errHandler.ReportError(SMILErrorBadAttribute,
                                                       pName,
                                                       pAnchor->m_pNode->m_ulTagStartLine);
                            }
                        }
                    }
            }

          pBuf->Release();
          if (HXR_OK != rc)
          {
            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
            errHandler.ReportError(SMILErrorBadAttribute,
                  pszValue, pNode->m_ulTagStartLine);
            break;
          }

          rc = pNode->m_pValues->GetNextPropertyCString(pName, pBuf);
          if (HXR_OK != rc) // /No more props, so quit w/OK status:
          {
            rc = HXR_OK;
            break;
          }
      } // /end while.
      if (!bSourcePlaystateWasSet)
      {
          // /If show="new", this sourcePlaystate must default to "play":
          if (strcmp(pAnchor->m_show, "new") == 0)
          {
            pAnchor->m_sourcePlaystate = SMILLinkPlaystatePlay;
          }
      }
      if (bTargetWasSet)
      {
          // /Make sure that, if target was specified, show is ignored:
          pAnchor->m_show += "(ignored)";
      }
    }

    // /Fixes area|anchor version of PR 60111: if this didn't have a "begin"
    // attribute, then set the following to TRUE because default begin is a
    // clock value (if our syncBase isn't an excl):
    if (!pAnchor->m_pBeginTimeList)
    {
      SMILNode* pSyncBase = getSyncAncestor(pAnchor->m_pNode);
      if (pSyncBase  &&  SMILExcl != pSyncBase->m_tag)
      {
          pAnchor->m_bHasAtLeastOneNonEventBasedBegin = TRUE;
      }
    }

    // /Now, adjust duration in case both "dur" and "end" were set
    // and in case either "dur" or "end" was set and "min" and/or "max"
    // were set (since we don't have a chance to do this later as this is
    // not a child media element so durationResolved() never gets called):
    ULONG32 ulLesserOfDurOrEndMinusBegin = pAnchor->m_ulDuration;
    LONG32 lEndMinusBegin = bEndSet? (pAnchor->m_lEndOffset -
          (bBeginSet? pAnchor->m_lBeginOffset : 0)) :
          (SMILTIME_NEGATIVE_INFINITY);

    if ((UINT32)-1 == ulLesserOfDurOrEndMinusBegin  ||
          (INT32)ulLesserOfDurOrEndMinusBegin > lEndMinusBegin)
    {
      if (SMILTIME_NEGATIVE_INFINITY != lEndMinusBegin)
      {
          ulLesserOfDurOrEndMinusBegin= lEndMinusBegin<0? 0:lEndMinusBegin;
      }
    }
    if (bDurSet  &&  bEndSet)
    {
      pAnchor->m_ulDuration = ulLesserOfDurOrEndMinusBegin;
      pAnchor->m_bEndOffsetSet = FALSE;
    }
    if (bMinSet)
    {
      if ((UINT32)-1 == ulLesserOfDurOrEndMinusBegin  ||
            ulLesserOfDurOrEndMinusBegin < pAnchor->m_ulMinActiveDur)
      {
          pAnchor->m_ulDuration = pAnchor->m_ulMinActiveDur;
          pAnchor->m_bEndOffsetSet = FALSE;
      }
    }
    if (bMaxSet)
    {
      // /Note: min is already guaranteed to be less than or equal to max
      // so we don't have to worry about min>max here:
      if ((UINT32)-1 == ulLesserOfDurOrEndMinusBegin  ||
            ulLesserOfDurOrEndMinusBegin > pAnchor->m_ulMaxActiveDur)
      {
          pAnchor->m_ulDuration = pAnchor->m_ulMaxActiveDur;
          pAnchor->m_bEndOffsetSet = FALSE;
      }
    }

    return pAnchor;
}

CSmilSource*
CSmilParser::makeSource(SMILNode* pNode)
{
    HX_RESULT rc = HXR_OK;
    CSmilSource* pSource = new CSmilSource(pNode);

    // assign to a group
    if(pNode->m_nGroup == (UINT16)-1)
    {
      SMILNode* 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)
    {
      BOOL        bFoundCB = FALSE;
      BOOL        bFoundCE = FALSE;
        BOOL        bFoundBG = FALSE;

      // /Get sync ancestor in case it's a wrapper seq for repeating:
      SMILNode* pSyncAncestor = getSyncAncestor(pNode);
      BOOL bSyncAncestorIsRepeatWrapperSeq = pSyncAncestor?
            pSyncAncestor->m_bIsSeqWrapperForRepeatElement : FALSE;
      BOOL bAttributeHandledByWrappingParent = FALSE;

      const char* pName    = 0;
      IHXBuffer* pBuf     = 0;
      rc = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);
      while(HXR_OK == rc)
      {
          const char* pszValue =
                NULL!=pBuf?(const char*) pBuf->GetBuffer():NULL;
          HX_ASSERT(pszValue);
          if (!pszValue)
          {
            // /XXXEH- TODO: handle error below:
            rc = HXR_UNEXPECTED;
            break;
          }

            // Check if the attribute is a legal namespaced attribute
            const char*     pszNSAttr = NULL;
            CNamespaceInfo* pNSInfo = getNamespaceInfo(pName, pszNSAttr);

            // Get the SMIL2Attribute
            SMIL2Attribute eAttr = getSMIL2Attribute(pName);
            // Switch on attribute name
            switch (eAttr)
            {
                case SMIL2AttrSrc:
                    {
                        CHXString src = pszValue;

                        // 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;
                        }
                        // Check the "DisableDataURLValidation" prefkey
                        BOOL bDisable = FALSE;
                        ::getBooleanPreference(m_pContext,
                                               "DisableDataURLValidation",
                                               bDisable);
                        if (!bDisable)
                        {
                            // Validate this if it is a data: URL
                            rc = validateDataURL(pSource->m_src);
                            if (FAILED(rc))
                            {
                                CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                                errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                        pNode->m_ulTagStartLine);
                                pBuf->Release();
                                goto exit;
                            }
                        }
                    }
                    break;
                case SMIL2AttrRegion:
                    {
                        //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 = pszValue;
                        //pSource->m_region += "_";
                        //pSource->m_region += szPersistentComponentID;
                    }
                    break;
                case SMIL2AttrColor:
                    {
                        rc = parseColor(pszValue,
                                        pSource->m_ulColor,
                                        pSource->m_eColorType);
                        if (FAILED(rc))
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                                   pNode->m_ulTagStartLine);
                        }
                    }
                    break;
                case SMIL2AttrBegin:
                    {
                        // Have we already seen a begin attribute?
                        if (!pSource->m_bBeginOffsetSet)
                        {
                            // /XXXEH- do this for non-repeat wrappers for
                            // end(when dur also specified),min,max as well,
                            // and do same in seq/par/excl, too (and anchors?):
                            if (!pNode->m_bBeginHandledByWrapperParent)
                            {
                                // /These conditions used to be part of the
                                // [else if(..."begin"...)] condition, above which fixed
                                // the begin-offset-propagated-to-all-repeat-iterations
                                // bug; begin offset should only apply to first iteration:
                                if (!pNode->m_bRepeatHandled  ||
                                        !RepeatReplica==pNode->m_repeatTag)
                                {
                                    rc = parseBeginEnd(pszValue, pSource,
                                            SMILSyncAttrBegin);
                                    if (FAILED(rc))
                                    {
                                        CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                                        errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                                               pNode->m_ulTagStartLine);
                                    }
                                }
                                else
                                {
                                    HX_ASSERT(bSyncAncestorIsRepeatWrapperSeq);
                                }
                            }
                            else
                            {
                                HX_ASSERT(bSyncAncestorIsRepeatWrapperSeq);
                            }
                        }
                        else
                        {
                            // Yes, we've already seen this attribute
                            rc = HXR_FAIL;
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute,
                                                   "begin",
                                                   pNode->m_ulTagStartLine);
                        }
                    }
                    break;
                case SMIL2AttrEnd:
                    {
                        // /XXXEH- do this for non-repeat wrappers for
                        // end(when dur also specified),min,max as well,
                        // and do same in seq/par/excl, too (and anchors?):
                        if (!pNode->m_bEndHandledByWrapperParent)
                        {
                            rc = parseBeginEnd(pszValue, pSource, SMILSyncAttrEnd);
                            pSource->m_bHasExplicitEnd = (HXR_OK == rc);
                        }
                        else
                        {
                            HX_ASSERT(bSyncAncestorIsRepeatWrapperSeq);
                        }
                    }
                    break;
                case SMIL2AttrClipBegin:
                    {
                        bFoundCB = TRUE;
                        rc = parseDuration(pszValue, pSource,
                            SMILSyncAttrClipBegin);
                    }
                    break;
                case SMIL2AttrClipEnd:
                    {
                        bFoundCE = TRUE;
                        rc = parseDuration(pszValue, pSource,
                            SMILSyncAttrClipEnd);
                    }
                    break;
                case SMIL2AttrDur:
                    {
                        rc = parseDuration(pszValue, pSource,
                            SMILSyncAttrDur);
                        pSource->m_bHasExplicitDur = (HXR_OK == rc);
                    }
                    break;
                case SMIL2AttrMin:
                    {
                        // /XXXEH- do this for non-repeat wrappers for
                        // end(when dur also specified),min,max as well,
                        // and do same in seq/par/excl, too (and anchors?):
                        if (!pNode->m_bMinHandledByWrapperParent)
                        {
                            rc = parseDuration(pszValue, pSource, SMILSyncAttrMin);
                        }
                        else
                        {
                            HX_ASSERT(bSyncAncestorIsRepeatWrapperSeq);
                        }
                    }
                    break;
                case SMIL2AttrMax:
                    {
                        // /XXXEH- do this for non-repeat wrappers for
                        // end(when dur also specified),min,max as well,
                        // and do same in seq/par/excl, too (and anchors?):
                        if (!pNode->m_bMaxHandledByWrapperParent)
                        {
                            rc = parseDuration(pszValue, pSource, SMILSyncAttrMax);
                        }
                        else
                        {
                            HX_ASSERT(bSyncAncestorIsRepeatWrapperSeq);
                        }
                    }
                    break;
                case SMIL2AttrSyncTolerance:
                    {
                        rc = parseDuration(pszValue, pSource,
                            SMILSyncAttrSyncTolerance);
                    }
                    break;
                case SMIL2AttrSyncToleranceDefault:
                    {
                        rc = parseDuration(pszValue, pSource,
                            SMILSyncAttrSyncToleranceDefault);
                    }
                    break;
                case SMIL2AttrSyncBehavior:
                    {
                        rc = parseSyncBehaviorVal(pszValue,
                                pSource, SMILSyncAttrSyncBehavior);
                        if (HXR_OK != rc)
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                    pNode->m_ulTagStartLine);
                            pBuf->Release();
                            goto exit;
                        }
                    }
                    break;
                case SMIL2AttrSyncBehaviorDefault:
                    {
                        rc = parseSyncBehaviorVal(pszValue,
                                pSource, SMILSyncAttrSyncBehaviorDefault);
                        if (HXR_OK != rc)
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                    pNode->m_ulTagStartLine);
                            pBuf->Release();
                            goto exit;
                        }
                    }
                    break;
                case SMIL2AttrFill:
                    {
                        rc = parseFill(pszValue, pSource->m_eFill);
                        if (HXR_OK != rc)
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                    pNode->m_ulTagStartLine);
                            pBuf->Release();
                            goto exit;
                        }
                    }
                    break;
                case SMIL2AttrFillDefault:
                    {
                        rc = parseFillDefault(pszValue, pSource->m_eFillDefault);
                        if (HXR_OK != rc)
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                    pNode->m_ulTagStartLine);
                            pBuf->Release();
                            goto exit;
                        }
                    }
                    break;
                case SMIL2AttrErase:
                    {
                        if (!strcmp(pszValue, "never"))
                        {
                            pSource->m_eErase = EraseNever;
                        }
                        else if (!strcmp(pszValue, "whenDone"))
                        {
                            pSource->m_eErase = EraseWhenDone;
                        }
                        else
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                                   pNode->m_ulTagStartLine);
                            pBuf->Release();
                            rc = HXR_FAIL;
                            goto exit;
                        }
                    }
                    break;
                case SMIL2AttrTitle:
                    {
                        pSource->m_title = pszValue;
                    }
                    break;
                case SMIL2AttrAuthor:
                    {
                        pSource->m_author = pszValue;
                    }
                    break;
                case SMIL2AttrCopyright:
                    {
                        pSource->m_copyright = pszValue;
                    }
                    break;
                case SMIL2AttrAbstract:
                    {
                        pSource->m_abstract = pszValue;
                    }
                    break;
                case SMIL2AttrAlt:
                    {
                        pSource->m_alt = pszValue;
                    }
                    break;
                case SMIL2AttrLongdesc:
                    {
                        pSource->m_longdesc = pszValue;
                    }
                    break;
                case SMIL2AttrReadIndex:
                    {
                        ULONG32 ulBufLen = pBuf->GetSize();
                        if (ulBufLen > 0)
                        {
                            char* pCur = (char*)pszValue;
                            HX_ASSERT ('\0' == pszValue[ulBufLen-1]);
                            // /Get rid of leading spaces:
                            while (*pCur  &&  isspace(*pCur))
                            {
                                pCur++;
                            }
                            // /Now it should be all numerals:
                            while (*pCur  &&  '0'<=*pCur  &&  '9'>=*pCur)
                            {
                                pCur++;
                            }
                            // /And all that should be left are spaces:
                            while (*pCur  &&  isspace(*pCur))
                            {
                                pCur++;
                            }
                            if (*pCur)
                            {
                                // /Bad char found (must be (S)*(#)*(S)*):
                                rc = HXR_INVALID_PARAMETER;
                                CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                                char* pTmp = new char[strlen(pszValue) + strlen(pName) + strlen("=\"\"") + 1];
                                if (pTmp)
                                {   HX_ASSERT(strlen(pName) <= 9); // /pTmp used to be: char[strlen(pszValue) + 12 + 1], above.
                                    strcpy(pTmp, pName); /* Flawfinder: ignore */
                                    strcat(pTmp, "=\""); /* Flawfinder: ignore */
                                    strcat(pTmp, pszValue); /* Flawfinder: ignore */
                                    strcat(pTmp, "\""); /* Flawfinder: ignore */
                                }
                                errHandler.ReportError(SMILErrorBadAttribute,
                                                       pTmp ? pTmp : pszValue,
                                                       pNode->m_ulTagStartLine);
                                HX_VECTOR_DELETE(pTmp);
                                pBuf->Release();
                                goto exit;
                            }
                            else
                            {
                                pSource->m_ulReadIndex = atol(pszValue);
                            }
                        }
                    }
                    break;
                case SMIL2AttrRestart:
                    {
                        rc = parseRestart(pszValue, pSource);
                    }
                    break;
                case SMIL2AttrRestartDefault:
                    {
                        rc = parseRestartDefault(pszValue, pSource);
                    }
                    break;
                case SMIL2AttrRepeat:
                    {
                        const char* pRepeatCount = pszValue;

                        if(strcmp(pRepeatCount, "indefinite") == 0)
                        {
                            pSource->m_fRepeatValue = MAX_UINT32;
                        }
                        else
                        {
                            pSource->m_fRepeatValue = atof(pRepeatCount);
                        }
                    }
                    break;
                case SMIL2AttrSensitivity:
                    {
                        rc = parseSensitivity(pszValue, pSource);
                    }
                    break;
                case SMIL2AttrLeft:
                    {
                        rc = parseRegionDimension(pszValue,
                                                  pSource->m_Rect.m_dLeft,
                                                  pSource->m_Rect.m_eLeftType);
                    }
                    break;
                case SMIL2AttrTop:
                    {
                        rc = parseRegionDimension(pszValue,
                                                  pSource->m_Rect.m_dTop,
                                                  pSource->m_Rect.m_eTopType);
                    }
                    break;
                case SMIL2AttrRight:
                    {
                        rc = parseRegionDimension(pszValue,
                                                  pSource->m_Rect.m_dRight,
                                                  pSource->m_Rect.m_eRightType);
                    }
                    break;
                case SMIL2AttrBottom:
                    {
                        rc = parseRegionDimension(pszValue,
                                                  pSource->m_Rect.m_dBottom,
                                                  pSource->m_Rect.m_eBottomType);
                    }
                    break;
                case SMIL2AttrWidth:
                    {
                        rc = parseRegionDimension(pszValue,
                                                  pSource->m_Rect.m_dWidth,
                                                  pSource->m_Rect.m_eWidthType);
                    }
                    break;
                case SMIL2AttrHeight:
                    {
                        rc = parseRegionDimension(pszValue,
                                                  pSource->m_Rect.m_dHeight,
                                                  pSource->m_Rect.m_eHeightType);
                    }
                    break;
                case SMIL2AttrZ_Index:
                    {
                        rc = parseZIndex(pszValue,
                                         pSource->m_lZIndex,
                                         pSource->m_eZIndexType);
                    }
                    break;
                case SMIL2AttrFit:
                    {
                        rc = parseFit(pszValue, pSource->m_eFit);
                        if (SUCCEEDED(rc))
                        {
                            pSource->m_bFitSpecified = TRUE;
                        }
                    }
                    break;
                case SMIL2AttrBackgroundColor:
                    {
                        rc = parseColor(pszValue,
                                        pSource->m_ulBackgroundColor,
                                        pSource->m_eBackgroundColorType);
                        if (SUCCEEDED(rc))
                        {
                            bFoundBG = TRUE;
                            pSource->m_bBackgroundColorSpecified = TRUE;
                        }
                    }
                    break;
                case SMIL2AttrRegPoint:
                    {
                        // Grab the regPoint attribute
                        pSource->m_RegPoint = pszValue;
                        // Check to see if this is a predefined regPoint
                        HX_RESULT rv = parseRegAlign(pszValue,
                                                     pSource->m_ePredefRegPoint);
                        if (SUCCEEDED(rv))
                        {
                            // It WAS predefined
                            pSource->m_bRegPointIsPredef = TRUE;
                        }
                        else
                        {
                            // It was NOT predefined, so it must
                            // be an id of a <regPoint> element
                            pSource->m_bRegPointIsPredef = FALSE;
                        }
                    }
                    break;
                case SMIL2AttrRegAlign:
                    {
                        rc = parseRegAlign(pszValue, pSource->m_eRegAlign);
                        if (SUCCEEDED(rc))
                        {
                            pSource->m_bRegAlignSpecified = TRUE;
                        }
                    }
                    break;
                case SMIL2AttrMediaRepeat:
                    {
                        pSource->m_MediaRepeat = pszValue;
                    }
                    break;
                case SMIL2AttrTransIn:
                    {
#if defined(HELIX_FEATURE_SMIL2_TRANSITIONS)
                        if (m_pTransitionMap)
                        {
                            // Make a copy of the string
                            CHXString cTransIn(pszValue);
                            const char* pszTransIn = (const char*) cTransIn;
                            // transIn is a semi-colon-separated list of id's
                            char* pszToken = strtok((char*) pszTransIn, "; ");
                            while (pszToken)
                            {
                                void* pVoid = NULL;
                                if (m_pTransitionMap->Lookup(pszToken, pVoid))
                                {
                                    pSource->m_beginTransition = pszToken;
                                    break;
                                }
                                pszToken = strtok(NULL, "; ");
                            }
                        }
#endif /* #if defined(HELIX_FEATURE_SMIL2_TRANSITIONS) */
                    }
                    break;
                case SMIL2AttrTransOut:
                    {
#if defined(HELIX_FEATURE_SMIL2_TRANSITIONS)
                        if (m_pTransitionMap)
                        {
                            // Make a copy of the string
                            CHXString cTransOut(pszValue);
                            const char* pszTransOut = (const char*) cTransOut;
                            // transIn is a semi-colon-separated list of id's
                            char* pszToken = strtok((char*) pszTransOut, "; ");
                            while (pszToken)
                            {
                                void* pVoid = NULL;
                                if (m_pTransitionMap->Lookup(pszToken, pVoid))
                                {
                                    pSource->m_endTransition = pszToken;
                                    break;
                                }
                                pszToken = strtok(NULL, "; ");
                            }
                        }
#endif /* #if defined(HELIX_FEATURE_SMIL2_TRANSITIONS) */
                    }
                    break;
                default:
                    {
                        if(!bFoundCB && strcmp(pName, "clip-begin") == 0)
                        {
                            rc = parseDuration(pszValue, pSource,
                                SMILSyncAttrClipBegin);
                        }
                        else if(!bFoundCE && strcmp(pName, "clip-end") == 0)
                        {
                            rc = parseDuration(pszValue, pSource,
                                SMILSyncAttrClipEnd);
                        }
                        else if (strcmp(pName, "background-color") == 0)
                        {
                            // Checking the flag makes us prefer the camelCase
                            // version over the hyphenated version should both
                            // be specified
                            if (!bFoundBG)
                            {
                                rc = parseColor(pszValue,
                                                pSource->m_ulBackgroundColor,
                                                pSource->m_eBackgroundColorType);
                                if (SUCCEEDED(rc))
                                {
                                    pSource->m_bBackgroundColorSpecified = TRUE;
                                }
                            }
                        }
                        else if (SMILPrefetch == pNode->m_tag)
                        {
                            BOOL bIsPercent = FALSE;
                            const char* pTmpBuf = pszValue;
                            const char* pPercentSignLoc = strchr(pTmpBuf,'%');
                            if (pPercentSignLoc)
                            {
                                bIsPercent = TRUE;
                                if (pPercentSignLoc == pTmpBuf)
                                {
                                    rc = HXR_FAILED; // /invalid % value.
                                    pBuf->Release();
                                    goto exit;
                                }
                            }
                            // /XXXEH- TODO: in each of the following three attributes,
                            // should we handle value errors like x>100% or just go ahead
                            // and pass to the core and let the core sort it out?
                            HX_RESULT pnrslt = HXR_OK;
                            if(strcmp(pName, "mediaTime") == 0)
                            {
                                UINT32 ulTimeValue = (UINT32)-1;
                                if (!bIsPercent)
                                {
                                    pnrslt = parseClockValue(pTmpBuf, ulTimeValue);
                                    if (HXR_OK == pnrslt)
                                    {
                                        pSource->m_ulPrefetchAmount = ulTimeValue;
                                        pSource->m_typeOfPrefetchAmount = PrefetchTime;
                                    }
                                    else
                                    {
                                        // /XXXEH- TODO: determine whether we want to
                                        // ignore this element if specified time is not
                                        // valid (by getting 0s of it as done below) or
                                        // rather fire off an error:
                                        pSource->m_ulPrefetchAmount = 0;
                                        pSource->m_typeOfPrefetchAmount = PrefetchTime;
                                    }
                                }
                                else
                                {
                                    pSource->m_ulPrefetchAmount = atol(pTmpBuf);
                                    pSource->m_typeOfPrefetchAmount =
                                            PrefetchTimePercent;
                                }
                            }
                            else if(strcmp(pName, "mediaSize") == 0)
                            {
                                pSource->m_ulPrefetchAmount = atol(pTmpBuf);
                                pSource->m_typeOfPrefetchAmount =
                                        (bIsPercent?
                                        PrefetchBytesPercent:PrefetchBytes);
                            }
                            else if(strcmp(pName, "bandwidth") == 0)
                            {
                                pSource->m_ulPrefetchAmount = atol(pTmpBuf);
                                pSource->m_typeOfPrefetchAmount =
                                        (bIsPercent?
                                        PrefetchBytesPercent:PrefetchBytes);
                            }
                            else
                            {
                                // Check if this attribute is namespace-prefixed. Since
                                // we assume that this document has already been validated,
                                // then we don't have to throw an error if it's not - it's
                                // probably just an attribute we are not parsing above.
                                if (pNSInfo)
                                {
                                    if (pNSInfo->m_eNamespace == NamespaceHandledBy ||
                                        pNSInfo->m_eNamespace == NamespaceAllSMIL2Extensions)
                                    {
                                        // This is the handledBy namespace, which has
                                        // one attribute on the <prefetch> element.
                                        if (strcmp(pszNSAttr, "handledBy") == 0 &&
                                            pNode->m_tag                   == SMILPrefetch)
                                        {
                                            if (strcmp(pszValue, "_rpbrowser") == 0)
                                            {
                                                pSource->m_eHandledBy = HandledByRPBrowser;
                                            }
                                            else if (strcmp(pszValue, "_rpcontextwin") == 0)
                                            {
                                                pSource->m_eHandledBy = HandledByRPContextWin;
                                            }
                                            else if (strcmp(pszValue, "_rpengine") == 0)
                                            {
                                                pSource->m_eHandledBy = HandledByRPEngine;
                                            }
                                            else if (strcmp(pszValue, "auto") == 0)
                                            {
                                                pSource->m_eHandledBy = HandledByAuto;
                                            }
                                            else
                                            {
                                                rc = HXR_FAIL;
                                            }
                                        }
                                        else
                                        {
                                            rc = HXR_FAIL;
                                        }
                                        if (FAILED(rc))
                                        {
                                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                                                   pNode->m_ulTagStartLine);
                                        }
                                    }
                                }
                            }
                        }
                        else
                        {
                            // Check if this attribute is namespace-prefixed. Since
                            // we assume that this document has already been validated,
                            // then we don't have to throw an error if it's not - it's
                            // probably just an attribute we are not parsing above.
                            if (pNSInfo)
                            {
                                // If this is the alphaControl namespace, then
                                // we should parse for alpha-related attributes
                                if (pNSInfo->m_eNamespace == NamespaceAlphaControl        ||
                                    pNSInfo->m_eNamespace == NamespaceAccessErrorBehavior ||
                                    pNSInfo->m_eNamespace == NamespaceAllSMIL2Extensions)
                                {
                                    if(strcmp(pszNSAttr, "backgroundOpacity") == 0)
                                    {
                                        rc = HXParseOpacity(pszValue, pSource->m_ulBackgroundOpacity);
                                        if (SUCCEEDED(rc))
                                        {
                                            pSource->m_bBackgroundOpacitySpecified = TRUE;
                                        }
                                    }
                                    else if(strcmp(pszNSAttr, "mediaOpacity") == 0)
                                    {
                                        rc = HXParseOpacity(pszValue, pSource->m_ulMediaOpacity);
                                        if (SUCCEEDED(rc))
                                        {
                                            pSource->m_bMediaOpacitySpecified = TRUE;
                                        }
                                    }
                                    else if(strcmp(pszNSAttr, "chromaKey") == 0)
                                    {
                                        rc = HXParseColorUINT32(pszValue, pSource->m_ulChromaKey);
                                        if (SUCCEEDED(rc))
                                        {
                                            pSource->m_bChromaKeySpecified = TRUE;
                                        }
                                    }
                                    else if(strcmp(pszNSAttr, "chromaKeyTolerance") == 0)
                                    {
                                        rc = HXParseColorUINT32(pszValue, pSource->m_ulChromaKeyTolerance);
                                    }
                                    else if(strcmp(pszNSAttr, "chromaKeyOpacity") == 0)
                                    {
                                        rc = HXParseOpacity(pszValue, pSource->m_ulChromaKeyOpacity);
                                    }
                                    else if(strcmp(pszNSAttr, "accessErrorBehavior") == 0)
                                    {
                                        rc = parseAccessErrorBehavior(pszValue,
                                                                      pSource->m_eAccessErrorBehavior);
                                        if (FAILED(rc))
                                        {
                                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                                                   pNode->m_ulTagStartLine);
                                        }
                                    }
                                }
                                else if (pNSInfo->m_eNamespace == NamespaceXMLEvents)
                                {
                                    if(strcmp(pszNSAttr, "handler") == 0)
                                    {
                                        pSource->m_Handler = pszValue;
                                        parseHandlerForID(pszValue,
                                                          pSource->m_HandlerID);
                                        // Add this element to the list of
                                        // elements with handler attributes
                                        if (!m_pElementsWithHandlerList)
                                        {
                                            m_pElementsWithHandlerList = new CHXSimpleList();
                                        }
                                        if (m_pElementsWithHandlerList)
                                        {
                                            m_pElementsWithHandlerList->AddTail((void*) pSource);
                                        }
                                    }
                                }
                            }
                      // /Handle internally-added values here, e.g.,
                      // destination sound level if this element was
                      // created to play to a region target of a link
                      // in this presentation (PR 66650):
                      if (strcmp(pName,
                            TARGET_OF_LINK_DESTINATION_SOUND_LEVEL_STR) == 0)
                      {
                        pSource->m_ulLinkTargetDestnLevel_pct = atoi(pszValue);
                      }
                      else if (strcmp(pName,
                            TARGET_OF_LINK_SOURCE_SOUND_LEVEL_STR) == 0)
                      {
                        pSource->m_ulLinkTargetSourceLevel_pct = atoi(pszValue);
                      }
                        }
                    }
            }

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

          rc = pNode->m_pValues->GetNextPropertyCString(pName, pBuf);
      }
#if defined(HELIX_FEATURE_SMIL2_BRUSH)
        // If this is a <brush>, then we have
        // to generate a "src" attribute.
        if (pNode->m_tag == SMILBrush)
        {
            // Create the base of our src string
            CHXString cSrc("data:text/brush;base64,");
            // Convert the color attribute to a "#RRGGBB" format
            CHXString cColor("#000000");
            if (pSource->m_eColorType == CSS2TypeColor)
            {
                char szTmp[16]; /* Flawfinder: ignore */
                sprintf(szTmp, "#%06X", pSource->m_ulColor); /* Flawfinder: ignore */
                cColor = (const char*) szTmp;
            }
            // Now we need to create our brush string
            CHXString cBrushText("<brush color=\"");
            cBrushText += cColor;
            cBrushText += "\"/>";
            // Allocate an output string big enough to hold the
            // base-64-encoded string
            char* pStr64 = new char [cBrushText.GetLength() << 1];
            if (pStr64)
            {
                // Encode to base-64
                INT32 lEncodedLen = BinTo64((const BYTE*)(const char*) cBrushText,
                                            cBrushText.GetLength(),
                                            pStr64);
                // Create a string out of this encoded data
                CHXString cEncodedBrushText((const char*) pStr64, lEncodedLen);
                // Append this string to our src string
                cSrc += cEncodedBrushText;
                // Assign this to the source element
                pSource->m_src = cSrc;
            }
            HX_VECTOR_DELETE(pStr64);
        }
#endif /* #if defined(HELIX_FEATURE_SMIL2_BRUSH) */
    }

    // /Check if this track might ever end earlier than scheduled
    // as may happen in an excl or if it has multiple begin times
    // or an event-based begin time (which can resolve multiple
    // times):
    if (!pSource->m_bAudioDeviceReflushHintNeeded  &&
          ( hasAncestor(SMILExcl, pNode)  ||
          ( pSource->m_restartBehavior != SmilRestartNever  &&
            ( pSource->m_bHasAtLeastOneEventBasedBegin  ||
            (pSource->m_pBeginTimeList  &&
                pSource->m_pBeginTimeList->GetCount() > 1)) )) )
    {
      pSource->m_bAudioDeviceReflushHintNeeded = TRUE;
    }

    // If our "fill" attribute is "default", then we have
    // to resolve it from either our fillDefault attribute
    // (which may involve checking our ancestors).
    resolveFillValue(pSource);

    // /Helps fix PR 56481: if this element didn't have a "begin" attribute,
    // then set the following to TRUE because default begin is a clock value
    // (if our syncBase isn't an excl):
    if (!pSource->m_pBeginTimeList)
    {
      SMILNode* pSyncBase = getSyncAncestor(pSource->m_pNode);
      if (pSyncBase  &&  SMILExcl != pSyncBase->m_tag)
      {
          pSource->m_bHasAtLeastOneNonEventBasedBegin = TRUE;
      }
    }

    rc = adjustDuration(pSource);
    if (HXR_OK == rc)
    {
      pSource->m_ulAuthoredDur = pSource->m_ulDuration;
    }

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

    return pSource;
}

CSmilSeqElement*
CSmilParser::makeSeqElement(SMILNode* pNode)
{
    HX_RESULT rc = HXR_OK;

    CSmilSeqElement* pElement =
      new CSmilSeqElement(pNode);

    BOOL bMinSet = FALSE;
    BOOL bMaxSet = FALSE;
    ULONG32 ulLesserOfDurOrEndMinusBegin = (UINT32)-1;
    LONG32 lEndMinusBegin = (UINT32)-1;

    // /Adjust dur if pNode->m_ulRepeatDur is set to a valid value:
    if (pNode->m_ulRepeatDur != UINT32(-1))
    {
      pElement->m_ulDuration = pNode->m_ulRepeatDur;
    }

    if(pNode->m_pValues)
    {
      // /Get sync ancestor in case it's a wrapper seq for repeating:
      SMILNode* pSyncAncestor = getSyncAncestor(pNode);
      BOOL bSyncAncestorIsRepeatWrapperSeq = pSyncAncestor?
            pSyncAncestor->m_bIsSeqWrapperForRepeatElement : FALSE;
      BOOL bAttributeHandledByWrappingParent = FALSE;

      const char* pName = 0;
      IHXBuffer* pBuf = 0;
      rc = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);

      while(HXR_OK == rc)
      {
          const char* pszValue =
                NULL!=pBuf?(const char*) pBuf->GetBuffer():NULL;
          HX_ASSERT(pszValue);
          if (!pszValue)
          {
            // /XXXEH- TODO: handle error below:
            rc = HXR_UNEXPECTED;
            break;
          }

            // Get the SMIL2Attribute
            SMIL2Attribute eAttr = getSMIL2Attribute(pName);
            switch (eAttr)
            {
                case SMIL2AttrDur:
                    {
                        rc = parseDuration(pszValue, pElement, SMILSyncAttrDur);
                        pElement->m_bHasExplicitDur = (HXR_OK == rc);
                    }
                    break;
                case SMIL2AttrMin:
                    {
                        if (!pNode->m_bMinHandledByWrapperParent)
                        {
                            rc = parseDuration(pszValue, pElement, SMILSyncAttrMin);
                            bMinSet = (HXR_OK == rc);
                        }
                        else
                        {
                            HX_ASSERT(bSyncAncestorIsRepeatWrapperSeq);
                        }
                    }
                    break;
                case SMIL2AttrMax:
                    {
                        if (!pNode->m_bMaxHandledByWrapperParent)
                        {
                            rc = parseDuration(pszValue, pElement, SMILSyncAttrMax);
                            bMaxSet = (HXR_OK == rc);
                        }
                        else
                        {
                            HX_ASSERT(bSyncAncestorIsRepeatWrapperSeq);
                        }
                    }
                    break;
                case SMIL2AttrBegin:
                    {
                        if (!pNode->m_bBeginHandledByWrapperParent)
                        {
                            HX_ASSERT(!pNode->m_bRepeatHandled  ||
                                    !RepeatReplica==pNode->m_repeatTag);
                            rc= parseBeginEnd(pszValue, pElement, SMILSyncAttrBegin);
                        }
                        else
                        {
                            HX_ASSERT(bSyncAncestorIsRepeatWrapperSeq);
                        }
                    }
                    break;
                case SMIL2AttrEnd:
                    {
                        // /XXXEH- do this for non-repeat wrappers for
                        // end(when dur also specified),min,max as well,
                        // and do same in seq/par/excl, too (and anchors?):
                        if (!pNode->m_bEndHandledByWrapperParent)
                        {
                            rc = parseBeginEnd(pszValue, pElement, SMILSyncAttrEnd);
                            pElement->m_bHasExplicitEnd = (HXR_OK == rc);
                        }
                        else
                        {
                            HX_ASSERT(bSyncAncestorIsRepeatWrapperSeq);
                        }
                    }
                    break;
                case SMIL2AttrFill:
                    {
                        rc = parseFill(pszValue, pElement->m_eFill);
                        if (HXR_OK != rc)
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                    pNode->m_ulTagStartLine);
                            pBuf->Release();
                            goto exit;
                        }
                    }
                    break;
                case SMIL2AttrFillDefault:
                    {
                        rc = parseFillDefault(pszValue, pElement->m_eFillDefault);
                        if (HXR_OK != rc)
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                    pNode->m_ulTagStartLine);
                            pBuf->Release();
                            goto exit;
                        }
                    }
                    break;
                case SMIL2AttrErase:
                    {
                        if (!strcmp(pszValue, "never"))
                        {
                            pElement->m_eErase = EraseNever;
                        }
                        else if (!strcmp(pszValue, "whenDone"))
                        {
                            pElement->m_eErase = EraseWhenDone;
                        }
                        else
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                                   pNode->m_ulTagStartLine);
                            pBuf->Release();
                            rc = HXR_FAIL;
                            goto exit;
                        }
                    }
                    break;
                case SMIL2AttrTitle:
                    {
                        pElement->m_title = pszValue;
                    }
                    break;
                case SMIL2AttrAuthor:
                    {
                        pElement->m_author = pszValue;
                    }
                    break;
                case SMIL2AttrCopyright:
                    {
                        pElement->m_copyright = pszValue;
                    }
                    break;
                case SMIL2AttrAbstract:
                    {
                        pElement->m_abstract = pszValue;
                    }
                    break;
                case SMIL2AttrRestart:
                    {
                        rc = parseRestart(pszValue, pElement);
                    }
                    break;
                case SMIL2AttrRestartDefault:
                    {
                        rc = parseRestartDefault(pszValue, pElement);
                    }
                    break;
                case SMIL2AttrRepeat:
                    {
                        pElement->m_fRepeatValue = atof(pszValue);
                    }
                    break;
                case SMIL2AttrSyncTolerance:
                    {
                        rc = parseDuration(pszValue, pElement,
                            SMILSyncAttrSyncTolerance);
                    }
                    break;
                case SMIL2AttrSyncToleranceDefault:
                    {
                        rc = parseDuration(pszValue, pElement,
                            SMILSyncAttrSyncToleranceDefault);
                    }
                    break;
                case SMIL2AttrSyncBehavior:
                    {
                        rc = parseSyncBehaviorVal(pszValue,
                                pElement, SMILSyncAttrSyncBehavior);
                        if (HXR_OK != rc)
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                    pNode->m_ulTagStartLine);
                        }
                    }
                    break;
                case SMIL2AttrSyncBehaviorDefault:
                    {
                        rc = parseSyncBehaviorVal(pszValue,
                                pElement, SMILSyncAttrSyncBehaviorDefault);
                        if (HXR_OK != rc)
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                    pNode->m_ulTagStartLine);
                        }
                    }
                    break;
                default:
                    {
                        // Check if the attribute is a legal namespaced attribute
                        const char*     pszNSAttr = NULL;
                        CNamespaceInfo* pNSInfo = getNamespaceInfo(pName, pszNSAttr);
                        if (pNSInfo &&
                            (pNSInfo->m_eNamespace == NamespaceAllSMIL2Extensions ||
                             pNSInfo->m_eNamespace == NamespaceAccessErrorBehavior))
                        {
                            if(strcmp(pszNSAttr, "accessErrorBehavior") == 0)
                            {
                                rc = parseAccessErrorBehavior(pszValue,
                                                              pElement->m_eAccessErrorBehavior);
                                if (FAILED(rc))
                                {
                                    CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                                    errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                                           pNode->m_ulTagStartLine);
                                }
                            }
                        }
                    }
            }

          pBuf->Release();

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

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

    // /Now, adjust duration in case both "dur" and "end" were set
    // and in case either "dur" or "end" was set and "min" and/or "max"
    // were set (since we don't have a chance to do this later as this is
    // not a child media element so durationResolved() never gets called):
    ulLesserOfDurOrEndMinusBegin = pElement->m_ulDuration;
    lEndMinusBegin = pElement->m_bEndOffsetSet?
          (pElement->m_lEndOffset -
          (pElement->m_bBeginOffsetSet? pElement->m_lBeginOffset : 0)) :
          (SMILTIME_NEGATIVE_INFINITY);

    if ((UINT32)-1 == ulLesserOfDurOrEndMinusBegin  ||
          (INT32)ulLesserOfDurOrEndMinusBegin > lEndMinusBegin)
    {
      if (SMILTIME_NEGATIVE_INFINITY != lEndMinusBegin)
      {
          ulLesserOfDurOrEndMinusBegin= lEndMinusBegin<0? 0:lEndMinusBegin;
      }
    }
    if ((UINT32)-1 != pElement->m_ulDuration  &&  pElement->m_bEndOffsetSet)
    {
      pElement->m_ulDuration = ulLesserOfDurOrEndMinusBegin;
      pElement->m_bEndOffsetSet = FALSE;
    }
    if (bMinSet)
    {
      if ((UINT32)-1 == ulLesserOfDurOrEndMinusBegin  ||
            ulLesserOfDurOrEndMinusBegin < pElement->m_ulMinActiveDur)
      {
          pElement->m_ulDuration = pElement->m_ulMinActiveDur;
          pElement->m_bEndOffsetSet = FALSE;
      }
    }
    if (bMaxSet)
    {
      // /Note: min is already guaranteed to be less than or equal to max
      // so we don't have to worry about min>max here:
      if ((UINT32)-1 == ulLesserOfDurOrEndMinusBegin  ||
            ulLesserOfDurOrEndMinusBegin > pElement->m_ulMaxActiveDur)
      {
          pElement->m_ulDuration = pElement->m_ulMaxActiveDur;
          pElement->m_bEndOffsetSet = FALSE;
      }
    }

    // If our "fill" attribute is "default", then we have
    // to resolve it from either our fillDefault attribute
    // (which may involve checking our ancestors).
    resolveFillValue(pElement);

    // /Fixes seq part of PR 60111: if this seq didn't have a "begin"
    // attribute, then set the following to TRUE because default begin is a
    // clock value (if our syncBase isn't an excl):
    if (!pElement->m_pBeginTimeList)
    {
      SMILNode* pSyncBase = getSyncAncestor(pElement->m_pNode);
      if (pSyncBase  &&  SMILExcl != pSyncBase->m_tag)
      {
          pElement->m_bHasAtLeastOneNonEventBasedBegin = TRUE;
      }
    }

    rc = adjustDuration(pElement);
    if (HXR_OK == rc)
    {
      pElement->m_ulAuthoredDur = pElement->m_ulDuration;
    }

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

    return pElement;
}

CSmilParElement*
CSmilParser::makeParElement(SMILNode* pNode)
{
    HX_RESULT rc = HXR_OK;

    CSmilParElement* pElement =
      new CSmilParElement(pNode);
    BOOL bMinSet = FALSE;
    BOOL bMaxSet = FALSE;
    ULONG32 ulLesserOfDurOrEndMinusBegin = (UINT32)-1;
    LONG32 lEndMinusBegin = (UINT32)-1;

    if(pNode->m_pValues)
    {
      // /Get sync ancestor in case it's a wrapper seq for repeating:
      SMILNode* pSyncAncestor = getSyncAncestor(pNode);
      BOOL bSyncAncestorIsRepeatWrapperSeq = pSyncAncestor?
            pSyncAncestor->m_bIsSeqWrapperForRepeatElement : FALSE;
      BOOL bAttributeHandledByWrappingParent = FALSE;

      const char* pName = 0;
      IHXBuffer* pBuf = 0;
      rc = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);
      while(HXR_OK == rc)
      {
          const char* pszValue =
                NULL!=pBuf?(const char*) pBuf->GetBuffer():NULL;
          HX_ASSERT(pszValue);
          if (!pszValue)
          {
            // /XXXEH- TODO: handle error below:
            rc = HXR_UNEXPECTED;
            break;
          }

            // Get the SMIL2Attribute
            SMIL2Attribute eAttr = getSMIL2Attribute(pName);
            switch (eAttr)
            {
                case SMIL2AttrDur:
                    {
                        rc = parseDuration(pszValue, pElement, SMILSyncAttrDur);
                        pElement->m_bHasExplicitDur = (HXR_OK == rc);
                    }
                    break;
                case SMIL2AttrMin:
                    {
                        if (!pNode->m_bMinHandledByWrapperParent)
                        {
                            rc = parseDuration(pszValue, pElement, SMILSyncAttrMin);
                            bMinSet = (HXR_OK == rc);
                        }
                        else
                        {
                            HX_ASSERT(bSyncAncestorIsRepeatWrapperSeq);
                        }
                    }
                    break;
                case SMIL2AttrMax:
                    {
                        if (!pNode->m_bMaxHandledByWrapperParent)
                        {
                            rc = parseDuration(pszValue, pElement, SMILSyncAttrMax);
                            bMaxSet = (HXR_OK == rc);
                        }
                        else
                        {
                            HX_ASSERT(bSyncAncestorIsRepeatWrapperSeq);
                        }
                    }
                    break;
                case SMIL2AttrBegin:
                    {
                        if (!pNode->m_bBeginHandledByWrapperParent)
                        {
                            HX_ASSERT(!pNode->m_bRepeatHandled  ||
                                    !RepeatReplica==pNode->m_repeatTag);
                            rc= parseBeginEnd(pszValue, pElement, SMILSyncAttrBegin);
                        }
                        else
                        {
                            HX_ASSERT(bSyncAncestorIsRepeatWrapperSeq);
                        }
                    }
                    break;
                case SMIL2AttrEnd:
                    {
                        if (!pNode->m_bEndHandledByWrapperParent)
                        {
                            rc = parseBeginEnd(pszValue, pElement, SMILSyncAttrEnd);
                            pElement->m_bHasExplicitEnd = (HXR_OK == rc);
                        }
                        else
                        {
                            HX_ASSERT(bSyncAncestorIsRepeatWrapperSeq);
                        }
                    }
                    break;
                case SMIL2AttrEndsync:
                    {
                        rc = parseDuration(pszValue, pElement, SMILSyncAttrEndsync);
                    }
                    break;
                case SMIL2AttrFill:
                    {
                        rc = parseFill(pszValue, pElement->m_eFill);
                        if (HXR_OK != rc)
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                    pNode->m_ulTagStartLine);
                            pBuf->Release();
                            goto exit;
                        }
                    }
                    break;
                case SMIL2AttrFillDefault:
                    {
                        rc = parseFillDefault(pszValue, pElement->m_eFillDefault);
                        if (HXR_OK != rc)
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                    pNode->m_ulTagStartLine);
                            pBuf->Release();
                            goto exit;
                        }
                    }
                    break;
                case SMIL2AttrErase:
                    {
                        if (!strcmp(pszValue, "never"))
                        {
                            pElement->m_eErase = EraseNever;
                        }
                        else if (!strcmp(pszValue, "whenDone"))
                        {
                            pElement->m_eErase = EraseWhenDone;
                        }
                        else
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                                   pNode->m_ulTagStartLine);
                            pBuf->Release();
                            rc = HXR_FAIL;
                            goto exit;
                        }
                    }
                    break;
                case SMIL2AttrTitle:
                    {
                        pElement->m_title = pszValue;
                    }
                    break;
                case SMIL2AttrAuthor:
                    {
                        pElement->m_author = pszValue;
                    }
                    break;
                case SMIL2AttrCopyright:
                    {
                        pElement->m_copyright = pszValue;
                    }
                    break;
                case SMIL2AttrAbstract:
                    {
                        pElement->m_abstract = pszValue;
                    }
                    break;
                case SMIL2AttrRestart:
                    {
                        rc = parseRestart(pszValue, pElement);
                    }
                    break;
                case SMIL2AttrRestartDefault:
                    {
                        rc = parseRestartDefault(pszValue, pElement);
                    }
                    break;
                case SMIL2AttrRepeat:
                    {
                        pElement->m_fRepeatValue = atof(pszValue);
                    }
                    break;
                case SMIL2AttrSyncTolerance:
                    {
                        rc = parseDuration(pszValue, pElement,
                            SMILSyncAttrSyncTolerance);
                    }
                    break;
                case SMIL2AttrSyncToleranceDefault:
                    {
                        rc = parseDuration(pszValue, pElement,
                            SMILSyncAttrSyncToleranceDefault);
                    }
                    break;
                case SMIL2AttrSyncBehavior:
                    {
                        rc = parseSyncBehaviorVal(pszValue,
                                pElement, SMILSyncAttrSyncBehavior);
                        if (HXR_OK != rc)
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                    pNode->m_ulTagStartLine);
                        }
                    }
                    break;
                case SMIL2AttrSyncBehaviorDefault:
                    {
                        rc = parseSyncBehaviorVal(pszValue,
                                pElement, SMILSyncAttrSyncBehaviorDefault);
                        if (HXR_OK != rc)
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                    pNode->m_ulTagStartLine);
                        }
                    }
                    break;
                default:
                    {
                        // Check if the attribute is a legal namespaced attribute
                        const char*     pszNSAttr = NULL;
                        CNamespaceInfo* pNSInfo = getNamespaceInfo(pName, pszNSAttr);
                        if (pNSInfo &&
                            (pNSInfo->m_eNamespace == NamespaceAllSMIL2Extensions ||
                             pNSInfo->m_eNamespace == NamespaceAccessErrorBehavior))
                        {
                            if(strcmp(pszNSAttr, "accessErrorBehavior") == 0)
                            {
                                rc = parseAccessErrorBehavior(pszValue,
                                                              pElement->m_eAccessErrorBehavior);
                                if (FAILED(rc))
                                {
                                    CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                                    errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                                           pNode->m_ulTagStartLine);
                                }
                            }
                        }
                    }
            }


          pBuf->Release();

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

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

    // /Now, adjust duration in case both "dur" and "end" were set
    // and in case either "dur" or "end" was set and "min" and/or "max"
    // were set (since we don't have a chance to do this later as this is
    // not a child media element so durationResolved() never gets called):
    ulLesserOfDurOrEndMinusBegin = pElement->m_ulDuration;
    lEndMinusBegin = pElement->m_bEndOffsetSet?
          (pElement->m_lEndOffset -
          (pElement->m_bBeginOffsetSet? pElement->m_lBeginOffset : 0)) :
          (SMILTIME_NEGATIVE_INFINITY);

    if ((UINT32)-1 == ulLesserOfDurOrEndMinusBegin  ||
          (INT32)ulLesserOfDurOrEndMinusBegin > lEndMinusBegin)
    {
      if (SMILTIME_NEGATIVE_INFINITY != lEndMinusBegin)
      {
          ulLesserOfDurOrEndMinusBegin= lEndMinusBegin<0? 0:lEndMinusBegin;
      }
    }
    if ((UINT32)-1 != pElement->m_ulDuration  &&  pElement->m_bEndOffsetSet)
    {
      pElement->m_ulDuration = ulLesserOfDurOrEndMinusBegin;
      pElement->m_bEndOffsetSet = FALSE;
    }
    if (bMinSet)
    {
      if ((UINT32)-1 == ulLesserOfDurOrEndMinusBegin  ||
            ulLesserOfDurOrEndMinusBegin < pElement->m_ulMinActiveDur)
      {
          pElement->m_ulDuration = pElement->m_ulMinActiveDur;
          pElement->m_bEndOffsetSet = FALSE;
      }
    }
    if (bMaxSet)
    {
      // /Note: min is already guaranteed to be less than or equal to max
      // so we don't have to worry about min>max here:
      if ((UINT32)-1 == ulLesserOfDurOrEndMinusBegin  ||
            ulLesserOfDurOrEndMinusBegin > pElement->m_ulMaxActiveDur)
      {
          pElement->m_ulDuration = pElement->m_ulMaxActiveDur;
          pElement->m_bEndOffsetSet = FALSE;
      }
    }

    // If our "fill" attribute is "default", then we have
    // to resolve it from either our fillDefault attribute
    // (which may involve checking our ancestors).
    resolveFillValue(pElement);

    // /Fixes par part of PR 60111: if this par didn't have a "begin"
    // attribute, then set the following to TRUE because default begin is a
    // clock value (if our syncBase isn't an excl):
    if (!pElement->m_pBeginTimeList)
    {
      SMILNode* pSyncBase = getSyncAncestor(pElement->m_pNode);
      if (pSyncBase  &&  SMILExcl != pSyncBase->m_tag)
      {
          pElement->m_bHasAtLeastOneNonEventBasedBegin = TRUE;
      }
    }

    rc = adjustDuration(pElement);
    if (HXR_OK == rc)
    {
      pElement->m_ulAuthoredDur = pElement->m_ulDuration;
    }

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

    return pElement;
}

CSmilExclElement*
CSmilParser::makeExclElement(SMILNode* pNode)
{
    HX_RESULT rc = HXR_OK;

    CSmilExclElement* pElement =
      new CSmilExclElement(pNode);

    BOOL bMinSet = FALSE;
    BOOL bMaxSet = FALSE;
    ULONG32 ulLesserOfDurOrEndMinusBegin = (UINT32)-1;
    LONG32 lEndMinusBegin = (UINT32)-1;

    if(pNode->m_pValues)
    {
      // /Get sync ancestor in case it's a wrapper seq for repeating:
      SMILNode* pSyncAncestor = getSyncAncestor(pNode);
      BOOL bSyncAncestorIsRepeatWrapperSeq = pSyncAncestor?
            pSyncAncestor->m_bIsSeqWrapperForRepeatElement : FALSE;
      BOOL bAttributeHandledByWrappingParent = FALSE;

      const char* pName = 0;
      IHXBuffer* pBuf = 0;
      rc = pNode->m_pValues->GetFirstPropertyCString(pName, pBuf);

      while(HXR_OK == rc)
      {
          const char* pszValue =
                NULL!=pBuf?(const char*) pBuf->GetBuffer():NULL;
          HX_ASSERT(pszValue);
          if (!pszValue)
          {
            // /XXXEH- TODO: handle error below:
            rc = HXR_UNEXPECTED;
            break;
          }

            // Get the SMIL2Attribute
            SMIL2Attribute eAttr = getSMIL2Attribute(pName);
            // Switch on attribute name
            switch (eAttr)
            {
                case SMIL2AttrDur:
                    {
                        rc = parseDuration(pszValue, pElement, SMILSyncAttrDur);
                        pElement->m_bHasExplicitDur = (HXR_OK == rc);
                    }
                    break;
                case SMIL2AttrMin:
                    {
                        if (!pNode->m_bMinHandledByWrapperParent)
                        {
                            rc = parseDuration(pszValue, pElement, SMILSyncAttrMin);
                            bMinSet = (HXR_OK == rc);
                        }
                        else
                        {
                            HX_ASSERT(bSyncAncestorIsRepeatWrapperSeq);
                        }
                    }
                    break;
                case SMIL2AttrMax:
                    {
                        if (!pNode->m_bMaxHandledByWrapperParent)
                        {
                            rc = parseDuration(pszValue, pElement, SMILSyncAttrMax);
                            bMaxSet = (HXR_OK == rc);
                        }
                        else
                        {
                            HX_ASSERT(bSyncAncestorIsRepeatWrapperSeq);
                        }
                    }
                    break;
                case SMIL2AttrBegin:
                    {
                        if (!pNode->m_bBeginHandledByWrapperParent)
                        {
                            HX_ASSERT(!pNode->m_bRepeatHandled  ||
                                    !RepeatReplica==pNode->m_repeatTag);
                            rc= parseBeginEnd(pszValue, pElement, SMILSyncAttrBegin);
                        }
                        else
                        {
                            HX_ASSERT(bSyncAncestorIsRepeatWrapperSeq);
                        }
                    }
                    break;
                case SMIL2AttrEnd:
                    {
                        if (!pNode->m_bEndHandledByWrapperParent)
                        {
                            rc = parseBeginEnd(pszValue, pElement, SMILSyncAttrEnd);
                            pElement->m_bHasExplicitEnd = (HXR_OK == rc);
                        }
                        else
                        {
                            HX_ASSERT(bSyncAncestorIsRepeatWrapperSeq);
                        }
                    }
                    break;
                case SMIL2AttrEndsync:
                    {
                        rc = parseDuration(pszValue, pElement, SMILSyncAttrEndsync);
                    }
                    break;
                case SMIL2AttrFill:
                    {
                        rc = parseFill(pszValue, pElement->m_eFill);
                        if (HXR_OK != rc)
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                    pNode->m_ulTagStartLine);
                            pBuf->Release();
                            goto exit;
                        }
                    }
                    break;
                case SMIL2AttrFillDefault:
                    {
                        rc = parseFillDefault(pszValue, pElement->m_eFillDefault);
                        if (HXR_OK != rc)
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                    pNode->m_ulTagStartLine);
                            pBuf->Release();
                            goto exit;
                        }
                    }
                    break;
                case SMIL2AttrErase:
                    {
                        if (!strcmp(pszValue, "never"))
                        {
                            pElement->m_eErase = EraseNever;
                        }
                        else if (!strcmp(pszValue, "whenDone"))
                        {
                            pElement->m_eErase = EraseWhenDone;
                        }
                        else
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                                   pNode->m_ulTagStartLine);
                            pBuf->Release();
                            rc = HXR_FAIL;
                            goto exit;
                        }
                    }
                    break;
                case SMIL2AttrTitle:
                    {
                        pElement->m_title = pszValue;
                    }
                    break;
                case SMIL2AttrAuthor:
                    {
                        pElement->m_author = pszValue;
                    }
                    break;
                case SMIL2AttrCopyright:
                    {
                        pElement->m_copyright = pszValue;
                    }
                    break;
                case SMIL2AttrAbstract:
                    {
                        pElement->m_abstract = pszValue;
                    }
                    break;
                case SMIL2AttrRestart:
                    {
                        rc = parseRestart(pszValue, pElement);
                    }
                    break;
                case SMIL2AttrRestartDefault:
                    {
                        rc = parseRestartDefault(pszValue, pElement);
                    }
                    break;
                case SMIL2AttrRepeat:
                    {
                        pElement->m_fRepeatValue =
                                atof(pszValue);
                    }
                    break;
                case SMIL2AttrSyncTolerance:
                    {
                        rc = parseDuration(pszValue, pElement,
                            SMILSyncAttrSyncTolerance);
                    }
                    break;
                case SMIL2AttrSyncToleranceDefault:
                    {
                        rc = parseDuration(pszValue, pElement,
                            SMILSyncAttrSyncToleranceDefault);
                    }
                    break;
                case SMIL2AttrSyncBehavior:
                    {
                        rc = parseSyncBehaviorVal(pszValue,
                                pElement, SMILSyncAttrSyncBehavior);
                        if (HXR_OK != rc)
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                    pNode->m_ulTagStartLine);
                        }
                    }
                    break;
                case SMIL2AttrSyncBehaviorDefault:
                    {
                        rc = parseSyncBehaviorVal(pszValue,
                                pElement, SMILSyncAttrSyncBehaviorDefault);
                        if (HXR_OK != rc)
                        {
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                    pNode->m_ulTagStartLine);
                        }
                    }
                    break;
                default:
                    {
                        // Check if the attribute is a legal namespaced attribute
                        const char*     pszNSAttr = NULL;
                        CNamespaceInfo* pNSInfo = getNamespaceInfo(pName, pszNSAttr);
                        if (pNSInfo &&
                            (pNSInfo->m_eNamespace == NamespaceAllSMIL2Extensions ||
                             pNSInfo->m_eNamespace == NamespaceAccessErrorBehavior))
                        {
                            if(strcmp(pszNSAttr, "accessErrorBehavior") == 0)
                            {
                                rc = parseAccessErrorBehavior(pszValue,
                                                              pElement->m_eAccessErrorBehavior);
                                if (FAILED(rc))
                                {
                                    CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                                    errHandler.ReportError(SMILErrorBadAttribute, pszValue,
                                                           pNode->m_ulTagStartLine);
                                }
                            }
                        }
                    }
            }

          pBuf->Release();

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

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

    // /Now, adjust duration in case both "dur" and "end" were set
    // and in case either "dur" or "end" was set and "min" and/or "max"
    // were set (since we don't have a chance to do this later as this is
    // not a child media element so durationResolved() never gets called):
    ulLesserOfDurOrEndMinusBegin = pElement->m_ulDuration;
    lEndMinusBegin = pElement->m_bEndOffsetSet?
          (pElement->m_lEndOffset -
          (pElement->m_bBeginOffsetSet? pElement->m_lBeginOffset : 0)) :
          (SMILTIME_NEGATIVE_INFINITY);

    if ((UINT32)-1 == ulLesserOfDurOrEndMinusBegin  ||
          (INT32)ulLesserOfDurOrEndMinusBegin > lEndMinusBegin)
    {
      if (SMILTIME_NEGATIVE_INFINITY != lEndMinusBegin)
      {
          ulLesserOfDurOrEndMinusBegin= lEndMinusBegin<0? 0:lEndMinusBegin;
      }
    }
    if ((UINT32)-1 != pElement->m_ulDuration  &&  pElement->m_bEndOffsetSet)
    {
      pElement->m_ulDuration = ulLesserOfDurOrEndMinusBegin;
      pElement->m_bEndOffsetSet = FALSE;
    }
    if (bMinSet)
    {
      if ((UINT32)-1 == ulLesserOfDurOrEndMinusBegin  ||
            ulLesserOfDurOrEndMinusBegin < pElement->m_ulMinActiveDur)
      {
          pElement->m_ulDuration = pElement->m_ulMinActiveDur;
          pElement->m_bEndOffsetSet = FALSE;
      }
    }
    if (bMaxSet)
    {
      // /Note: min is already guaranteed to be less than or equal to max
      // so we don't have to worry about min>max here:
      if ((UINT32)-1 == ulLesserOfDurOrEndMinusBegin  ||
            ulLesserOfDurOrEndMinusBegin > pElement->m_ulMaxActiveDur)
      {
          pElement->m_ulDuration = pElement->m_ulMaxActiveDur;
          pElement->m_bEndOffsetSet = FALSE;
      }
    }

    // If our "fill" attribute is "default", then we have
    // to resolve it from either our fillDefault attribute
    // (which may involve checking our ancestors).
    resolveFillValue(pElement);

    // /Fixes excl version of PR 60111: if this excl didn't have a "begin"
    // attribute, then set the following to TRUE because default begin is a
    // clock value (if our syncBase isn't an excl):
    if (!pElement->m_pBeginTimeList)
    {
      SMILNode* pSyncBase = getSyncAncestor(pElement->m_pNode);
      if (pSyncBase  &&  SMILExcl != pSyncBase->m_tag)
      {
          pElement->m_bHasAtLeastOneNonEventBasedBegin = TRUE;
      }
    }

    rc = adjustDuration(pElement);
    if (HXR_OK == rc)
    {
      pElement->m_ulAuthoredDur = pElement->m_ulDuration;
    }

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

    return pElement;
}

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)

// XXXMEH
// List of error conditions to check for
// 1. 1 value -> default to discrete
// 4. all values same type
// 5. If just two values and paced -> default to linear
CSmilAnimateElement* CSmilParser::makeAnimateElement(SMILNode* pNode)
{
    CSmilAnimateElement* pAnimate = NULL;
    HX_RESULT            retVal   = HXR_OK;

    if (pNode)
    {
        pAnimate = new CSmilAnimateElement(pNode);
        if (pAnimate)
        {
            retVal = animSetupElement(pAnimate);
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    if (FAILED(retVal))
    {
        HX_DELETE(pAnimate);
    }

    return pAnimate;
}

HX_RESULT CSmilParser::animSetupElement(CSmilAnimateElement* pAnim)
{
    HX_RESULT retVal = HXR_OK;

    if(pAnim && pAnim->m_pNode && pAnim->m_pNode->m_pValues)
    {
        // Set up flags to determine what type of animation this is
        BOOL        bFromSpecified          = FALSE;
        BOOL        bToSpecified            = FALSE;
        BOOL        bBySpecified            = FALSE;
        BOOL        bValuesSpecified        = FALSE;
        BOOL        bTargetElementSpecified = FALSE;
        BOOL        bAttrNameSpecified      = FALSE;
        BOOL        bGotNSCalcMode          = FALSE;
        SMILNodeTag eAnimTag                = pAnim->m_pNode->m_tag;
        // Loop through the atttributes
        const char* pName = NULL;
        IHXBuffer* pVal  = NULL;
        HX_RESULT   rc    = pAnim->m_pNode->m_pValues->GetFirstPropertyCString(pName, pVal);
        while(SUCCEEDED(rc) && SUCCEEDED(retVal))
        {
            // Get the value of the attribute
            const char* pszVal = (const char*) pVal->GetBuffer();
            // Get the SMIL2Attribute
            SMIL2Attribute eAttr = getSMIL2Attribute(pName);
            // Switch on attribute name
            switch (eAttr)
            {
                case SMIL2AttrAttributeName:
                    {
                        // Set the flag
                        bAttrNameSpecified = TRUE;
                        // Find out which attribute we are animating
                        if (strcmp(pszVal, "left") == 0)
                        {
                            pAnim->m_ucAttributeName = kAttrNameLeft;
                        }
                        else if (strcmp(pszVal, "top") == 0)
                        {
                            pAnim->m_ucAttributeName = kAttrNameTop;
                        }
                        else if (strcmp(pszVal, "right") == 0)
                        {
                            pAnim->m_ucAttributeName = kAttrNameRight;
                        }
                        else if (strcmp(pszVal, "bottom") == 0)
                        {
                            pAnim->m_ucAttributeName = kAttrNameBottom;
                        }
                        else if (strcmp(pszVal, "width") == 0)
                        {
                            pAnim->m_ucAttributeName = kAttrNameWidth;
                        }
                        else if (strcmp(pszVal, "height") == 0)
                        {
                            pAnim->m_ucAttributeName = kAttrNameHeight;
                        }
                        else if (strcmp(pszVal, "background-color") == 0 ||
                                 strcmp(pszVal, "backgroundColor")  == 0)
                        {
                            pAnim->m_ucAttributeName = kAttrNameBackgroundColor;
                        }
                        else if (strcmp(pszVal, "z-index") == 0 ||
                                 strcmp(pszVal, "zIndex")  == 0)
                        {
                            pAnim->m_ucAttributeName = kAttrNameZIndex;
                        }
                        else if (strcmp(pszVal, "soundLevel") == 0)
                        {
                            pAnim->m_ucAttributeName = kAttrNameSoundLevel;
                        }
                        else if (strcmp(pszVal, "coords") == 0)
                        {
                            pAnim->m_ucAttributeName = kAttrNameCoords;
                        }
                        else if (strcmp(pszVal, "color") == 0)
                        {
                            pAnim->m_ucAttributeName = kAttrNameColor;
                        }
                        else if (strcmp(pszVal, "value") == 0)
                        {
                            pAnim->m_ucAttributeName = kAttrNameValue;
                        }
                        else
                        {
                            // Find out if this attributeName is namespace-prefixed.
                            HX_RESULT       rv            = HXR_FAIL;
                            const char*     pszNSAttrName = NULL;
                            CNamespaceInfo* pNSInfo       = getNamespaceInfo(pszVal, pszNSAttrName);
                            if (pNSInfo)
                            {
                                // This attributeName IS namespace-prefixed.
                                //
                                // If this is the alphaControl namespace, then we
                                // we should parse for the four time-manipulations attributes
                                if (pNSInfo->m_eNamespace == NamespaceAlphaControl ||
                                    pNSInfo->m_eNamespace == NamespaceAllSMIL2Extensions)
                                {
                                    if(strcmp(pszNSAttrName, "mediaOpacity") == 0)
                                    {
                                        pAnim->m_ucAttributeName = kAttrNameMediaOpacity;
                                        rv                       = HXR_OK;
                                    }
                                    else if (strcmp(pszNSAttrName, "backgroundOpacity") == 0)
                                    {
                                        pAnim->m_ucAttributeName = kAttrNameBackgroundOpacity;
                                        rv                       = HXR_OK;
                                    }
                                }
                            }
                            if (FAILED(rv))
                            {
                                retVal = HXR_FAIL;
                                CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                                errHandler.ReportError(SMILErrorBadAttribute,
                                                       pName,
                                                       pAnim->m_pNode->m_ulTagStartLine);
                            }
                        }
                    }
                    break;
                case SMIL2AttrFrom:
                    {
                        bFromSpecified = TRUE;
                    }
                    break;
                case SMIL2AttrTo:
                    {
                        bToSpecified = TRUE;
                    }
                    break;
                case SMIL2AttrBy:
                    {
                        bBySpecified = TRUE;
                    }
                    break;
                case SMIL2AttrValues:
                    {
                        bValuesSpecified = TRUE;
                    }
                    break;
                case SMIL2AttrBegin:
                    {
                        retVal = parseBeginEnd(pszVal, pAnim, SMILSyncAttrBegin);
                    }
                    break;
                case SMIL2AttrEnd:
                    {
                        retVal = parseBeginEnd(pszVal, pAnim, SMILSyncAttrEnd);
                        pAnim->m_bHasExplicitEnd = (HXR_OK == retVal);
                    }
                    break;
                case SMIL2AttrDur:
                    {
                        if (strcmp(pszVal, "indefinite") != 0)
                        {
                            retVal = parseDuration(pszVal, pAnim, SMILSyncAttrDur);
                            if (SUCCEEDED(retVal))
                            {
                                pAnim->m_bHasExplicitDur = TRUE;
                                pAnim->m_ulSimpleDuration = pAnim->m_ulDuration;
                            }
                        }
                    }
                    break;
                case SMIL2AttrRepeatDur:
                    {
                        if (strcmp(pszVal, "indefinite") != 0)
                        {
                            retVal = parseClockValue(pszVal, pAnim->m_ulRepeatDur);
                        }
                        else
                        {
                            pAnim->m_bRepeatDurIsIndefinite = TRUE;
                        }
                    }
                    break;
                case SMIL2AttrTargetElement:
                    {
                        bTargetElementSpecified   = TRUE;
                        pAnim->m_pTargetElementID = new CHXString(pszVal);
                        if (!pAnim->m_pTargetElementID)
                        {
                            retVal = HXR_OUTOFMEMORY;
                        }
                    }
                    break;
                case SMIL2AttrCalcMode:
                    {
                        if (!bGotNSCalcMode)
                        {
                            if (strcmp(pszVal, "discrete") == 0)
                            {
                                pAnim->m_ucCalcMode = kCalcModeDiscrete;
                            }
                            else if (strcmp(pszVal, "linear") == 0)
                            {
                                pAnim->m_ucCalcMode = kCalcModeLinear;
                            }
                            else if (strcmp(pszVal, "paced") == 0)
                            {
                                pAnim->m_ucCalcMode = kCalcModePaced;
                            }
                            else
                            {
                                // XXXMEH - illegal calcMode
                                retVal = HXR_FAIL;
                                CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                                errHandler.ReportError(SMILErrorBadAttribute,
                                                       pName,
                                                       pAnim->m_pNode->m_ulTagStartLine);
                            }
                        }
                    }
                    break;
                case SMIL2AttrAccumulate:
                    {
                        if(eAnimTag != SMILSet)
                        {
                            if (strcmp(pszVal, "sum") == 0)
                            {
                                pAnim->m_ucAccumulate = kAccumulateSum;
                            }
                            else if (strcmp(pszVal, "none") == 0)
                            {
                                pAnim->m_ucAccumulate = kAccumulateNone;
                            }
                            else
                            {
                                // XXXMEH - illegal accumulate
                                retVal = HXR_FAIL;
                                CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                                errHandler.ReportError(SMILErrorBadAttribute,
                                                       pName,
                                                       pAnim->m_pNode->m_ulTagStartLine);
                            }
                        }
                    }
                    break;
                case SMIL2AttrAdditive:
                    {
                        if(eAnimTag != SMILSet)
                        {
                            if (strcmp(pszVal, "sum") == 0)
                            {
                                pAnim->m_ucAdditive = kAdditiveSum;
                            }
                            else if (strcmp(pszVal, "replace") == 0)
                            {
                                pAnim->m_ucAdditive = kAdditiveReplace;
                            }
                            else
                            {
                                // XXXMEH - illegal additive
                                retVal = HXR_FAIL;
                                CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                                errHandler.ReportError(SMILErrorBadAttribute,
                                                       pName,
                                                       pAnim->m_pNode->m_ulTagStartLine);
                            }
                        }
                    }
                    break;
                case SMIL2AttrRepeatCount:
                    {
                        pAnim->m_dRepeatCount = atof(pszVal);
                        if (pAnim->m_dRepeatCount <= 0.0)
                        {
                            // XXXMEH - Negative value for repeatCount
                            retVal = HXR_FAIL;
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute,
                                                   pName,
                                                   pAnim->m_pNode->m_ulTagStartLine);
                        }
                    }
                    break;
                case SMIL2AttrFill:
                    {
                        retVal = parseFill(pszVal, pAnim->m_eFill);
                        if (FAILED(retVal))
                        {
                            // XXXMEH - illegal fill
                            retVal = HXR_FAIL;
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute,
                                                   pName,
                                                   pAnim->m_pNode->m_ulTagStartLine);
                        }
                    }
                    break;
                case SMIL2AttrFillDefault:
                    {
                        retVal = parseFillDefault(pszVal, pAnim->m_eFillDefault);
                        if (FAILED(retVal))
                        {
                            // XXXMEH - illegal fillDefault
                            retVal = HXR_FAIL;
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorBadAttribute,
                                                   pName,
                                                   pAnim->m_pNode->m_ulTagStartLine);
                        }
                    }
                    break;
                default:
                    {
                        // Find out if this attribute is namespace-prefixed. Since
                        // we assume that this document has already been validated,
                        // then we don't have to throw an error if it's not - it's
                        // probably just an attribute we are not parsing above.
                        const char*     pszAttr = NULL;
                        CNamespaceInfo* pNSInfo = getNamespaceInfo(pName, pszAttr);
                        if (pNSInfo)
                        {
                            // This attribute IS namespace-prefixed.
                            //
                            // If this is the TimeManipulations namespace, then
                            // we should parse for the four time-manipulations attributes
                            if (pNSInfo->m_eNamespace == NamespaceSMIL2TimeManipulations)
                            {
                                if(strcmp(pszAttr, "accelerate") == 0)
                                {
                                    retVal = parseAccelDecel(pszVal, pAnim->m_dAccelerate);
                                    if (FAILED(retVal))
                                    {
                                        // XXXMEH - illegal accelerate attribute
                                        CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                                        errHandler.ReportError(SMILErrorBadAttribute,
                                                               pName,
                                                               pAnim->m_pNode->m_ulTagStartLine);
                                    }
                                }
                                else if(strcmp(pszAttr, "decelerate") == 0)
                                {
                                    retVal = parseAccelDecel(pszVal, pAnim->m_dDecelerate);
                                    if (FAILED(retVal))
                                    {
                                        // XXXMEH - illegal decelerate attribute
                                        CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                                        errHandler.ReportError(SMILErrorBadAttribute,
                                                               pName,
                                                               pAnim->m_pNode->m_ulTagStartLine);
                                    }
                                }
                                else if(strcmp(pszAttr, "autoReverse") == 0)
                                {
                                    if (strcmp(pszVal, "true") == 0)
                                    {
                                        pAnim->m_bAutoReverse = TRUE;
                                    }
                                    else if (strcmp(pszVal, "false") == 0)
                                    {
                                        pAnim->m_bAutoReverse = FALSE;
                                    }
                                    else
                                    {
                                        // XXXMEH - illegal autoReverse
                                        retVal = HXR_FAIL;
                                        CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                                        errHandler.ReportError(SMILErrorBadAttribute,
                                                               pName,
                                                               pAnim->m_pNode->m_ulTagStartLine);
                                    }
                                }
                                else if(strcmp(pszAttr, "speed") == 0)
                                {
                                    double dVal = 0.0;
                                    retVal = HXParseDouble(pszVal, dVal);
                                    if (SUCCEEDED(retVal))
                                    {
                                        // Spec explicitly says to ignore a speed of 0.0,
                                        // so if we see a 0.0, we simply set it back to
                                        // the default speed of 1.0
                                        if (dVal == 0.0)
                                        {
                                            dVal = 1.0;
                                        }
                                        // Assign to the speed parameter
                                        pAnim->m_dSpeed = dVal;
                                    }
                                    else
                                    {
                                        // XXXMEH - illegal speed attribute
                                        CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                                        errHandler.ReportError(SMILErrorBadAttribute,
                                                               pName,
                                                               pAnim->m_pNode->m_ulTagStartLine);
                                    }
                                }
                            }
#if defined(XXXMEH_SPLINE_ANIMATION)
                            else if (pNSInfo->m_eNamespace == NamespaceSMIL2SplineAnimation)
                            {
                                // We need to parse for "keyTimes", "keySplines", "path"
                                if(strcmp(pszAttr, "keyTimes") == 0)
                                {
                                    retVal = parseKeyTimes(pszVal, pAnim);
                                }
                                else if(strcmp(pszAttr, "keySplines") == 0)
                                {
                                    retVal = parseKeySplines(pszVal, pAnim);
                                }
                                else if(strcmp(pszAttr, "path") == 0)
                                {
                                    retVal = parseSVGPath(pszVal, pAnim);
                                }
                                else if (strcmp(pszAttr, "calcMode") == 0)
                                {
                                    bGotNSCalcMode = TRUE;
                                    if (strcmp(pszVal, "discrete") == 0)
                                    {
                                        pAnim->m_ucCalcMode = kCalcModeDiscrete;
                                    }
                                    else if (strcmp(pszVal, "linear") == 0)
                                    {
                                        pAnim->m_ucCalcMode = kCalcModeLinear;
                                    }
                                    else if (strcmp(pszVal, "paced") == 0)
                                    {
                                        pAnim->m_ucCalcMode = kCalcModePaced;
                                    }
                                    else if (strcmp(pszVal, "spline") == 0)
                                    {
                                        pAnim->m_ucCalcMode = kCalcModeSpline;
                                    }
                                    else
                                    {
                                        retVal = HXR_FAIL;
                                    }
                                }
                                if (FAILED(retVal))
                                {
                                    CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                                    errHandler.ReportError(SMILErrorBadAttribute,
                                                           pName,
                                                           pAnim->m_pNode->m_ulTagStartLine);
                                }
                            }
#endif // #if defined(XXXMEH_SPLINE_ANIMATION)
                        }
                    }
            }

            HX_RELEASE(pVal);

            if (SUCCEEDED(retVal))
            {
                rc = pAnim->m_pNode->m_pValues->GetNextPropertyCString(pName, pVal);
            }
        }

      // /Fixes animate version of PR 60111: : if this didn't have a "begin"
      // attribute, then set the following to TRUE because default begin is a
      // clock value (if our syncBase isn't an excl):
      if (!pAnim->m_pBeginTimeList)
      {
          SMILNode* pSyncBase = getSyncAncestor(pAnim->m_pNode);
          if (pSyncBase  &&  SMILExcl != pSyncBase->m_tag)
          {
            pAnim->m_bHasAtLeastOneNonEventBasedBegin = TRUE;
          }
      }

        if (SUCCEEDED(retVal))
        {
            // Set the authored dur
            pAnim->m_ulAuthoredDur = pAnim->m_ulDuration;
            // If our "fill" attribute is "default", then we have
            // to resolve it from either our fillDefault attribute
            // (which may involve checking our ancestors).
            resolveFillValue(pAnim);
            // If this element didn't have a "begin" attribute,
            // then set the following to TRUE because default begin is a clock value
            // (if our syncBase isn't an excl):
            if (!pAnim->m_pBeginTimeList)
            {
              SMILNode* pSyncBase = getSyncAncestor(pAnim->m_pNode);
              if (pSyncBase  &&  SMILExcl != pSyncBase->m_tag)
              {
                  pAnim->m_bHasAtLeastOneNonEventBasedBegin = TRUE;
              }
            }
            // Now we can do some additional checking
            //
            // SPEC: If the target attribute does not support linear
            // interpolation (e.g. for strings), or if the values attribute
            // has only one value, the calcMode attribute is ignored and
            // discrete interpolation is used.
            // SPEC: This attribute (accumulate) is ignored if the target
            // attribute value does not support addition. (Thus, if it
            // is ignored, it defaults to "none").
            // SPEC: This attribute (additive) is ignored if the target
            // attribute does not support additive animation. (Thus, if
            // it is ignored, then it defaults to "replace").
            if (pAnim->m_ucAttributeName == kAttrNameCoords ||
                pAnim->m_ucAttributeName == kAttrNameValue)
            {
                pAnim->m_ucCalcMode   = kCalcModeDiscrete;
                pAnim->m_ucAccumulate = kAccumulateNone;
                pAnim->m_ucAdditive   = kAdditiveReplace;
            }
            // If the attribute doesn't have a meaningful notion of distance,
            // then we can't do paced animation. Therefore, if we specified
            // paced for one of these attributes, then we should default
            // back to linear animation. Note that we don't have to worry
            // about coords, since it was handled separately above.
            if (pAnim->m_ucCalcMode == kCalcModePaced &&
                (pAnim->m_ucAttributeName == kAttrNameZIndex ||
                 pAnim->m_ucAttributeName == kAttrNameSoundLevel))
            {
                pAnim->m_ucCalcMode = kCalcModeLinear;
            }
            // Check to make sure that accelerate and decelerate attributes
            // don't sum to more than 1.0. Spec says:
            //
            // The sum of accelerate and decelerate must not exceed 1.
            // If the individual values of the accelerate and decelerate
            // attributes are between 0 and 1 and the sum is greater
            // than 1, then both the accelerate and decelerate attributes
            // will be ignored and the timed element will behave as if
            // neither attribute was specified.
            if (pAnim->m_dAccelerate + pAnim->m_dDecelerate > 1.0)
            {
                // Reset them to their defaults, as
                // if they had not been specified.
                pAnim->m_dAccelerate = 0.0;
                pAnim->m_dDecelerate = 0.0;
            }
            // If the target element is not specified, then make
            // the parent element the target element
            if (!bTargetElementSpecified)
            {
                if (pAnim->m_pNode->m_pParent)
                {
                    pAnim->m_pTargetElementID = new CHXString(pAnim->m_pNode->m_pParent->m_id);
                    if (pAnim->m_pTargetElementID)
                    {
                        // Check to see if this is a real id
                        SMILNode* pNode = NULL;
                        if(!m_pIDMap->Lookup((const char*) *pAnim->m_pTargetElementID,
                                            (void*&)pNode))
                        {
                            // There's no element with this id in the map
                            retVal = HXR_FAIL;
                            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                            errHandler.ReportError(SMILErrorNonexistentID,
                                                   pName,
                                                   pAnim->m_pNode->m_ulTagStartLine);
                        }
                    }
                    else
                    {
                        retVal = HXR_OUTOFMEMORY;
                    }
                }
                else
                {
                    // XXXMEH - huh? no parent?
                    HX_ASSERT(FALSE);
                    retVal = HXR_FAIL;
                    CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                    errHandler.ReportError(SMILErrorGeneralError,
                                           pName,
                                           pAnim->m_pNode->m_ulTagStartLine);
                }
            }
            // If the attribute was not specified, then we have to
            // be <animateMotion>, since these implicitly target
            // attributes.
            if (SUCCEEDED(retVal) && !bAttrNameSpecified)
            {
                if (pAnim->m_pNode->m_tag == SMILAnimateMotion)
                {
                    // If we are <animateMotion>, then we know we are
                    // animating "left" and "top" of a region.
                    pAnim->m_ucAttributeName = kAttrNameLeftTop;
                }
                else
                {
                    // Uh-oh, no attribute name specified, but
                    // we are not <animateMotion>. This is an error.
                    retVal = HXR_FAIL;
                    CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                    errHandler.ReportError(SMILErrorRequiredAttributeMissing,
                                           "attributeName",
                                           pAnim->m_pNode->m_ulTagStartLine);
                }
            }
            if (SUCCEEDED(retVal))
            {
                // Now let's check to see that the animation element,
                // target element, the attribute name match up correctly
                SMILNode* pNode = NULL;
                if(m_pIDMap->Lookup((const char*) *pAnim->m_pTargetElementID,
                                    (void*&)pNode))
                {
                    // Set the target element tag
                    pAnim->m_eTargetElementTag = pNode->m_tag;
                    // Check the element/attribute combination
                    if (pAnim->m_pNode->m_tag == SMILAnimate ||
                        pAnim->m_pNode->m_tag == SMILSet)
                    {
                        if (pNode->m_tag == SMILRegion)
                        {
                            if (!(pAnim->m_ucAttributeName == kAttrNameSoundLevel ||
                                  pAnim->m_ucAttributeName == kAttrNameWidth      ||
                                  pAnim->m_ucAttributeName == kAttrNameHeight     ||
                                  pAnim->m_ucAttributeName == kAttrNameZIndex     ||
                                  pAnim->m_ucAttributeName == kAttrNameLeft       ||
                                  pAnim->m_ucAttributeName == kAttrNameRight      ||
                                  pAnim->m_ucAttributeName == kAttrNameTop        ||
                                  pAnim->m_ucAttributeName == kAttrNameBottom     ||
                                  pAnim->m_ucAttributeName == kAttrNameBackgroundColor))
                            {
                                retVal = HXR_FAIL;
                            }
                        }
                        else if (pNode->m_tag == SMILAnchor  ||
                                 pNode->m_tag == SMILArea)
                        {
                            if (pAnim->m_ucAttributeName != kAttrNameCoords)
                            {
                                retVal = HXR_FAIL;
                            }
                        }
                        else if (pNode->m_tag == SMILText       ||
                                 pNode->m_tag == SMILImg        ||
                                 pNode->m_tag == SMILAnimation  ||
                                 pNode->m_tag == SMILRef        ||
                                 pNode->m_tag == SMILAudio      ||
                                 pNode->m_tag == SMILVideo      ||
                                 pNode->m_tag == SMILTextstream)
                        {
                            if (!(pAnim->m_ucAttributeName == kAttrNameLeft            ||
                                  pAnim->m_ucAttributeName == kAttrNameRight           ||
                                  pAnim->m_ucAttributeName == kAttrNameTop             ||
                                  pAnim->m_ucAttributeName == kAttrNameBottom          ||
                                  pAnim->m_ucAttributeName == kAttrNameWidth           ||
                                  pAnim->m_ucAttributeName == kAttrNameHeight          ||
                                  pAnim->m_ucAttributeName == kAttrNameZIndex          ||
                                  pAnim->m_ucAttributeName == kAttrNameBackgroundColor ||
                                  pAnim->m_ucAttributeName == kAttrNameMediaOpacity    ||
                                  pAnim->m_ucAttributeName == kAttrNameBackgroundOpacity))
                            {
                                retVal = HXR_FAIL;
                            }
                        }
                        else if (pNode->m_tag == SMILBrush)
                        {
                            if (!(pAnim->m_ucAttributeName == kAttrNameLeft       ||
                                  pAnim->m_ucAttributeName == kAttrNameRight      ||
                                  pAnim->m_ucAttributeName == kAttrNameTop        ||
                                  pAnim->m_ucAttributeName == kAttrNameBottom     ||
                                  pAnim->m_ucAttributeName == kAttrNameWidth      ||
                                  pAnim->m_ucAttributeName == kAttrNameHeight     ||
                                  pAnim->m_ucAttributeName == kAttrNameZIndex     ||
                                  pAnim->m_ucAttributeName == kAttrNameColor      ||
                                  pAnim->m_ucAttributeName == kAttrNameBackgroundColor))
                            {
                                retVal = HXR_FAIL;
                            }
                        }
                        else if (pNode->m_tag == SMILRootLayout ||
                                 pNode->m_tag == SMILViewport)
                        {
                            if (!(pAnim->m_ucAttributeName == kAttrNameWidth ||
                                  pAnim->m_ucAttributeName == kAttrNameHeight))
                            {
                                retVal = HXR_FAIL;
                            }
                        }
                        else if (pNode->m_tag == SMILParam)
                        {
                            if (pAnim->m_ucAttributeName != kAttrNameValue)
                            {
                                retVal = HXR_FAIL;
                            }
                        }
                        else
                        {
                            retVal = HXR_FAIL;
                        }
                    }
                    else if (pAnim->m_pNode->m_tag == SMILAnimateMotion)
                    {
                        if (pNode->m_tag == SMILRegion     ||
                            pNode->m_tag == SMILText       ||
                            pNode->m_tag == SMILImg        ||
                            pNode->m_tag == SMILAnimation  ||
                            pNode->m_tag == SMILRef        ||
                            pNode->m_tag == SMILAudio      ||
                            pNode->m_tag == SMILVideo      ||
                            pNode->m_tag == SMILTextstream ||
                            pNode->m_tag == SMILBrush)
                        {
                            if (pAnim->m_ucAttributeName != kAttrNameLeftTop)
                            {
                                retVal = HXR_FAIL;
                            }
                        }
                        else
                        {
                            retVal = HXR_FAIL;
                        }
                    }
                    else if (pAnim->m_pNode->m_tag == SMILAnimateColor)
                    {
                        if (pNode->m_tag == SMILRegion    ||
                            pNode->m_tag == SMILText      ||
                            pNode->m_tag == SMILImg       ||
                            pNode->m_tag == SMILAnimation ||
                            pNode->m_tag == SMILRef       ||
                            pNode->m_tag == SMILAudio     ||
                            pNode->m_tag == SMILVideo     ||
                            pNode->m_tag == SMILTextstream)
                        {
                            if (pAnim->m_ucAttributeName != kAttrNameBackgroundColor)
                            {
                                retVal = HXR_FAIL;
                            }
                        }
                        else if (pNode->m_tag == SMILBrush)
                        {
                            if (pAnim->m_ucAttributeName != kAttrNameColor)
                            {
                                retVal = HXR_FAIL;
                            }
                        }
                        else
                        {
                            retVal = HXR_FAIL;
                        }
                    }
                }
                if (FAILED(retVal))
                {
                    // We either had an illegal target element or
                    // an illegal attribute name.
                    CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                    errHandler.ReportError(SMILErrorBadAttribute,
                                           "attributeName",
                                           pAnim->m_pNode->m_ulTagStartLine);
                }
            }
            if (SUCCEEDED(retVal))
            {
                // Did we have a simple dur specified?
                if (pAnim->m_ulSimpleDuration != ((UINT32) -1))
                {
                    // Yes the simple duration was specified.
                    //
                    // Compute active duration. First initialize
                    // it to the simple duration
                    pAnim->m_ulActiveDuration = pAnim->m_ulSimpleDuration;
                    // Did we have repeatDur="indefinite"?
                    if (pAnim->m_bRepeatDurIsIndefinite)
                    {
                        // Yes, we have repeatDur="indefinite"
                        //
                        // Do we have any repeatCount?
                        if (pAnim->m_dRepeatCount != 1.0)
                        {
                            // Yes, we have a repeatCount specified.
                            //
                            // Do we have autoReverse?
                            double dRC = 0.0;
                            if (pAnim->m_bAutoReverse)
                            {
                                dRC = (double) pAnim->m_dRepeatCount * pAnim->m_ulSimpleDuration * 2;
                            }
                            else
                            {
                                dRC = (double) pAnim->m_dRepeatCount * pAnim->m_ulSimpleDuration;
                            }
                            // This repeatCount-driven active duration will
                            // take precedence over the repeatDur="indefinite"
                            pAnim->m_ulActiveDuration          = (UINT32) floor(dRC + 0.5);
                            pAnim->m_bIndefiniteActiveDuration = FALSE;
                            // m_ulADNoSpeed is the active duration without taking
                            // the speed attribute into account, so we simply initialize
                            // it before we make the speed modification
                            pAnim->m_ulADNoSpeed = pAnim->m_ulActiveDuration;
                            // Now if speed is not 1.0, then we have to adjust
                            // the active duration by the absolute value of the speed
                            // Speed globally scales the active duration
                            if (pAnim->m_dSpeed != 1.0 &&
                                pAnim->m_dSpeed != 0.0)
                            {
                                double dSpeedAbs = fabs(pAnim->m_dSpeed);
                                double dNewAD    = (double) pAnim->m_ulActiveDuration / dSpeedAbs;
                                pAnim->m_ulActiveDuration = (UINT32) floor(dNewAD + 0.5);
                            }
                        }
                        else
                        {
                            // We don't have a repeatCount, so the active duration
                            // is indefinite
                            pAnim->m_bIndefiniteActiveDuration = TRUE;
                        }
                    }
                    else
                    {
                        // Do we repeat at all?
                        if (pAnim->m_dRepeatCount != 1.0 ||
                            pAnim->m_ulRepeatDur  != ((UINT32) -1))
                        {
                            // Yes we repeat. Compute how long we would
                            // repeat from repeatCount
                            UINT32 ulADRepeatCount = 0xFFFFFFFF;
                            if (pAnim->m_dRepeatCount != 1.0)
                            {
                                // Do we have autoReverse?
                                double dRC = 0.0;
                                if (pAnim->m_bAutoReverse)
                                {
                                    dRC = (double) pAnim->m_dRepeatCount * pAnim->m_ulSimpleDuration * 2;
                                }
                                else
                                {
                                    dRC = (double) pAnim->m_dRepeatCount * pAnim->m_ulSimpleDuration;
                                }
                                ulADRepeatCount = (UINT32) floor(dRC + 0.5);
                            }
                            // Get how long we would repeat from repeatDur
                            UINT32 ulADRepeatDur = 0xFFFFFFFF;
                            if (pAnim->m_ulRepeatDur != ((UINT32) -1))
                            {
                                ulADRepeatDur = pAnim->m_ulRepeatDur;
                            }
                            // Now compute the active duration - it will be the minimum
                            // of the simple duration, active duration from repeatCount,
                            // and active duration from repeatDur.
                            pAnim->m_ulActiveDuration = 0xFFFFFFFF;
                            if (ulADRepeatCount < pAnim->m_ulActiveDuration)
                            {
                                pAnim->m_ulActiveDuration = ulADRepeatCount;
                            }
                            if (ulADRepeatDur < pAnim->m_ulActiveDuration)
                            {
                                pAnim->m_ulActiveDuration = ulADRepeatDur;
                            }
                            // If active duration is still 0xFFFFFFFF (shouldn't happen), then
                            // the active duration is just the same as the
                            // simple duration
                            if (pAnim->m_ulActiveDuration == 0xFFFFFFFF)
                            {
                                pAnim->m_ulActiveDuration = pAnim->m_ulSimpleDuration;
                            }
                        }
                        else
                        {
                            // Do we have autoReverse?
                            if (pAnim->m_bAutoReverse)
                            {
                                pAnim->m_ulActiveDuration = pAnim->m_ulSimpleDuration * 2;
                            }
                        }
                        // m_ulADNoSpeed is the active duration without taking
                        // the speed attribute into account, so we simply initialize
                        // it before we make the speed modification
                        pAnim->m_ulADNoSpeed = pAnim->m_ulActiveDuration;
                        // Now if speed is not 1.0, then we have to adjust
                        // the active duration by the absolute value of the speed
                        // Speed globally scales the active duration
                        if (pAnim->m_dSpeed != 1.0 &&
                            pAnim->m_dSpeed != 0.0)
                        {
                            double dSpeedAbs = fabs(pAnim->m_dSpeed);
                            double dNewAD    = (double) pAnim->m_ulActiveDuration / dSpeedAbs;
                            pAnim->m_ulActiveDuration = (UINT32) floor(dNewAD + 0.5);
                        }
                    }
                }
                else
                {
                    // No simple duration was specified. Therefore, we have
                    // an indefinite simple duration.
                    pAnim->m_bIndefiniteSimpleDuration = TRUE;
                    // Do we have a non-indefinite repeatDur specified?
                    if (pAnim->m_ulRepeatDur != ((UINT32) -1))
                    {
                        // Yes, repeatDur was specified and it was NOT
                        // "indefinite". Therefore, our active duration
                        // is NOT indefinite.
                        pAnim->m_bIndefiniteActiveDuration = FALSE;
                        // Our active duration is the repeatDur
                        pAnim->m_ulActiveDuration          = pAnim->m_ulRepeatDur;
                    }
                    else
                    {
                        // Either repeatDur wasn't specified or it was
                        // specified and it was "indefinite". Either way,
                        // our active duration is going to be indefinite.
                        pAnim->m_bIndefiniteActiveDuration = TRUE;
                    }
                }
            }
            if (SUCCEEDED(retVal))
            {
                // First let's determine what kind of animation it is
                if (bValuesSpecified)
                {
                    // SPEC: If values is specified, from/by/to are ignored
                    pAnim->m_ucAnimationType = kAnimTypeValues;
                }
                else
                {
                    // No values specified, so let's look at the from/by/to
                    //
                    // SPEC: "The simpler from/to/by syntax provides for several
                    // variants. To use one of these variants, one of by or to must
                    // be specified; a from value is optional. It is not legal to
                    // specify both by and to attributes; if both are specified, only
                    // the to attribute will be used (the by will be ignored)."
                    if (bToSpecified)
                    {
                        if (bFromSpecified)
                        {
                            // This is a "from-to" animation
                            pAnim->m_ucAnimationType = kAnimTypeFromTo;
                        }
                        else
                        {
                            // This is a "to" animation
                            pAnim->m_ucAnimationType = kAnimTypeTo;
                            // Handle special cases for "to" animations -
                            // cumulative animations are not defined, so we always
                            // for accumulate="none"
                            pAnim->m_ucAccumulate = kAccumulateNone;
                        }
                    }
                    else if (bBySpecified)
                    {
                        if (bFromSpecified)
                        {
                            // This is a "from-by" animation
                            pAnim->m_ucAnimationType = kAnimTypeFromBy;
                        }
                        else
                        {
                            // This is a "by" animation
                            pAnim->m_ucAnimationType = kAnimTypeBy;
                            // SPEC: "A by animation with a by value vb is equivalent
                            // to the same animation with a values list with 2 values,
                            // 0 and vb, and additive="sum". Any other specification of
                            // the additive attribute in a by animation is ignored."
                            //
                            // So if the additive value is not sum, set it to sum
                            if (pAnim->m_ucAdditive != kAdditiveSum)
                            {
                                pAnim->m_ucAdditive = kAdditiveSum;
                            }
                        }
                    }
                    else
                    {
                        // XXXMEH - no "values", "to", or "by" specified - the
                        // animation should do nothing. For now, we fail. We won't
                        // throw an error, but we will just not do this
                        // animation. I'm just commenting out the error popup,
                        // cause I think we should still have it.
                        retVal = HXR_FAIL;
//                        CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
//                        errHandler.ReportError(SMILErrorRequiredAttributeMissing,
//                                               "values",
//                                               pAnim->m_pNode->m_ulTagStartLine);
                    }
                }

                if (SUCCEEDED(retVal))
                {
                    // Now we can parse the values of the values/from/to/by attributes
                    if (pAnim->m_ucAnimationType == kAnimTypeValues)
                    {
                        // Get the "values" attribute
                        IHXBuffer* pVal = NULL;
                        retVal           = pAnim->m_pNode->m_pValues->GetPropertyCString("values", pVal);
                        if (SUCCEEDED(retVal))
                        {
                            const char* pszVal = (const char*) pVal->GetBuffer();
                            // First count and separate the different values arguments
                            char*  pStr  = NULL;
                            char** ppStr = NULL;
                            retVal       = animCountValues(pszVal, pStr, pAnim->m_ulNumValues, ppStr);
                            if (SUCCEEDED(retVal))
                            {
                                // Allocate the array of CAttr*
                                pAnim->m_ppValue = new CAttr* [pAnim->m_ulNumValues];
                                if (pAnim->m_ppValue)
                                {
                                    // Zero out the memory
                                    memset((void*) pAnim->m_ppValue, 0,
                                           sizeof(CAttr*) * pAnim->m_ulNumValues);
                                    // Loop through and parse each argument
                                    for (UINT32 i = 0; i < pAnim->m_ulNumValues && SUCCEEDED(retVal); i++)
                                    {
                                        retVal = animParseValue(pAnim, ppStr[i], i);
                                    }
                                }
                                else
                                {
                                    pAnim->m_ulNumValues = 0;
                                    retVal = HXR_OUTOFMEMORY;
                                }
                            }
                            // Free the temporary strings
                            HX_VECTOR_DELETE(ppStr);
                            HX_VECTOR_DELETE(pStr);
                        }
                        HX_RELEASE(pVal);
                    }
                    else
                    {
                        // If we are not a values type, then there will
                        // always be two CAttr*
                        pAnim->m_ulNumValues = 2;
                        // Allocate the array of CAttr*
                        pAnim->m_ppValue = new CAttr* [pAnim->m_ulNumValues];
                        if (pAnim->m_ppValue)
                        {
                            // Zero out the memory
                            memset((void*) pAnim->m_ppValue, 0,
                                   sizeof(CAttr*) * pAnim->m_ulNumValues);
                            // Parse the from attribute
                            if (pAnim->m_ucAnimationType == kAnimTypeFromTo ||
                                pAnim->m_ucAnimationType == kAnimTypeFromBy)
                            {
                                IHXBuffer* pVal = NULL;
                                retVal           = pAnim->m_pNode->m_pValues->GetPropertyCString("from", pVal);
                                if (SUCCEEDED(retVal))
                                {
                                    const char* pszVal = (const char*) pVal->GetBuffer();
                                    retVal = animParseValue(pAnim, pszVal, 0);
                                }
                                HX_RELEASE(pVal);
                            }
                            // Parse the to attribute
                            if (SUCCEEDED(retVal) &&
                                (pAnim->m_ucAnimationType == kAnimTypeFromTo ||
                                 pAnim->m_ucAnimationType == kAnimTypeTo))
                            {
                                IHXBuffer* pVal = NULL;
                                retVal           = pAnim->m_pNode->m_pValues->GetPropertyCString("to", pVal);
                                if (SUCCEEDED(retVal))
                                {
                                    const char* pszVal = (const char*) pVal->GetBuffer();
                                    retVal = animParseValue(pAnim, pszVal, 1);
                                }
                                HX_RELEASE(pVal);
                            }
                            // Parse the by attribute
                            if (SUCCEEDED(retVal) &&
                                (pAnim->m_ucAnimationType == kAnimTypeFromBy ||
                                 pAnim->m_ucAnimationType == kAnimTypeBy))
                            {
                                IHXBuffer* pVal = NULL;
                                retVal           = pAnim->m_pNode->m_pValues->GetPropertyCString("by", pVal);
                                if (SUCCEEDED(retVal))
                                {
                                    const char* pszVal = (const char*) pVal->GetBuffer();
                                    retVal = animParseValue(pAnim, pszVal, 1);
                                }
                                HX_RELEASE(pVal);
                            }
                            // If this is either a "to" or "by" animation,
                            // then we need to fill in m_ppValue[0] with a
                            // zero-valued placeholder
                            if (pAnim->m_ucAnimationType == kAnimTypeTo ||
                                pAnim->m_ucAnimationType == kAnimTypeBy)
                            {
                                pAnim->m_ppValue[0] = new CAttr(pAnim->m_ucAttributeName);
                                if (!pAnim->m_ppValue[0])
                                {
                                    retVal = HXR_OUTOFMEMORY;
                                }
                            }
                        }
                        else
                        {
                            pAnim->m_ulNumValues = 0;
                            retVal = HXR_OUTOFMEMORY;
                        }
                    }
                }
                // Check if we are animating width
                // or height of a root-layout or topLayout
                if (SUCCEEDED(retVal) &&
                    (pAnim->m_eTargetElementTag == SMILRootLayout ||
                     pAnim->m_eTargetElementTag == SMILViewport))
                {
                    // Make sure that none of the attributes
                    // are percentages. It's illegal for a <root-layout>
                    // or topLayout to have a percentage value
                    // at any time.
                    for (UINT32 i = 0; i < pAnim->m_ulNumValues; i++)
                    {
                        if (pAnim->m_ppValue[i] &&
                            pAnim->m_ppValue[i]->GetCSS2Type(0) == CSS2TypePercentage)
                        {
                            retVal = HXR_FAIL;
                            break;
                        }
                    }
                }
#if defined(XXXMEH_SPLINE_ANIMATION)
                // Check any SplineAnimation parameters, if there are any
                if (SUCCEEDED(retVal))
                {
                    retVal = checkSplineAnimation(pAnim);
                }
#endif
            }
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

#if defined(XXXMEH_SPLINE_ANIMATION)

HX_RESULT CSmilParser::checkSplineAnimation(CSmilAnimateElement* pAnim)
{
    HX_RESULT retVal = HXR_OK;

    if (pAnim)
    {
        // Do we have a "path" attribute?
        if (pAnim->m_ppPathCmd && pAnim->m_ulNumPathCmds)
        {
            // First, convert all implicit path
            // commands to explicit path commands
            retVal = makeSVGPathExplicit(pAnim);
        }
        if (SUCCEEDED(retVal))
        {
            // Do we have any keyTimes?
            if (pAnim->m_ulNumKeyTimes && pAnim->m_pdKeyTime)
            {
                // Do we have from/to/values attribute or a path specification?
                if (pAnim->m_ulNumValues && pAnim->m_ppValue)
                {
                    // SPEC: If a list of keyTimes is specified, there
                    // must be exactly as many values in the keyTimes
                    // list as in the values list.
                    if (pAnim->m_ulNumValues != pAnim->m_ulNumKeyTimes)
                    {
                        retVal = HXR_FAIL;
                    }
                }
                else if (pAnim->m_ppPathCmd && pAnim->m_ulNumPathCmds)
                {
                }
            }
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT CSmilParser::makeSVGPathExplicit(CSmilAnimateElement* pAnim)
{
    HX_RESULT retVal = HXR_OK;

    if (pAnim)
    {
        if (pAnim->m_ppPathCmd && pAnim->m_ulNumPathCmds)
        {
            // Run through and count how many we would
            // have in the completely explicit array
            UINT32 i             = 0;
            UINT32 ulNumExplicit = 0;
            for (i = 0; i < pAnim->m_ulNumPathCmds; i++)
            {
                if (pAnim->m_ppPathCmd[i])
                {
                    PathCmdType eType       = pAnim->m_ppPathCmd[i]->m_eType;
                    UINT32      ulNumCoords = pAnim->m_ppPathCmd[i]->m_ulNumCoords;
                    switch (eType)
                    {
                        case PathCmdTypeMoveTo:
                        case PathCmdTypeLineTo:
                            {
                                ulNumExplicit += ulNumCoords / 2;
                            }
                            break;
                        case PathCmdTypeHorzLineTo:
                        case PathCmdTypeVertLineTo:
                            {
                                ulNumExplicit += ulNumCoords;
                            }
                            break;
                        case PathCmdTypeClosePath:
                            {
                                ++ulNumExplicit;
                            }
                            break;
                        case PathCmdTypeCubicBezierCurveTo:
                            {
                                ulNumExplicit += ulNumCoords / 6;
                            }
                            break;
                    }
                }
            }
            // Is the completely explicit number the same
            // as the current number? If so, we don't have
            // any work to do
            if (ulNumExplicit != pAnim->m_ulNumPathCmds)
            {
                // Allocate an array of PathCmd*'s
                PathCmd** ppPathCmd = new PathCmd* [ulNumExplicit];
                if (ppPathCmd)
                {
                    // NULL out the array
                    memset(ppPathCmd, 0, ulNumExplicit & sizeof(PathCmd*));
                    // Now run through the existing array. If the PathCmd
                    // has no implicit commands in it, then we just copy
                    // the pointer. If the PathCmd has implicit commands,
                    // then we need to create some number of PathCmd
                    // objects and then fill them in
                    //
                    // Initialize the explicit array index
                    UINT32 ulExplicitIndex = 0;
                    for (i = 0; i < pAnim->m_ulNumPathCmds; i++)
                    {
                        if (pAnim->m_ppPathCmd[i])
                        {
                            PathCmdType eType       = pAnim->m_ppPathCmd[i]->m_eType;
                            UINT32      ulNumCoords = pAnim->m_ppPathCmd[i]->m_ulNumCoords;
                            UINT32      ulNumCmds   = 0;
                            switch (eType)
                            {
                                case PathCmdTypeMoveTo:
                                    {
                                        ulNumCmds = ulNumCoords / 2;
                                        if (ulNumCmds == 1)
                                        {
                                            // Just copy over the pointer
                                            ppPathCmd[ulExplicitIndex++] = pAnim->m_ppPathCmd[i];
                                        }
                                        else
                                        {
                                            for (UINT32 j = 0; j < ulNumCmds && SUCCEEDED(retVal); j++)
                                            {
                                                // Create a PathCmd
                                                PathCmd* pNew = new PathCmd();
                                                if (pNew)
                                                {
                                                    // Copy the type and relative members.
                                                    // The first one should be a MoveTo and
                                                    // the rest should be LineTo's.
                                                    pNew->m_eType     = (j ? PathCmdTypeLineTo : PathCmdTypeMoveTo);
                                                    pNew->m_bRelative = pAnim->m_ppPathCmd[i]->m_bRelative;
                                                    // Allocate 2 doubles
                                                    pNew->m_ulNumCoords = 2;
                                                    pNew->m_pdCoord     = new double [2];
                                                    if (pNew->m_pdCoord)
                                                    {
                                                        // Get the j*2-th and j*2+1-th
                                                        // coordinates from the original
                                                        UINT32 k = j * 2;
                                                        pNew->m_pdCoord[0] = pAnim->m_ppPathCmd[i]->m_pdCoord[k];
                                                        pNew->m_pdCoord[1] = pAnim->m_ppPathCmd[i]->m_pdCoord[k+1];
                                                        // Add this to the new array
                                                        ppPathCmd[ulExplicitIndex++] = pNew;
                                                    }
                                                    else
                                                    {
                                                        retVal = HXR_OUTOFMEMORY;
                                                    }
                                                }
                                                else
                                                {
                                                    retVal = HXR_OUTOFMEMORY;
                                                }
                                            }
                                            // Now we can delete the old PathCmd
                                            HX_DELETE(pAnim->m_ppPathCmd[i]);
                                        }
                                    }
                                    break;
                                case PathCmdTypeLineTo:
                                    {
                                        ulNumCmds = ulNumCoords / 2;
                                        if (ulNumCmds == 1)
                                        {
                                            // Just copy over the pointer
                                            ppPathCmd[ulExplicitIndex++] = pAnim->m_ppPathCmd[i];
                                        }
                                        else
                                        {
                                            for (UINT32 j = 0; j < ulNumCmds && SUCCEEDED(retVal); j++)
                                            {
                                                // Create a PathCmd
                                                PathCmd* pNew = new PathCmd();
                                                if (pNew)
                                                {
                                                    // Copy the type and relative members
                                                    // from the original PathCmd
                                                    pNew->m_eType       = pAnim->m_ppPathCmd[i]->m_eType;
                                                    pNew->m_bRelative   = pAnim->m_ppPathCmd[i]->m_bRelative;
                                                    // Allocate 2 doubles
                                                    pNew->m_ulNumCoords = 2;
                                                    pNew->m_pdCoord     = new double [2];
                                                    if (pNew->m_pdCoord)
                                                    {
                                                        // Get the j*2-th and j*2+1-th
                                                        // coordinates from the original
                                                        UINT32 k = j * 2;
                                                        pNew->m_pdCoord[0] = pAnim->m_ppPathCmd[i]->m_pdCoord[k];
                                                        pNew->m_pdCoord[1] = pAnim->m_ppPathCmd[i]->m_pdCoord[k+1];
                                                        // Add this to the new array
                                                        ppPathCmd[ulExplicitIndex++] = pNew;
                                                    }
                                                    else
                                                    {
                                                        retVal = HXR_OUTOFMEMORY;
                                                    }
                                                }
                                                else
                                                {
                                                    retVal = HXR_OUTOFMEMORY;
                                                }
                                            }
                                            // Now we can delete the old PathCmd
                                            HX_DELETE(pAnim->m_ppPathCmd[i]);
                                        }
                                    }
                                    break;
                                case PathCmdTypeHorzLineTo:
                                case PathCmdTypeVertLineTo:
                                    {
                                        ulNumCmds = ulNumCoords;
                                        if (ulNumCmds == 1)
                                        {
                                            // Just copy over the pointer
                                            ppPathCmd[ulExplicitIndex++] = pAnim->m_ppPathCmd[i];
                                        }
                                        else
                                        {
                                            for (UINT32 j = 0; j < ulNumCmds && SUCCEEDED(retVal); j++)
                                            {
                                                // Create a PathCmd
                                                PathCmd* pNew = new PathCmd();
                                                if (pNew)
                                                {
                                                    // Copy the type and relative members
                                                    // from the original PathCmd
                                                    pNew->m_eType       = pAnim->m_ppPathCmd[i]->m_eType;
                                                    pNew->m_bRelative   = pAnim->m_ppPathCmd[i]->m_bRelative;
                                                    // Allocate 2 doubles
                                                    pNew->m_ulNumCoords = 1;
                                                    pNew->m_pdCoord     = new double [1];
                                                    if (pNew->m_pdCoord)
                                                    {
                                                        // Get the j-th coordinates
                                                        // from the original
                                                        pNew->m_pdCoord[0] = pAnim->m_ppPathCmd[i]->m_pdCoord[j];
                                                        // Add this to the new array
                                                        ppPathCmd[ulExplicitIndex++] = pNew;
                                                    }
                                                    else
                                                    {
                                                        retVal = HXR_OUTOFMEMORY;
                                                    }
                                                }
                                                else
                                                {
                                                    retVal = HXR_OUTOFMEMORY;
                                                }
                                            }
                                            // Now we can delete the old PathCmd
                                            HX_DELETE(pAnim->m_ppPathCmd[i]);
                                        }
                                    }
                                    break;
                                case PathCmdTypeClosePath:
                                    {
                                        // Just copy over the pointer
                                        ppPathCmd[ulExplicitIndex++] = pAnim->m_ppPathCmd[i];
                                    }
                                    break;
                                case PathCmdTypeCubicBezierCurveTo:
                                    {
                                        ulNumCmds = ulNumCoords / 6;
                                        if (ulNumCmds == 1)
                                        {
                                            // Just copy over the pointer
                                            ppPathCmd[ulExplicitIndex++] = pAnim->m_ppPathCmd[i];
                                        }
                                        else
                                        {
                                            for (UINT32 j = 0; j < ulNumCmds && SUCCEEDED(retVal); j++)
                                            {
                                                // Create a PathCmd
                                                PathCmd* pNew = new PathCmd();
                                                if (pNew)
                                                {
                                                    // Copy the type and relative members
                                                    // from the original PathCmd
                                                    pNew->m_eType       = pAnim->m_ppPathCmd[i]->m_eType;
                                                    pNew->m_bRelative   = pAnim->m_ppPathCmd[i]->m_bRelative;
                                                    // Allocate 2 doubles
                                                    pNew->m_ulNumCoords = 6;
                                                    pNew->m_pdCoord     = new double [6];
                                                    if (pNew->m_pdCoord)
                                                    {
                                                        // Get the coordinates from the original
                                                        UINT32 k = j * 6;
                                                        for (UINT32 q = 0; q < 6; q++)
                                                        {
                                                            pNew->m_pdCoord[q] = pAnim->m_ppPathCmd[i]->m_pdCoord[k+q];
                                                        }
                                                        // Add this to the new array
                                                        ppPathCmd[ulExplicitIndex++] = pNew;
                                                    }
                                                    else
                                                    {
                                                        retVal = HXR_OUTOFMEMORY;
                                                    }
                                                }
                                                else
                                                {
                                                    retVal = HXR_OUTOFMEMORY;
                                                }
                                            }
                                            // Now we can delete the old PathCmd
                                            HX_DELETE(pAnim->m_ppPathCmd[i]);
                                        }
                                    }
                                    break;
                            }
                        }
                    }
                    // Now we can replace the old PathCmd array
                    // with the new one
                    HX_VECTOR_DELETE(pAnim->m_ppPathCmd);
                    pAnim->m_ppPathCmd     = ppPathCmd;
                    pAnim->m_ulNumPathCmds = ulNumExplicit;
                }
                else
                {
                    retVal = HXR_OUTOFMEMORY;
                }
            }
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

#endif // #if defined(XXXMEH_SPLINE_ANIMATION)

HX_RESULT CSmilParser::animCountValues(const char* pszStr,
                                       REF(char*)  rpStr,
                                       REF(UINT32) rulNumValues,
                                       REF(char**) rppStr)
{
    HX_RESULT retVal = HXR_OK;

    // Set defaults
    rpStr        = NULL;
    rulNumValues = 0;
    rppStr       = NULL;
    // Allocate the string
    char* pStr = new char [strlen(pszStr) + 1];
    if (pStr)
    {
        // Copy the string
        strcpy(pStr, pszStr); /* Flawfinder: ignore */
        // Init the count
        UINT32 ulNumValues = 0;
        // Count the values
        char* pszToken = strtok(pStr, ";");
        while (pszToken)
        {
            ulNumValues++;
            pszToken = strtok(NULL, ";");
        }
        // Got to have at least one
        if (ulNumValues)
        {
            // Allocate an array of strings
            char** ppStr = new char* [ulNumValues];
            if (ppStr)
            {
                // Zero out the memory
                memset((void*) ppStr, 0, ulNumValues * sizeof(char*));
                // Recopy the string
                strcpy(pStr, pszStr); /* Flawfinder: ignore */
                // Run through again and find the points
                char*  pszToken = strtok(pStr, ";");
                UINT32 i        = 0;
                while (pszToken)
                {
                    ppStr[i++] = pszToken;
                    pszToken = strtok(NULL, ";");
                }
                // Assign the out parameters
                rpStr        = pStr;
                rulNumValues = ulNumValues;
                rppStr       = ppStr;
            }
            else
            {
                retVal = HXR_OUTOFMEMORY;
            }
            if (FAILED(retVal))
            {
                HX_VECTOR_DELETE(ppStr);
            }
        }
        else
        {
            retVal = HXR_FAIL;
        }
    }
    else
    {
        retVal = HXR_OUTOFMEMORY;
    }

    if (FAILED(retVal))
    {
        HX_VECTOR_DELETE(pStr);
    }

    return retVal;
}

HX_RESULT CSmilParser::animParseValue(CSmilAnimateElement* pAnim,
                                      const char*          pszVal,
                                      UINT32               i)
{
    HX_RESULT retVal = HXR_OK;

    if (pAnim && pszVal && i < pAnim->m_ulNumValues)
    {
        // Allocate a new CAttr
        CAttr* pAttr = new CAttr(pAnim->m_ucAttributeName, pszVal);
        if (pAttr)
        {
            retVal = pAttr->GetLastError();
            if (SUCCEEDED(retVal))
            {
                HX_DELETE(pAnim->m_ppValue[i]);
                pAnim->m_ppValue[i] = pAttr;
            }
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
        if (FAILED(retVal))
        {
            HX_DELETE(pAttr);
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

#endif /* #if defined(HELIX_FEATURE_SMIL2_ANIMATION) */

CSmilParamElement* CSmilParser::makeParamElement(SMILNode* pNode)
{
    CSmilParamElement* pRet   = NULL;
    HX_RESULT          retVal = HXR_OK;

    if (pNode)
    {
        pRet = new CSmilParamElement(pNode);
        if (pRet)
        {
            if (pNode->m_pValues)
            {
                const char* pszName = NULL;
                IHXBuffer* pValue  = NULL;
                HX_RESULT   rv      = pNode->m_pValues->GetFirstPropertyCString(pszName, pValue);
                while (SUCCEEDED(rv) && SUCCEEDED(retVal))
                {
                    // Get the string version of the value
                    const char* pszValue = (const char*) pValue->GetBuffer();
                    // Get the SMIL2Attribute
                    SMIL2Attribute eAttr = getSMIL2Attribute(pszName);
                    // Switch on attribute name
                    switch (eAttr)
                    {
                        case SMIL2AttrName:
                            {
                                HX_RELEASE(pRet->m_pName);
                                pRet->m_pName = pValue;
                                pRet->m_pName->AddRef();
                            }
                            break;
                        case SMIL2AttrValue:
                            {
                                HX_RELEASE(pRet->m_pValue);
                                pRet->m_pValue = pValue;
                                pRet->m_pValue->AddRef();
                            }
                            break;
                        default:
                            {
                                // Find out if this attribute is namespace-prefixed. Since
                                // we assume that this document has already been validated,
                                // then we don't have to throw an error if it's not - it's
                                // probably just an attribute we are not parsing above.
                                const char*     pszAttr = NULL;
                                CNamespaceInfo* pNSInfo = getNamespaceInfo(pszName, pszAttr);
                                if (pNSInfo)
                                {
                                    // This attribute IS namespace-prefixed.
                                    //
                                    // If this is the Param namespace, then
                                    // we should parse it for "delivery"
                                    if (pNSInfo->m_eNamespace == NamespaceParam ||
                                        pNSInfo->m_eNamespace == NamespaceAllSMIL2Extensions)
                                    {
                                        if(!strcmp(pszAttr, "delivery"))
                                        {
                                            if (!strcmp(pszValue, "server"))
                                            {
                                                pRet->m_eDelivery = DeliveryServer;
                                            }
                                            else if (!strcmp(pszValue, "client"))
                                            {
                                                pRet->m_eDelivery = DeliveryClient;
                                            }
                                            else
                                            {
                                                retVal = HXR_FAIL;
                                                CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                                                errHandler.ReportError(SMILErrorBadAttribute,
                                                                       pszName,
                                                                       pRet->m_pNode->m_ulTagStartLine);
                                            }
                                        }
                                    }
                                }
                            }
                    }
                    // Get the next attribute
                    HX_RELEASE(pValue);
                    rv = pNode->m_pValues->GetNextPropertyCString(pszName, pValue);
                }
            }
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    if (FAILED(retVal))
    {
        HX_DELETE(pRet);
    }

    return pRet;
}

CSmilBodyElement* CSmilParser::makeBodyElement(SMILNode* pNode)
{
    CSmilBodyElement* pRet   = NULL;
    HX_RESULT         retVal = HXR_OK;

    if (pNode)
    {
        pRet = new CSmilBodyElement(pNode);
        if (pRet)
        {
            if (pNode->m_pValues)
            {
                const char* pszName = NULL;
                IHXBuffer* pValue  = NULL;
                HX_RESULT   rv      = pNode->m_pValues->GetFirstPropertyCString(pszName, pValue);
                while (SUCCEEDED(rv) && SUCCEEDED(retVal))
                {
                    // Get the string version of the value
                    const char* pszValue = (const char*) pValue->GetBuffer();
                    // Get the SMIL2Attribute
                    SMIL2Attribute eAttr = getSMIL2Attribute(pszName);
                    // Switch on attribute name
                    switch (eAttr)
                    {
                        // We currently don't care about other
                        // attributes on <body>
                        default:
                            {
                                // Find out if this attribute is namespace-prefixed. Since
                                // we assume that this document has already been validated,
                                // then we don't have to throw an error if it's not - it's
                                // probably just an attribute we are not parsing above.
                                const char*     pszAttr = NULL;
                                CNamespaceInfo* pNSInfo = getNamespaceInfo(pszName, pszAttr);
                                if (pNSInfo)
                                {
                                    // This attribute IS namespace-prefixed.
                                    if (pNSInfo->m_eNamespace == NamespaceAccessErrorBehavior ||
                                        pNSInfo->m_eNamespace == NamespaceAllSMIL2Extensions)
                                    {
                                        if(!strcmp(pszAttr, "accessErrorBehavior"))
                                        {
                                            retVal = parseAccessErrorBehavior(pszValue, pRet->m_eAccessErrorBehavior);
                                            if (FAILED(retVal))
                                            {
                                                CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                                                errHandler.ReportError(SMILErrorBadAttribute,
                                                                       pszName,
                                                                       pRet->m_pNode->m_ulTagStartLine);
                                            }
                                        }
                                    }
                                }
                            }
                    }
                    // Get the next attribute
                    HX_RELEASE(pValue);
                    rv = pNode->m_pValues->GetNextPropertyCString(pszName, pValue);
                }
            }
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    if (FAILED(retVal))
    {
        HX_DELETE(pRet);
    }

    return pRet;
}

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

    while(lPos)
    {
      CSmilElement* pPkt = (CSmilElement*)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
CSmilParser::mapID(SMILNode* 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;
      CSmilSMILSyntaxErrorHandler 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
CSmilParser::mapChildrenIDs(SMILNodeList* 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)
    {
      SMILNode* pNode = (SMILNode*)(*i);
      rc = mapID(pNode, bOverWrite);

      HX_ASSERT(rc == HXR_OK);

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

    return rc;
}

HX_RESULT
CSmilParser::markRepeatReplica(SMILNodeList* 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)
    {
      SMILNode* pNode = (SMILNode*)(*i);
      pNode->m_repeatTag = repeatTag;

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

    return rc;
}

#if defined(HELIX_FEATURE_SMIL2_VALIDATION)

HX_RESULT CSmilParser::validateContentModel(UINT32 ulElement, SMILNodeList* pChildren)
{
    HX_RESULT retVal = HXR_OK;

    if (pChildren)
    {
        if (ulElement < NumSMIL2Elements)
        {
            // First just do a check to make sure that
            // there are no illegal elements.
            UINT32 ulNumLegalChildren  = 0;
            UINT32 ulNumPCChildren     = 0;
            UINT32 ulNumLayoutChildren = 0;
            UINT32 ulHeadIndex         = 0;
            UINT32 ulBodyIndex         = 0;
            LISTPOSITION pos = pChildren->GetHeadPosition();
            while (pos && SUCCEEDED(retVal))
            {
                SMILNode* pChild = (SMILNode*) pChildren->GetNext(pos);
                if (pChild &&
                    !pChild->m_bCloseNode &&
                    !pChild->m_bNamespacedElement)
                {
                    if (!m_pContentModelMap->IsSet(ulElement, pChild->m_eElement))
                    {
                        retVal = HXR_FAIL;
                        CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
                        errHandler.ReportError(SMILErrorUnexpectedTag,
                                               pChild->m_name,
                                               pChild->m_ulTagStartLine);
                    }
                    else
                    {
                        if (ulElement == SMIL2ElemExcl &&
                            pChild->m_eElement == SMIL2ElemPriorityClass)
                        {
                            ulNumPCChildren++;
                        }
                        else if (ulElement == SMIL2ElemSwitch &&
                                 pChild->m_eElement == SMIL2ElemLayout)
                        {
                            ulNumLayoutChildren++;
                        }
                        else if (ulElement == SMIL2ElemSmil)
                        {
                            if (pChild->m_eElement == SMIL2ElemHead)
                            {
                                ulHeadIndex = ulNumLegalChildren;
                            }
                            else if (pChild->m_eElement == SMIL2ElemBody)
                            {
                                ulBodyIndex = ulNumLegalChildren;
                            }
                        }
                        ulNumLegalChildren++;
                    }
                }
            }
            // Now if we succeeded, then there are a few elements with
            // very specific content models.
            if (SUCCEEDED(retVal))
            {
                switch (ulElement)
                {
                    case SMIL2ElemCustomAttributes:
                        {
                            // Must be at least 1 customTest element
                            if (!ulNumLegalChildren)
                            {
                                retVal = HXR_FAIL;
                            }
                        }
                        break;
                    case SMIL2ElemExcl:
                        {
                            // If there are ANY priority class children, then they
                            // have to be the ONLY children.
                            if (ulNumPCChildren > 0 &&
                                ulNumPCChildren < ulNumLegalChildren)
                            {
                                retVal = HXR_FAIL;
                            }
                        }
                        break;
                    case SMIL2ElemHead:
                        {
                            // XXXMEH - TODO: figure out what
                            // is meant by the content model of <head>.
                        }
                        break;
                    case SMIL2ElemSmil:
                        {
                            // Content model is (head?, body?), so if there's
                            // only 1 legal child, then we know we're correct.
                            // If there's two, then we have to make sure the
                            // first is a <head> and the second is a <body>
                            if (ulNumLegalChildren == 2 &&
                                ulBodyIndex <= ulHeadIndex)
                            {
                                retVal = HXR_FAIL;
                            }
                        }
                        break;
                    case SMIL2ElemSwitch:
                        {
                            // If there are ANY layout children, then they
                            // have to be the ONLY children.
                            if (ulNumLayoutChildren > 0 &&
                                ulNumLayoutChildren < ulNumLegalChildren)
                            {
                                retVal = HXR_FAIL;
                            }
                        }
                        break;
                }
            }
        }
        else
        {
            retVal = HXR_FAIL;
        }
    }

    return retVal;
}

HX_RESULT CSmilParser::validateNode(SMILNode* pNode)
{
    HX_RESULT retVal = HXR_OK;

    if (pNode &&
        !pNode->m_bCloseNode &&       // don't care about closing nodes
        !pNode->m_bNamespacedElement) // won't validate namespaced elements
    {
        // Initialize the IHXValues which will hold
        // any attributes which are different from
        // their normalized form
        IHXValues* pNormAttributes = NULL;
        // Initialize error info
        SMILErrorTag eError       = SMILErrorNone;
        BOOL         bChildFailed = FALSE;
        CHXString    cErrStr;
        // Check that all the attributes are legal attributes
        if (pNode->m_pValues)
        {
            const char* pszName = NULL;
            IHXBuffer* pValue  = NULL;
            HX_RESULT   rv      = pNode->m_pValues->GetFirstPropertyCString(pszName, pValue);
            while (SUCCEEDED(rv) && SUCCEEDED(retVal))
            {
                // Is the attribute name valid?
                BOOL               bNSAttr     = FALSE;
                BOOL               bUnknownNS  = FALSE;
                SupportedNamespace eNS         = NamespaceNotImplemented;
                UINT32             ulAttribute = 0;
                void* pVoid        = NULL;
                if (m_pAttributeMap->Lookup(pszName, pVoid))
                {
                    // Get the attribute index
                    ulAttribute = (UINT32) pVoid;
                }
                else
                {
                    // We didn't find the attribute in the SMIL 2 namespace,
                    // so let's see if the attribute has a valid namespace prefix
                    const char*     pszAttr = NULL;
                    CNamespaceInfo* pNSInfo = getNamespaceInfo(pszName, pszAttr);
                    if (pNSInfo)
                    {
                        // Set the flag saying this is a namespaced attribute
                        bNSAttr = TRUE;
                        // Yes, this attribute has a valid namespace prefix
                        // But is this a namespace we support?
                        if (pNSInfo->m_eNamespace != NamespaceNotImplemented)
                        {
                            // Set the namespace to use for later
                            eNS = pNSInfo->m_eNamespace;
                            // Look up the attribute in our
                            // extension namespace map
                            if (m_pExtAttributeMap->Lookup(pszAttr, pVoid))
                            {
                                // Get the attribute index
                                ulAttribute = (UINT32) pVoid;
                            }
                            else
                            {
                                // We don't have this attribute in our
                                // namespace map
                                retVal  = HXR_FAIL;
                                eError  = SMILErrorUnrecognizedAttribute;
                                cErrStr = pszName;
                            }
                        }
                        else
                        {
                            // Valid namespace, but not one we implement
                            bUnknownNS = TRUE;
                        }
                    }
                    else
                    {
                        // No this attribute doesn't have a namespace
                        // prefix that has been declared.
                        //
                        // Is this the <smil> element and is this an xmlns:foo attribute?
                        // If so, then let it go by since we have already processed
                        // the namespaces
                        if (pNode->m_eElement != SMIL2ElemSmil ||
                            strncmp(pszName, "xmlns:", 6) != 0)
                        {
                            // This is just an error
                            retVal  = HXR_FAIL;
                            eError  = SMILErrorUnrecognizedAttribute;
                            cErrStr = pszName;
                        }
                        else
                        {
                            // We don't want to do validation on xmlns:xx
                            // attributes, so treat this as an unknown NS
                            bUnknownNS = TRUE;
                        }
                    }
                }
                if (SUCCEEDED(retVal))
                {
                    // Did we recognize the namespace? If we didn't
                    // implement the namespace, then we don't need to
                    // do anything further
                    if (!bUnknownNS)
                    {
                        // If this was a namespaced attribute, then
                        // check the namespace it came from
                        if (bNSAttr)
                        {
                            retVal = checkExtensionAttributeNamespace((SMIL2Attribute) ulAttribute, eNS);
                            if (FAILED(retVal))
                            {
                                eError  = SMILErrorUnrecognizedAttribute;
                                cErrStr = pszName;
                            }
                        }
                        if (SUCCEEDED(retVal))
                        {
                            // Check if the attribute is legal for this element
                            if (m_pLegalAttrMap->IsSet(pNode->m_eElement, ulAttribute))
                            {
                                // Legal attribute, now validate the attribute
                                const char* pszOrigAttr = (const char*) pValue->GetBuffer();
                                char*       pszNormAttr = NULL;
                                retVal = validateAttribute(pNode->m_eElement, ulAttribute,
                                                           pszOrigAttr, pszNormAttr);
                                if (SUCCEEDED(retVal))
                                {
                                    // We validated correctly.
                                    //
                                    // Now check if the normalized attribute
                                    // was different from the original attribute.
                                    if (strcmp(pszOrigAttr, pszNormAttr))
                                    {
                                        if (!pNormAttributes)
                                        {
                                            m_pClassFactory->CreateInstance(CLSID_IHXValues,
                                                                            (void**) &pNormAttributes);
                                        }
                                        addStringProperty(pNormAttributes,
                                                          m_pContext,
                                                          pszName,
                                                          pszNormAttr);
                                    }
                                }
                                else
                                {
                                    // Attribute doesn't validate
                                    eError  = SMILErrorBadAttribute;
                                    cErrStr = pszName;
                                }
                                HX_VECTOR_DELETE(pszNormAttr);
                            }
                            else
                            {
                                // Nope, this attribute is not legal on this element
                                retVal  = HXR_FAIL;
                                eError  = SMILErrorUnrecognizedAttribute;
                                cErrStr = pszName;
                            }
                        }
                    }
                }
                // Get the next attribute
                HX_RELEASE(pValue);
                rv = pNode->m_pValues->GetNextPropertyCString(pszName, pValue);
            }
        }
        if (SUCCEEDED(retVal))
        {
            // Now check if all REQUIRED attributes are present
            // If there is an entry for our element, then we have
            // required attributes to check. If not, then this
            // element has no required attributes
            if (m_ppReqAttrList &&
                m_ppReqAttrList[pNode->m_eElement])
            {
                // Get the list
                CHXSimpleList* pList = m_ppReqAttrList[pNode->m_eElement];
                // This element does have required attributes - check them
                LISTPOSITION pos = pList->GetHeadPosition();
                while (pos && SUCCEEDED(retVal))
                {
                    UINT32 ulAttr = (UINT32) pList->GetNext(pos);
                    if (ulAttr < NumSMIL2Attributes)
                    {
                        // Get the attribute string
                        const char* pszStr = g_AttributeTable[ulAttr].m_pszString;
                        // Does this element have any attributes?
                        if (pNode->m_pValues)
                        {
                            // Check if this attribute is present
                            IHXBuffer* pAttrBuf = NULL;
                            retVal = pNode->m_pValues->GetPropertyCString(pszStr, pAttrBuf);
                            HX_RELEASE(pAttrBuf);
                        }
                        else
                        {
                            // No attributes, so we KNOW it doesn't have
                            // this required attribute
                            retVal  = HXR_FAIL;
                        }
                        if (FAILED(retVal))
                        {
                            cErrStr = pszStr;
                        }
                    }
                }
                if (FAILED(retVal))
                {
                    eError = SMILErrorRequiredAttributeMissing;
                }
            }
            if (SUCCEEDED(retVal))
            {
                // Validate the content according to the DTD content model
                retVal = validateContentModel(pNode->m_eElement, pNode->m_pNodeList);
                if (SUCCEEDED(retVal))
                {
                    // Now loop through each of the children and
                    // validate each of them. First, do we have any children?
                    if (pNode->m_pNodeList)
                    {
                        LISTPOSITION pos = pNode->m_pNodeList->GetHeadPosition();
                        while (pos && SUCCEEDED(retVal))
                        {
                            SMILNode* pChildNode = (SMILNode*) pNode->m_pNodeList->GetNext(pos);
                            if (pChildNode)
                            {
                                retVal = validateNode(pChildNode);
                                if (FAILED(retVal))
                                {
                                    bChildFailed = TRUE;
                                }
                            }
                        }
                    }
                }
            }
        }
        // Now check if we need to overwrite some of
        // the original attributes with their normalized forms.
        if (SUCCEEDED(retVal) && pNormAttributes)
        {
            // We need to copy all the strings for
            // which the normalized attribute value
            // was different than the original attribute
            // value. Note that this will replace the
            // original attribute value.
            const char* pszName    = NULL;
            IHXBuffer* pNormValue = NULL;
            HX_RESULT rv = pNormAttributes->GetFirstPropertyCString(pszName, pNormValue);
            while (SUCCEEDED(rv))
            {
                // Sanity check to make sure the attribute already exists
                // in the current attributes IHXValues
                IHXBuffer* pOrigValue = NULL;
                pNode->m_pValues->GetPropertyCString(pszName, pOrigValue);
                if (pOrigValue)
                {
                    pNode->m_pValues->SetPropertyCString(pszName, pNormValue);
                }
                HX_RELEASE(pOrigValue);
                // Get the next normalized attribute
                HX_RELEASE(pNormValue);
                rv = pNormAttributes->GetNextPropertyCString(pszName, pNormValue);
            }
        }
        // Now report any errors, but not if it
        // was just that the child of this node failed, because
        // presumably that node threw an error when it
        // was validated.
        if (FAILED(retVal) && !bChildFailed)
        {
            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
            errHandler.ReportError(eError, cErrStr, pNode->m_ulTagStartLine);
        }
        // Release the normalized attributes
        HX_RELEASE(pNormAttributes);
    }

    return retVal;
}

HX_RESULT CSmilParser::validateCDATA(const char* pszStr) const
{
    HX_RESULT retVal = HXR_OK;

    if (pszStr)
    {
        char*  pCh   = (char*) pszStr;
        while (*pCh)
        {
          // /Removing the ++ from "...= *pCh++;" fixes bug where
          // HXGetNextChar(pCh), below, otherwise will go on into
          // whatever is past the end of pCh:
            UCHAR c = *pCh;
            if (c <  0x20 &&
                c != 0x09 &&
                c != 0x0A &&
                c != 0x0D)
            {
                retVal = HXR_FAIL;
                break;
            }
            pCh = HXGetNextChar(pCh);
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT CSmilParser::validateIDREF(const char* pszStr) const
{
    HX_RESULT retVal = HXR_OK;

    if (pszStr)
    {
        UINT32 ulLen = (UINT32) strlen(pszStr);
        char*  pCh   = (char*) pszStr;
        // Check first character
        char c = *pCh++;
        ulLen--;
        if (isXMLLetter(c) ||
            c == '_'       ||
            c == ':')
        {
            // Check rest of letters
            while (ulLen--)
            {
                char c = *pCh++;
                if (!isXMLNameChar(c))
                {
                    retVal = HXR_FAIL;
                    break;
                }
            }
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT CSmilParser::validateNMTOKEN(const char* pszStr) const
{
    HX_RESULT retVal = HXR_OK;

    if (pszStr)
    {
        UINT32 ulLen = (UINT32) strlen(pszStr);
        if (ulLen >= 1)
        {
            char* pCh = (char*) pszStr;
            while (ulLen--)
            {
                char c = *pCh++;
                if (!isXMLNameChar(c))
                {
                    retVal = HXR_FAIL;
                    break;
                }
            }
        }
        else
        {
            retVal = HXR_FAIL;
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT CSmilParser::validateEnumerated(UINT32 ulElem, UINT32 ulAttr, const char* pszStr)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pszStr &&
        ulAttr < NumSMIL2Attributes &&
        m_ppEnumAttrMap &&
        m_ppEnumAttrMap[ulAttr])
    {
        void* pVoid = NULL;
        if (m_ppEnumAttrMap[ulAttr]->Lookup(pszStr, pVoid))
        {
            retVal = HXR_OK;
        }
        else
        {
            // If this is the "type" or "subtype" attribute
            // on the <transition> element, then we also
            // have to check for namespaced attribute values
            if (ulElem == SMIL2ElemTransition &&
                (ulAttr == SMIL2AttrType ||
                 ulAttr == SMIL2AttrSubtype))
            {
                // XXXMEH - the spec says that we should not
                // error out on bad transition types or subtypes.
                // Previously, we required that a transition
                // type or subtype be namespaced in order not
                // throw an error. Now we don't require that.
//                if (isNamespacePrefixed(pszStr))
//                {
                    retVal = HXR_OK;
//                }
            }
        }
    }

    return retVal;
}

BOOL CSmilParser::isNamespacePrefixed(const char* pszStr)
{
    BOOL bRet = FALSE;

    if (pszStr && m_pValNSList)
    {
        LISTPOSITION pos = m_pValNSList->GetHeadPosition();
        while (pos)
        {
            CNamespaceInfo* pInfo = (CNamespaceInfo*) m_pValNSList->GetNext(pos);
            if (pInfo)
            {
                UINT32 ulPrefixLen = (UINT32) strlen(pInfo->m_pszPrefix);
                if (!strncmp(pszStr, pInfo->m_pszPrefix, ulPrefixLen))
                {
                    // We match this namespace prefix
                    bRet = TRUE;
                    break;
                }
            }
        }
    }

    return bRet;
}

HX_RESULT CSmilParser::normalizeAttribute(const char* pszStr,
                                          BOOL        bIsCDATA,
                                          REF(char*)  rpszNormal)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pszStr)
    {
        UINT32 ulLen    = (UINT32) strlen(pszStr);
        char*  pNormal1 = new char [ulLen + 1];
        if (pNormal1)
        {
            memset(pNormal1, 0, ulLen + 1);
            // Do first part of XML normalization algorithm
            char* pSrc = (char*) pszStr;
            char* pDst = pNormal1;
            while (ulLen--)
            {
                char c = *pSrc++;
                if (c == 0x20 ||
                    c == 0x0D ||
                    c == 0x0A ||
                    c == 0x09)
                {
                    *pDst++ = (char) 0x20;
                }
                else
                {
                    *pDst++ = c;
                }
            }
            // If the attribute is not CDATA, then we
            // need to remove leading and trailing spaces
            // as well as remove sequences of spaces.
            if (bIsCDATA)
            {
                HX_VECTOR_DELETE(rpszNormal);
                rpszNormal = pNormal1;
                retVal     = HXR_OK;
            }
            else
            {
                ulLen = (UINT32) strlen(pNormal1);
                char* pNormal2 = new char [ulLen + 1];
                if (pNormal2)
                {
                    memset(pNormal2, 0, ulLen + 1);
                    BOOL  bFirst   = TRUE;
                    char* pszToken = strtok(pNormal1, " ");
                    while (pszToken)
                    {
                        if (!bFirst)
                        {
                            strcat(pNormal2, " "); /* Flawfinder: ignore */
                        }
                        strcat(pNormal2, pszToken); /* Flawfinder: ignore */
                        if (bFirst) bFirst = FALSE;
                        pszToken = strtok(NULL, " ");
                    }
                    // Copy to the out parameter
                    HX_VECTOR_DELETE(pNormal1);
                    HX_VECTOR_DELETE(rpszNormal);
                    rpszNormal = pNormal2;
                    retVal     = HXR_OK;
                }
            }
        }
    }

    return retVal;
}

HX_RESULT CSmilParser::validateAttribute(UINT32      ulElement,
                                         UINT32      ulAttrib,
                                         const char* pszStr,
                                         REF(char*)  rpszNormStr)
{
    HX_RESULT retVal = HXR_OK;

    if (ulElement < NumSMIL2Elements &&
        ulAttrib  < NumSMIL2Attributes &&
        pszStr)
    {
        // Look up the attribute type
        XMLAttributeType eType = m_pAttrType[ulAttrib];
        // There are two different elements with
        // two different "type" attributes, so distinguish
        // in between them.
        if (ulAttrib == SMIL2AttrType)
        {
            if (ulElement == SMIL2ElemTransition)
            {
                eType = XMLAttrTypeEnumerated;
            }
            else
            {
                eType = XMLAttrTypeCDATA;
            }
        }
        // Normalize the attribute
        HX_VECTOR_DELETE(rpszNormStr);
        retVal = normalizeAttribute(pszStr, (eType == XMLAttrTypeCDATA), rpszNormStr);
        if (SUCCEEDED(retVal))
        {
            switch (eType)
            {
                case XMLAttrTypeCDATA:
                    retVal = validateCDATA(rpszNormStr);
                    break;
                case XMLAttrTypeEnumerated:
                    retVal = validateEnumerated(ulElement, ulAttrib, rpszNormStr);
                    break;
                case XMLAttrTypeID:
                case XMLAttrTypeIDREF:
                    retVal = validateIDREF(rpszNormStr);
                    break;
                case XMLAttrTypeNMTOKEN:
                    retVal = validateNMTOKEN(rpszNormStr);
                    break;
                default:
                    HX_ASSERT(FALSE);
                    break;
            }
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT CSmilParser::validateElementName(SMILNode* pNode)
{
    HX_RESULT retVal = HXR_OK;

    if (pNode && !pNode->m_bCloseNode)
    {
        // Is the element name legal?
        void*              pVoid        = NULL;
        UINT32             ulElement    = 0;
        BOOL               bUnknownNS   = FALSE;
        BOOL               bChildFailed = FALSE;
        SupportedNamespace eNS          = NamespaceNotImplemented;
        if (m_pElementMap->Lookup(pNode->m_name, pVoid))
        {
            // We found the element in our SMIL 2 namespace
            //
            // Get the element index
            ulElement = (UINT32) pVoid;
        }
        else
        {
            // We didn't find the element in our SMIL 2 namespace
            //
            // This element name didn't look up in the legal list, so
            // we should first check if it is a namespaced element
            const char*     pszElem = NULL;
            CNamespaceInfo* pNSInfo = getNamespaceInfo(pNode->m_name, pszElem);
            if (pNSInfo)
            {
                // Yes, this is a namespaced element, so set
                // the flag so we can remember this later
                pNode->m_bNamespacedElement = TRUE;
                // Is this a recognized RN namespace?
                if (pNSInfo->m_eNamespace != NamespaceNotImplemented)
                {
                    // Save the namespace for later
                    eNS = pNSInfo->m_eNamespace;
                    // Now that we know we are in a namespace that
                    // we have implemented, then let's look up
                    // the element name in our map
                    if (m_pExtElementMap->Lookup(pszElem, pVoid))
                    {
                        // Get the element index
                        ulElement = (UINT32) pVoid;
                    }
                    else
                    {
                        // This element claims to be in the above
                        // extension namespaces, but is not an extension
                        // element we recognize
                        retVal = HXR_FAIL;
                    }
                }
                else
                {
                    // Properly namespaced, but not a namespace
                    // we recognize. So ignore it.
                    bUnknownNS = TRUE;
                }
            }
        }
        // At this point if we have retVal == HXR_FAIL, then
        // we have an illegal element name. If we have retVal == HXR_OK
        // and bUnknownNS == TRUE, then this element is properly namespaced
        // with an namespace we don't recognize.
        if (SUCCEEDED(retVal) && !bUnknownNS)
        {
            // Do a sanity check on the index
            if (ulElement < NumSMIL2Elements)
            {
                // Assign the element enum
                pNode->m_eElement = g_ElementEnumMap[ulElement];
                // If we are an rn:param, then we treat this
                // as a normal <param>
                if (pNode->m_eElement == SMIL2ElemRNParam)
                {
                    pNode->m_tag = SMILParam;
                }
                // If we are an extension element, then check
                // our namespace
                if (pNode->m_bNamespacedElement)
                {
                    retVal = checkExtensionElementNamespace(pNode->m_eElement,
                                                            eNS);
                }
                // Recursively check our children
                if (SUCCEEDED(retVal))
                {
                    if (pNode->m_pNodeList)
                    {
                        LISTPOSITION pos = pNode->m_pNodeList->GetHeadPosition();
                        while (pos && SUCCEEDED(retVal))
                        {
                            SMILNode* pChildNode = (SMILNode*) pNode->m_pNodeList->GetNext(pos);
                            retVal = validateElementName(pChildNode);
                            if (FAILED(retVal))
                            {
                                bChildFailed = TRUE;
                            }
                        }
                    }
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
        // If we failed somewhere along the way, then throw an error.
        // However, since error returns bubble up, then we don't
        // need to throw an error on this element if its child failed.
        if (FAILED(retVal) && !bChildFailed)
        {
            CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
            errHandler.ReportError(SMILErrorUnrecognizedTag,
                                   pNode->m_name,
                                   pNode->m_ulTagStartLine);
        }
    }

    return retVal;
}

HX_RESULT CSmilParser::setupValidationNamespaces(SMILNode* pNode)
{
    HX_RESULT retVal = HXR_OK;

    if (pNode &&
        pNode->m_tag == SMILSmil &&
        pNode->m_pValues)
    {
        // Clear out any existing namespaces
        deleteValidationNamespaceList();
        // Loop through the attributes, looking for xmlns:foo attributes
        const char* pszName = NULL;
        IHXBuffer* pValue  = NULL;
        HX_RESULT   rv      = pNode->m_pValues->GetFirstPropertyCString(pszName, pValue);
        while (SUCCEEDED(rv) && SUCCEEDED(retVal))
        {
            // Is this an xmlns:foo attribute?
            if (strncmp(pszName, "xmlns:", 6) == 0)
            {
                // This is a namespace declaration, so we need to
                // record the namespace abbreviation
                //
                // If we don't already have a list of namespace
                // declarations, then create one
                if (!m_pValNSList)
                {
                    m_pValNSList = new CHXSimpleList();
                }
                if (m_pValNSList)
                {
                    // Create a CNamespaceInfo object
                    CNamespaceInfo* pInfo = new CNamespaceInfo();
                    if (pInfo)
                    {
                        // Make a copy of the namespace abbreviation
                        const char* pszNS  = pszName + 6;
                        UINT32      ulLen  = (UINT32) strlen(pszNS);
                        pInfo->m_pszPrefix = new char [ulLen + 2];
                        if (pInfo->m_pszPrefix)
                        {
                            // Copy the abbreviation
                            strcpy(pInfo->m_pszPrefix, pszNS); /* Flawfinder: ignore */
                            // Concatenate the ":" character to the prefix
                            strcat(pInfo->m_pszPrefix, ":"); /* Flawfinder: ignore */
                            // Now copy the URL
                            const char* pszURL = (const char*) pValue->GetBuffer();
                            pInfo->m_pszURL    = new char [strlen(pszURL) + 1];
                            if (pInfo->m_pszURL)
                            {
                                // Copy into the member
                                strcpy(pInfo->m_pszURL, pszURL); /* Flawfinder: ignore */
                                // Now map this string to a supported namespace
                                if (m_pNamespaceMap)
                                {
                                    void* pVoid = NULL;
                                    if (m_pNamespaceMap->Lookup(pszURL, pVoid))
                                    {
                                        pInfo->m_eNamespace   = (SupportedNamespace) (UINT32) pVoid;
                                        pInfo->m_bImplemented = TRUE;
                                    }
                                }
                                // Append the namespace info object to the list
                                m_pValNSList->AddTail((void*) pInfo);
                            }
                        }
                    }
                }
            }
            // Get the next attribute
            HX_RELEASE(pValue);
            rv = pNode->m_pValues->GetNextPropertyCString(pszName, pValue);
        }
    }

    return retVal;
}

HX_RESULT CSmilParser::validateAgainstDTD()
{
    HX_RESULT retVal = HXR_FAIL;

    if (m_pElementMap && m_pAttributeMap &&
        m_pExtElementMap && m_pExtAttributeMap &&
        m_pLegalAttrMap && m_pContentModelMap)
    {
        SMILNode* pSmilNode = findFirstNode(SMILSmil);
        if (pSmilNode)
        {
            retVal = setupValidationNamespaces(pSmilNode);
            if (SUCCEEDED(retVal))
            {
                retVal = validateElementName(pSmilNode);
                if (SUCCEEDED(retVal))
                {
                    retVal = validateNode(pSmilNode);
                }
            }
        }
    }

    return retVal;
}

HX_RESULT CSmilParser::checkExtensionElementNamespace(SMIL2Element eElem,
                                                      SupportedNamespace eNS)
{
    HX_RESULT retVal = HXR_FAIL;

    switch (eElem)
    {
        case SMIL2ElemRNParam:
            {
                if (eNS == NamespaceAllSMIL2Extensions ||
                    eNS == NamespaceParam)
                {
                    retVal = HXR_OK;
                }
            }
            break;
        case SMIL2ElemRNRendererList:
        case SMIL2ElemRNRenderer:
            {
                if (eNS == NamespaceAllSMIL2Extensions ||
                    eNS == NamespaceRendererList)
                {
                    retVal = HXR_OK;
                }
            }
            break;
    }

    return retVal;
}

HX_RESULT CSmilParser::checkExtensionAttributeNamespace(SMIL2Attribute eAttr,
                                                        SupportedNamespace eNS)
{
    HX_RESULT retVal = HXR_FAIL;

    switch (eAttr)
    {
        case SMIL2AttrRNBackgroundOpacity:
        case SMIL2AttrRNChromaKey:
        case SMIL2AttrRNChromaKeyOpacity:
        case SMIL2AttrRNChromaKeyTolerance:
        case SMIL2AttrRNMediaOpacity:
        case SMIL2AttrRNOpacity:
            {
                if (eNS == NamespaceAllSMIL2Extensions ||
                    eNS == NamespaceAlphaControl)
                {
                    retVal = HXR_OK;
                }
            }
            break;
        case SMIL2AttrRNDelivery:
            {
                if (eNS == NamespaceAllSMIL2Extensions ||
                    eNS == NamespaceParam)
                {
                    retVal = HXR_OK;
                }
            }
            break;
        case SMIL2AttrRNHandledBy:
            {
                if (eNS == NamespaceAllSMIL2Extensions ||
                    eNS == NamespaceHandledBy)
                {
                    retVal = HXR_OK;
                }
            }
            break;
        case SMIL2AttrRNSendTo:
        case SMIL2AttrRNContextWindow:
            {
                if (eNS == NamespaceAllSMIL2Extensions ||
                    eNS == NamespaceSendTo)
                {
                    retVal = HXR_OK;
                }
            }
            break;
        case SMIL2AttrRNSystemComponent:
            {
                if (eNS == NamespaceSystemComponent)
                {
                    retVal = HXR_OK;
                }
            }
            break;
        case SMIL2AttrRNResizeBehavior:
            {
                if (eNS == NamespaceAllSMIL2Extensions ||
                    eNS == NamespaceSizeControl)
                {
                    retVal = HXR_OK;
                }
            }
            break;
        case XMLEventsAttrHandler:
            {
                if (eNS == NamespaceXMLEvents)
                {
                    retVal = HXR_OK;
                }
            }
            break;
        case SMIL2AttrRNAccessErrorBehavior:
            {
                if (eNS == NamespaceAllSMIL2Extensions ||
                    eNS == NamespaceAccessErrorBehavior)
                {
                    retVal = HXR_OK;
                }
            }
            break;
    }

    return retVal;
}

#endif /* #if defined(HELIX_FEATURE_SMIL2_VALIDATION) */

CNamespaceInfo* CSmilParser::getNamespaceInfo(const char*      pszStr,
                                              REF(const char*) rpszAttr)
{
    CNamespaceInfo* pRet = NULL;

    if (pszStr && m_pValNSList)
    {
        LISTPOSITION pos = m_pValNSList->GetHeadPosition();
        while (pos)
        {
            CNamespaceInfo* pInfo = (CNamespaceInfo*) m_pValNSList->GetNext(pos);
            if (pInfo)
            {
                UINT32 ulPrefixLen = (UINT32) strlen(pInfo->m_pszPrefix);
                if (!strncmp(pszStr, pInfo->m_pszPrefix, ulPrefixLen))
                {
                    pRet     = pInfo;
                    rpszAttr = pszStr + ulPrefixLen;
                    break;
                }
            }
        }
    }

    return pRet;
}

HX_RESULT
CSmilParser::createElements()
{
    HX_RESULT rc = HXR_OK;

    SMILNode* pSmilNode = findFirstNode(SMILSmil);
    if(!pSmilNode)  // dangerwillrobinson!!!
    {
      rc = HXR_FAIL;
      CSmilSMILSyntaxErrorHandler errHandler(m_pContext);
      errHandler.ReportError(SMILErrorNotSMIL, NULL, 0);
      return rc;
    }

    rc = addToNamespaceScope(pSmilNode);
    if (SUCCEEDED(rc))
    {
      // XXXJEFFA Hardcode "cv" namespace prefix for Redstone alpha 7
        // XXXMEH - remove for SMIL 2
//    rc = addGlobalNamespace(SYSTEM_COMPONENT_NAMESPACE, "cv");

      // /Now, fix Structure Interop #2.1 (unrecognized smil-tag namespace)
      // and #2.3 (SMIL 2.0-module namespaces in the smil tag):
      BOOL bFailedTest = testAttributeFailed(pSmilNode);
      if (bFailedTest)
      {
          // / systemRequired was specified on the whole document, via the
          // <smil ...> tag, and the associated namespace was not specified
          // or is not a supported one.  Helps fix SMIL 2.0 Interop
          // Structure Module tests #2.1-#2.3.  Is NOT an error condition:
          rc = HXR_OK;
          return rc;
      }
    }

    if (FAILED(rc))
    {
      return rc;
    }

    SMILNode* 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)
    {
      SMILNode* pBodyNode = findFirstNode(SMILBody);
      if(pBodyNode &&
          pBodyNode->m_pNodeList)
      {
            CSmilBodyElement* pBodyEl = makeBodyElement(pBodyNode);
            if (pBodyEl)
            {
                pBodyNode->m_pElement = pBodyEl;
            }
          rc = addToNamespaceScope(pBodyNode);
          if (SUCCEEDED(rc))
          {
            SMILNode* pTopNode = getTimelineDescendent(pBodyNode, NULL);
            // /XXXEH- TODO: need to look at body element's m_pValues and
            // see if it has any timing attributes explicitly set; if
            // so, set the following to TRUE, else to FALSE:
            BOOL bBodyTagHasTimingAttributes = TRUE;
            m_bAllowPlaylistBehavior = TRUE;
#if defined(ALLOW_PLAYLIST_STYLE_SEQ)
#if defined(DONT_ALLOW_PLAYLIST_STYLE_SEQ_IF_EXPLICIT_OUTER_TIMECONTAINER)
            // /Here's the deal: if pTopNode is an ancestor of *all*
            // media elements, then we don't play as a playlist; the
            // *only* case where we play as a playlist is if there are
            // multiple timed children of the body element, where a
            // child of the body, for these purposes, is defined as a
            // timeline element that has no timeline ancestors other
            // than the body element:
            // /First, look for sibling of pTopNode:
            if (pTopNode)
            {
                SMILNode* pSiblingTopNode =
                      getTimelineDescendent(pBodyNode, pTopNode);
                if (pSiblingTopNode  &&
                      SMILUnknown != pSiblingTopNode->m_tag)
                {
                  SMILNode* pSyncAncestorTopNode =
                        getSyncAncestor(pTopNode);
                  SMILNode* pSyncAncestorSiblingTopNode =
                        getSyncAncestor(pSiblingTopNode);
                  // /If the sync bases are different, or if each is
                  // the body element, then do playlist, else don't:
                  if ((pSyncAncestorTopNode !=
                        pSyncAncestorSiblingTopNode)  ||
                        (!pSyncAncestorTopNode  &&
                        !pSyncAncestorSiblingTopNode)  ||
                        (SMILBody == pSyncAncestorTopNode->m_tag  &&
                        SMILBody ==
                        pSyncAncestorSiblingTopNode->m_tag) )
                  {
                      m_bAllowPlaylistBehavior = TRUE;
                  }
                  else
                  {
                      m_bAllowPlaylistBehavior = FALSE;
                  }
                }
                else
                {
                  m_bAllowPlaylistBehavior = FALSE;
                }
            }
#endif /* DONT_ALLOW_PLAYLIST_STYLE_SEQ_IF_EXPLICIT_OUTER_TIMECONTAINER. */

            // adjust context based on itself position within its parent
            // in nested meta
            if (m_elementWithinTag == WithinPar ||
                m_elementWithinTag == WithinSeqInPar)
            {
                m_bAllowPlaylistBehavior = FALSE;
            }

            if(pTopNode  &&
                  (pTopNode->m_tag != SMILSeq  ||
                  // /If body tag has timing attributes, we need to
                  // create an outer tag no matter what's already there
                  // so this outer tag can take the body's timing:
                  bBodyTagHasTimingAttributes) )
            {
                createSeqWrapper(pBodyNode->m_pNodeList,
                      !m_bAllowPlaylistBehavior);
            }

            //[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, FALSE);
            }
#else
            if (SMILSeq == pTop->m_tag)
            {
                m_bAllowPlaylistBehavior = TRUE;
            }
            else
            {
                m_bAllowPlaylistBehavior = FALSE;
            }

            // adjust context based on itself position within its parent
            // in nested meta
            if (m_elementWithinTag == WithinPar ||
                m_elementWithinTag == WithinSeqInPar)
            {
                m_bAllowPlaylistBehavior = FALSE;
            }

            // /We no longer care if it's a seq or not; we have to
            // respect any timing attributes that have been put on
            // the body element, and we also have to stop doing the
            // playlist-style seq handling:
            if(pTopNode)
            {
                createSeqWrapper(pBodyNode->m_pNodeList,
                             m_bAllowPlaylistBehavior);
            }
#endif

            if(HXR_OK != markTestAttributeNodes(pBodyNode->m_pNodeList) ||
               HXR_OK != expandRepeatElements(pBodyNode->m_pNodeList)   ||
               HXR_OK != createBodyElements(pBodyNode->m_pNodeList) ||
               HXR_OK != resolveSyncBaseElements() ||
               HXR_OK != assignGroupIndexes(pBodyNode->m_pNodeList) ||
               HXR_OK != constructTimelineElements(pBodyNode->m_pNodeList) ||
               HXR_OK != setInitialDelays(pBodyNode->m_pNodeList) ||
               //HXR_OK != updateEventElements(pBodyNode->m_pNodeList) ||
               HXR_OK != handleExclDescendants()  ||
      #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.
      }
    }

    // Check for any custom events
    if (SUCCEEDED(rc))
    {
        checkForExternalEvents();
        // Check to see if any of the sources
        // had the "handler" attribute defined,
        // and if so, then set corresponding info in
        // the the source which is the handler
        checkForEventHandlers();
    }

    return rc;
}

HX_RESULT
CSmilParser::createSeqWrapper(SMILNodeList* pNodeList, BOOL bMakeInnerPar)
{
    // /Create a default <seq></seq> wrapper within the <body></body>
    // and move all children as well as attributes of the body to the seq.
    // Then, make an internal par within that seq to get rid of the
    // "playlist" behavior that we've all come to love to hate when the
    // outer time container is a seq:
    HX_RESULT rc = HXR_OK;
    SMILNode* pSeqNode = new SMILNode;
    SMILNode* pParNode = NULL;
    SMILNode* pEndParNode = NULL;
    SMILNode* pWrapperNodeThatGetsTheChildren = pSeqNode;
    int count = pNodeList->GetCount();
    SMILNode* pEndBodyNode = NULL;
    SMILNode* pEndSeqNode = NULL;

    if (!pSeqNode)
    {
      rc = HXR_FAIL;
      goto cleanup;
    }

    pSeqNode->m_name = "seq";
    pSeqNode->m_pParent = pNodeList->m_pParentNode;
    pSeqNode->m_id = assignID("seq");
    pSeqNode->m_tag = SMILSeq;
    pSeqNode->m_pNodeList = new SMILNodeList;
    // /XXXEH- TODO: fix SMIL 2.0 Interop Structure Module #1.2 which needs
    // the following var to be set to FALSE if and only if there really is
    // explicit dur or end on the body element (and we don't want to do
    // playlist behavior in that case either):
    pSeqNode->m_bIsOuterWrapperTimeContainer = TRUE;

    HX_ASSERT(pSeqNode->m_pParent);
    if (pSeqNode->m_pParent  &&  SMILBody == pSeqNode->m_pParent->m_tag)
    {
      // /Copy all of body element's timing to this seq element:
      pSeqNode->m_pParent->m_pValues->AddRef();
      pSeqNode->m_pValues = pSeqNode->m_pParent->m_pValues;
      // /Give seq wrapper the id of the body since it is now the
      // acting body container:
      HX_ASSERT(pSeqNode->m_pParent->m_id.GetLength() > 0);
      if (pSeqNode->m_pParent->m_id.GetLength() > 0)
      {
          pSeqNode->m_id = pSeqNode->m_pParent->m_id;
          (*m_pIDMap)[(const char*)pSeqNode->m_id] =
                pSeqNode;
          pSeqNode->m_pParent->m_id = assignID("body");
          (*m_pIDMap)[(const char*)pSeqNode->m_pParent->m_id] =
                pSeqNode->m_pParent;
      }
    }

    if (bMakeInnerPar)
    {
      pParNode = new SMILNode;
      if (!pParNode)
      {
          HX_DELETE(pSeqNode);
          rc = HXR_FAIL;
          goto cleanup;
      }

      pParNode->m_name = "par";
      pParNode->m_pParent = pSeqNode;
      pParNode->m_id = assignID("par");
      pParNode->m_tag = SMILPar;
      pParNode->m_pNodeList = new SMILNodeList;
      pParNode->m_bIsOuterWrapperTimeContainer = TRUE;

      if (!pParNode->m_pNodeList)
      {
          HX_DELETE(pSeqNode);
          HX_DELETE(pParNode);
          rc = HXR_FAIL;
          goto cleanup;
      }

      // /This par will be the one that adopts the children of the body:
      pWrapperNodeThatGetsTheChildren = pParNode;

      // /The par is a child of the seq wrapper:
      pSeqNode->m_pNodeList->AddTail(pParNode);

      // /Create a </par> node and add it to the new par's list:
      pEndParNode = new SMILNode;
      pEndParNode->m_name = "par";
      pEndParNode->m_pParent = pParNode;
      pEndParNode->m_id = "CLOSE-par";
      pEndParNode->m_tag = SMILEndSeq;
    }

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

    // set the persistent duration on this wrapper node to
    // restrain the whole SMIL presentation
    //
    // XXX HP need to remove setting m_ulPersistentComponentDuration
    //            explicitly to the group object in ::addGroup() after
    //            Erik fix body duration
    if (m_ulPersistentComponentDuration)
    {
      if (!pWrapperNodeThatGetsTheChildren->m_pValues)
      {
          pWrapperNodeThatGetsTheChildren->m_pValues = new CHXHeader();
          pWrapperNodeThatGetsTheChildren->m_pValues->AddRef();
      }

      char szValue[MAX_DISPLAY_NAME] = {0}; /* Flawfinder: ignore */
      IHXBuffer* pBuffer = new CHXBuffer();
      pBuffer->AddRef();

      sprintf(szValue, "%lums", m_ulPersistentComponentDuration); /* Flawfinder: ignore */
      pBuffer->Set((const UCHAR*)szValue, strlen(szValue) + 1);

      pWrapperNodeThatGetsTheChildren->m_pValues->SetPropertyCString("dur", pBuffer);
      HX_RELEASE(pBuffer);
    }

    if (pParNode)
    {
      HX_ASSERT(pEndParNode);
      if (pEndParNode)
      {
          pParNode->m_pNodeList->AddTail(pEndParNode);
      }
    }

    pEndSeqNode = new SMILNode;
    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);

cleanup:
    return rc;
}


// /This handles creating a node for the case where an anchor (a or area) tag
// has a target="x" where "x" is the id of an existing region: (PR 52891)
HX_RESULT
CSmilParser::createElementForAnchorTarget(CSmilAAnchorElement* pAnchor,
        SMILNodeList* pNodeList)
{
    HX_RESULT rc = HXR_OK;
    SMILNode* pRefNode = NULL;
    SMILNode* pTailNode = NULL;
    IHXValues* pAttribs = NULL;
    int count = -1;

    if (!pAnchor  ||  !pAnchor->m_pNode  ||  !(const char*)pAnchor->m_pNode->m_id  ||
          !pNodeList  ||   !m_pClassFactory  ||  pAnchor->m_href.IsEmpty())
    {
      HX_ASSERT(pAnchor  &&  pAnchor->m_pNode  &&  (const char*)pAnchor->m_pNode->m_id  &&
            pNodeList  &&  m_pClassFactory  &&  !pAnchor->m_href.IsEmpty());
      rc = HXR_FAIL;
      goto cleanup;
    }

    pRefNode = new SMILNode;
    count = pNodeList->GetCount();

    if (!pRefNode  ||  count<2) // /2 <= a|area + CLOSE-par + ...
    {
      HX_ASSERT(pRefNode  &&  count>=2);
      rc = HXR_FAIL;
      goto cleanup;
    }

    pRefNode->m_name = "ref";
    pRefNode->m_pParent = pNodeList->m_pParentNode;
    pRefNode->m_id = assignID("ref_forLinkTarget");
    pRefNode->m_tag = SMILRef;

    // /Now, set the begin and src properties:
    if (HXR_OK == m_pClassFactory->CreateInstance(IID_IHXValues,
                      (void**)&pAttribs))
    {
        pRefNode->m_pValues = pAttribs;

      IHXBuffer* pBufBegin = NULL;
      IHXBuffer* pBufSrc = NULL;
      IHXBuffer* pBufRegion = NULL;
      IHXBuffer* pBufLinkDestnSndLevel = NULL;
      IHXBuffer* pBufLinkSourceSndLevel = NULL;
      if ((HXR_OK == m_pClassFactory->CreateInstance(IID_IHXBuffer,
            (void**)&pBufBegin))  &&
            (HXR_OK == m_pClassFactory->CreateInstance(IID_IHXBuffer,
            (void**)&pBufSrc))  &&
            (HXR_OK == m_pClassFactory->CreateInstance(IID_IHXBuffer,
            (void**)&pBufRegion))  &&
            (HXR_OK == m_pClassFactory->CreateInstance(IID_IHXBuffer,
            (void**)&pBufLinkDestnSndLevel))  &&
            (HXR_OK == m_pClassFactory->CreateInstance(IID_IHXBuffer,
            (void**)&pBufLinkSourceSndLevel)) )
      {
          CHXString valStr = (const char*)(pAnchor->m_pNode->m_id);
          valStr += ".activateEvent";
          valStr += '\0';
          const char* pVal = valStr;
          pBufBegin->Set((const unsigned char*)pVal, strlen(valStr)+1);
          pRefNode->m_pValues->SetPropertyCString("begin", pBufBegin);

          pBufSrc->Set((const unsigned char*)((const char*)pAnchor->m_href),
                pAnchor->m_href.GetLength()+1);
          pRefNode->m_pValues->SetPropertyCString("src", pBufSrc);

          pBufRegion->Set((const unsigned char*)((const char*)pAnchor->m_target),
                pAnchor->m_target.GetLength()+1);
          pRefNode->m_pValues->SetPropertyCString("region", pBufRegion);

          // /Helps fix PR 66650: set "soundLevel" here using the anchor's
          // destinationLevel and then use it if|when this source gets added
          char* pszTmp = new char[64];
          if (!pszTmp)
          {
            rc = HXR_OUTOFMEMORY;
          }
          else
          {
            sprintf(pszTmp, "%lu", pAnchor->m_ulDestinationLevel_pct); /* Flawfinder: ignore */
            pBufLinkDestnSndLevel->Set((const unsigned char*)pszTmp,
                  strlen(pszTmp)+1);
            pRefNode->m_pValues->SetPropertyCString(
                  TARGET_OF_LINK_DESTINATION_SOUND_LEVEL_STR,
                  pBufLinkDestnSndLevel);
            sprintf(pszTmp, "%lu", pAnchor->m_ulSourceLevel_pct); /* Flawfinder: ignore */
            pBufLinkSourceSndLevel->Set((const unsigned char*)pszTmp,
                  strlen(pszTmp)+1);
            pRefNode->m_pValues->SetPropertyCString(
                  TARGET_OF_LINK_SOURCE_SOUND_LEVEL_STR,
                  pBufLinkSourceSndLevel);
            delete [] pszTmp;

            // /This is needed for PR 55120, PR 66650, and PR 55791; a link
            // that has sourcePlaystate="play"|"pause" and messes with audio
            // level of either player will require reflush hint to be set:
            if (pAnchor->m_ulSourceLevel_pct !=
                  pAnchor->m_ulDestinationLevel_pct  &&
                  (SMILLinkPlaystatePlay == pAnchor->m_sourcePlaystate  ||
                  SMILLinkPlaystatePause == pAnchor->m_sourcePlaystate) )
            {
                m_bAllTracksNeedReflushHint = TRUE;
            }
          }

          HX_RELEASE(pBufBegin);
          HX_RELEASE(pBufSrc);
          HX_RELEASE(pBufRegion);
      }
    }

    // /Now add it to the par ancestor after first removing the "CLOSE-par"
    // node from the tail (adding it back afterwards):
    pTailNode = (SMILNode*)pNodeList->RemoveTail();
    pNodeList->AddTail(pRefNode);
    (*m_pIDMap)[(const char*)pRefNode->m_id] = pRefNode;
    if (pTailNode)
    {
      pNodeList->AddTail(pTailNode);
    }

    // /Set this in here since we know this setup was successful:
    pAnchor->m_bTargetIsARegion = TRUE;

cleanup:
    return rc;
}


#if 0  /*XXXEH- I wrote this and then didn't need it but it may come in
       handy later so #if(0) it out: */
BOOL
CSmilParser::hasNoSourceChildren(SMILNode* pNode)
{
    BOOL bNoSourceChildrenFound = TRUE;
    if (pNode  &&  pNode->m_pNodeList)
    {
      int count = pNode->m_pNodeList->GetCount();
      while( count > 0)
      {
          SMILNode* pChildNode =(SMILNode*)(pNode->m_pNodeList->GetHead());
          if (!pChildNode)
          {
            break;
          }
          if (isMediaObject(pChildNode))
          {
            bNoSourceChildrenFound = FALSE;
            break;
          }
          // /Recurse on down:
          else if (!hasNoSourceChildren(pChildNode))
          {
            bNoSourceChildrenFound = FALSE;
            break;
          }
          --count;
      }
    }
    return bNoSourceChildrenFound;
}
#endif /* 0 */

#if 0
//XXXBAB - useful for debugging
void
CSmilParser::PrintNode(SMILNode* 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)
      {
          SMILNode* pChildNode = (SMILNode*)(*i);
          PrintNode(pChildNode, fp, level+1);
      }
    }
}

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


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

//#define FOR_DEBUGGING_ONLY
#if defined(_DEBUG)  &&  defined(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;
      }

      SMILNode* pNode = (SMILNode*)(*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;
      UINT32 ulDuration = (UINT32)-1;
      if(pNode->m_pElement &&
          pNode->m_pElement->m_pTimelineElement)
      {
          ulDelay = pNode->m_pElement->m_pTimelineElement->getDelay();
          ulDuration = pNode->m_pElement->m_pTimelineElement->getDuration();
      }
      fprintf(fp, "%s tag=%d group=%d repeatid=%s delay=%d duration=%d deleted=%d\n", (const char*)pNode->m_id,
          pNode->m_tag, pNode->m_nGroup, (const char*)pNode->m_repeatid, ulDelay, ulDuration, pNode->m_bDelete);
      fclose(fp);

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

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

    if(!m_pAddGroupMap)
    {
      rc = HXR_UNEXPECTED;
    }
    else
    {
      UINT32 ulGroupNo = 0;
      CSmilAddGroup* 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
CSmilParser::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)
    {
      // /Fixes the SMIL-file part of PR 58151: sometimes this list will
      // have tokens that are padded w/whitespaces or are separated by ", "
      // instead of just ",", so first remove all whitespace chars after
      // the ',' (or at the start of the first token) and, if we run
      // into a whitespace-only string, go past the next comma:
      while (isspace(*pLangFrag)  ||  ',' == *pLangFrag)
      {
          *pLangFrag++;
      }
      if ('\0' == *pLangFrag)
      {
          break; // /Whitespace-only or emtpy string is not valid.
      }


      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
                // /Adding this check for "*"-only string "fixes" TLC's
                // PR 64646 that they don't want to fix for fear it will
                // break other things; only allow "*[x]" where [x] is one
                // or more characters since TLC sends (has since 1998) a
                // solo "*" at the end of the language list (for purposes
                // other than SMIL).  Without this fix, every language
                // (like systemLanguage="holwerMonkey") evaluates to TRUE:
                *(pPrefLang+1) != '\0')
          {
            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
CSmilParser::systemComponentFailed(IHXBuffer* pRequiredValue)
{
    BOOL bFailed = TRUE;

    if (m_pContext)
    {
        IHXUpgradeCollection* pUpgradeCollection = NULL;
        m_pContext->QueryInterface(IID_IHXUpgradeCollection,
                                   (void**) &pUpgradeCollection);
        if (pUpgradeCollection)
        {
          UINT32 ulIndexOfAddedValue =
                pUpgradeCollection->Add(eUT_Required, pRequiredValue, 0, 0);
            if (!m_pISystemRequired)
            {
                m_pContext->QueryInterface(IID_IHXSystemRequired,
                                           (void**) &m_pISystemRequired);
            }
            if (m_pISystemRequired)
            {
                bFailed = FAILED(m_pISystemRequired->HasFeatures(pUpgradeCollection));
            }
          if (bFailed)
          {
            // /Removing failed component fixes PR 95617. The
            // (persistent m_pContext's) upgradeCollection will fail the
            // next time, too, even if the next value should succeed, if
            // the current non-supported value remains in it:
            pUpgradeCollection->Remove(ulIndexOfAddedValue);
          }
        }
        HX_RELEASE(pUpgradeCollection);
    }

    return bFailed;
}

BOOL
CSmilParser::testAttributeFailed(SMILNode* 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 (FAILED(rc))
      {
          rc = pNode->m_pValues->GetPropertyCString("systemRequired", pBuf);
      }
      if(HXR_OK == rc)
      {
          const char* pActualString = (const char*)pBuf->GetBuffer();
          // Check with the core first

          if(m_pRequireTagsMap)
          {
            void* pTag = 0;
            // /In SMIL 2.0, this may be a list of logically ANDed values
            // separated by a '+', e.g., "HostLanguage+BrushMedia".  We
            // need to look up all of them and, if any in the list are
            // not found, we fail on the whole thing.  (See SMIL 2.0
            // Interop Structure Module test #2.2 & #2.3):
            // /XXXEH- do whitespace removal on either side of '+'s:
            const char* pStrWalker = pActualString;
            char* pNextPlusSign = (char*)(strchr(pStrWalker,'+'));
            do
            {
                if (pNextPlusSign)
                {
                  *pNextPlusSign = '\0';
                }
                if (!m_pRequireTagsMap->Lookup(pStrWalker, (void*&)pTag))
                {
                  bFailed = TRUE;
                }
                if (pNextPlusSign)
                {
                  *pNextPlusSign = '+';
                  pStrWalker = pNextPlusSign+1;
                  pNextPlusSign = (char*)(strchr(pStrWalker,'+'));
                }
                else
                {
                  pStrWalker = NULL;
                }
            } while (!bFailed  &&  pStrWalker);

          }
          else    // no required list, can't parse it
          {
            bFailed = TRUE;
          }

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

      rc = pNode->m_pValues->GetPropertyCString("systemBitrate", pBuf);
      if (FAILED(rc))
      {
          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("systemLanguage", pBuf);
      if (FAILED(rc))
      {
          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("systemCaptions", pBuf);
      if (FAILED(rc))
      {
          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;
          }
      }

      rc = pNode->m_pValues->GetPropertyCString("systemOverdubOrSubtitle",
          pBuf);
      if (FAILED(rc))
      {
          rc = pNode->m_pValues->GetPropertyCString(
            "system-overdub-or-caption", pBuf);
      }

      if (rc == HXR_OK)
      {
          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("systemScreenSize", pBuf);
      if (FAILED(rc))
      {
          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 it's (0,0) in our system, then we failed to get the values
          // from the OS via GetSystemScreenInfo() & thus don't support this
          // in this OS.  SMIL 2.0 spec says to return "TRUE" if you don't
          // support a test attribute:
          if((!m_ulScreenHeightPreference  &&  !m_ulScreenWidthPreference))
          {
            bFailed = FALSE;
          }
          else if((m_ulScreenHeightPreference < ulScreenHeight) ||
             (m_ulScreenWidthPreference < ulScreenWidth))
          {
            bFailed = TRUE;
          }
          pBuf->Release();
          if(bFailed)
          {
            goto exit;
          }
      }

      rc = pNode->m_pValues->GetPropertyCString("systemScreenDepth", pBuf);
      if (FAILED(rc))
      {
          rc = pNode->m_pValues->GetPropertyCString("system-screen-depth",
            pBuf);
      }
      if(HXR_OK == rc)
      {
          UINT32 ulScreenDepth = atol((const char*)pBuf->GetBuffer());
          // /If it's 0 in our system, then our player does not (in this
          // current build) support this.  The SMIL 2.0 spec says to
          // return "TRUE" if you don't support a test attribute:
          if(!m_ulScreenDepthPreference)
          {
            bFailed = FALSE;
          }
          else if(m_ulScreenDepthPreference < ulScreenDepth)
          {
            bFailed = TRUE;
          }
          pBuf->Release();
          if(bFailed)
          {
            goto exit;
          }
      }


      // /"systemAudioDesc", "systemOperatingSystem", and "systemCPU" were
      // introduced in SMIL 2.0:

      // /XXXEH- TODO: We need to coordinate with TLC folks to make this a
      // user-settable option:
      rc = pNode->m_pValues->GetPropertyCString("systemAudioDesc", pBuf);
      if(HXR_OK == rc)
      {
          const char* pActualValue = (const char*)pBuf->GetBuffer();
          if(strcmp(pActualValue, "on") == 0)
          {
            if(!m_bSystemAudioDescPreference)
            {
                bFailed = TRUE;
            }
          }
          else
          {
            if(m_bSystemAudioDescPreference)
            {
                bFailed = TRUE;
            }
          }
          pBuf->Release();
          if(bFailed)
          {
            goto exit;
          }
      }


      rc = pNode->m_pValues->GetPropertyCString("systemOperatingSystem",
            pBuf);
      if(HXR_OK == rc  &&  HX_PLATFORM_UNKNOWN!=m_versionInfo.dwPlatformId)
      {

#if defined(_MACINTOSH) || defined(_MAC_UNIX) // /Sanity check on Mac platform id:
          HX_ASSERT(HX_PLATFORM_MACOT == m_versionInfo.dwPlatformId  ||
                HX_PLATFORM_MACTCP == m_versionInfo.dwPlatformId);
#endif

          const char* pOS = (const char*)pBuf->GetBuffer();
          // /First, strip leading whitespace chars:
          while (*pOS  &&  isspace(*pOS))
          {
            *pOS++;
          }

          /*fixes PR 64428 (UseSystemOS version)*/
          if (!m_bUseSystemOS)
          {
            // /Fixes PR 81522 (systemOperatingSystem version): if user
            // says not to use OS info, treat it as unknown per SMIL2 spec:
            bFailed = FALSE; // /Setting to FALSE instead of comparing to
                         // "unknown" fixes OS part of PR 64428(revisted)
            goto doneSysOS;
          }

          // /From the SMIL 2.0 CR spec:
          // "The following list contains the suggested values for this
          //  test attribute (additional names may be supported by an
          //  implementation):
          //          aix, beos, bsdi, dgux, freebsd, hpux, irix, linux,
          //          macos, ncr, nec, netbsd, nextstep, nto, openbsd, openvms,
          //          os2, osf, palmos, qnx, sinix, rhapsody, sco, solaris,
          //          sonly, sunos, unixware, win16, win32, win9x, winnt,
          //          wince, unknown"
          // /NOTE: current Mozilla list can be found here:
          // http://lxr.mozilla.org/seamonkey/search?string=_PR_SI_SYSNAME
          switch (m_versionInfo.dwPlatformId)
          {
            case HX_PLATFORM_IRIX:
            {
                if (0 != strcmp("irix", pOS))
                {
                  bFailed = TRUE;
                }
            }
            break;

            case HX_PLATFORM_LINUX:
            {
                if (0 != strcmp("linux", pOS))
                {
                  bFailed = TRUE;
                }
            }
            break;

            case HX_PLATFORM_MACOT:
            case HX_PLATFORM_MACTCP:
            {
                if (0 != strcmp("macos", pOS))
                {
                  bFailed = TRUE;
//  _DARWIN, _CARBON, and/or _MAC_UNIX implies Darwin is present on a Mac:
#if defined(_DARWIN)  ||  defined(_CARBON)  ||  defined(_MAC_UNIX)
                  // /Fixes PR 95625: Also check for darwin (UNIX on Mac):
                  bFailed = (0 != strcmp("darwin", pOS));
#endif // / _DARWIN || _CARBON || _MAC_UNIX.
                }
            }
            break;

            case HX_PLATFORM_QNXNTO:
            {   // /We should assert!  But, hey, it might come back 8-]
                if (0 != strcmp("qnx", pOS))
                {
                  bFailed = TRUE;
                }
            }
            break;

            case HX_PLATFORM_SOLARIS:
            case HX_PLATFORM_SUNOS:
            {
                // /These are the same since RN started porting to them:
                if (0 != strcmp("solaris", pOS)  &&
                      0 != strcmp("sunos", pOS))
                {
                  bFailed = TRUE;
                }
            }
            break;

            case HX_PLATFORM_WIN16:
            {   // /We should really assert!  But, it might come back 8-]
                if (0 != strcmp("win16", pOS))
                {
                  bFailed = TRUE;
                }
            }
            break;

            case HX_PLATFORM_WIN32S:// /XXXEH is Win32s always on Win16??
            {
                if (0 != strcmp("win16", pOS))
                {
                  bFailed = TRUE;
                }
            }
            break;

            case HX_PLATFORM_WIN95:
            case HX_PLATFORM_WIN98:
            {
                if (0 != strcmp("win9x", pOS)  &&
                      0 != strcmp("win32", pOS))
                {
                  bFailed = TRUE;
                }
            }
            break;

            case HX_PLATFORM_WINNT:
            {
                if (0 != strcmp("winnt", pOS)  &&
                      0 != strcmp("win32", pOS))
                {
                  bFailed = TRUE;
                }
            }
            break;

            case HX_PLATFORM_SYMBIAN:
            {
                if (0 != strcmp("symbian", pOS))
                {
                  bFailed = TRUE;
                }
            }
            break;

            case HX_PLATFORM_UNKNOWN:
            {
                if (0 != strcmp("unknown", pOS))
                {
                  bFailed = TRUE;
                }
            }
            break;

            default:
            {
                // /The following SMIL 2.0 systemOperatingSystem values
                // are not covered in HXGetWinVer as of 12/2000:
                //          XXXaix, XXXbeos, bsdi, dgux, freebsd, XXXhpux, ncr,
                //          nec, netbsd, nextstep, nto, openbsd, openvms,
                //          os2, XXXosf, palmos, sinix, rhapsody, XXXsco,
                //          sonly, unixware, wince
#if defined(_AIX)
                if (0 != strcmp("aix", pOS))
                {
                  bFailed = TRUE;
                }
#elif defined(_BEOS)
                if (0 != strcmp("beos", pOS))
                {
                  bFailed = TRUE;
                }
#elif defined(_BSDI)
                if (0 != strcmp("bsdi", pOS))
                {
                  bFailed = TRUE;
                }
//  _DARWIN, _CARBON, and/or _MAC_UNIX implies Darwin is present on a Mac:
#elif defined(_DARWIN)  ||  defined(_CARBON)  ||  defined(_MAC_UNIX)
                // /Fixes PR 95625 (in case HX_PLATFORM_MAC... is not
                // handled, above):
                if (0 != strcmp("darwin", pOS))
                {
                  bFailed = TRUE;
                }
#elif defined(_FREEBSD)  ||  defined(_FREEBSD2)
                if (0 != strcmp("freebsd", pOS))
                {
                  bFailed = TRUE;
                }
#elif defined(_HPUX)
                if (0 != strcmp("hpux", pOS))
                {
                  bFailed = TRUE;
                }
#elif defined(_NETBSD)  ||  defined(__NetBSD__)
                if (0 != strcmp("netbsd", pOS))
                {
                  bFailed = TRUE;
                }
#elif defined(_OSF1)
                if (0 != strcmp("osf", pOS))
                {
                  bFailed = TRUE;
                }
#elif defined(_SCO_UW)
                if (0 != strcmp("sco", pOS))
                {
                  bFailed = TRUE;
                }
#elif defined(_WINCE)
                if (0 != strcmp("wince", pOS))
                {
                  bFailed = TRUE;
                }
// /SGI is not in the SMIL 2.0 spec, but the spec says we can add more than
// the OSes it recommends:
#elif defined(_SGI)
                if (0 != strcmp("sgi", pOS))
                {
                  bFailed = TRUE;
                }
#else // /Catch-all for any other OS; throw an error at compile time:
/* Please add to the above list with your OS's define and match it to
one of the following (or add a string if it's not in the following list and
be sure to tell RN documentation folks about it):
"dgux", "ncr", "nec", "nextstep", "nto", "openbsd", "openvms", "os2",
"palmos", "sinix", "rhapsody", "sonly", "unixware"
*/
#endif

            }
                break;
          }
          if (bFailed  &&  0 == strcmp("unknown", pOS))
          {
            bFailed = FALSE; // /"unknown should play on any OS"
          }

doneSysOS:
          HX_RELEASE(pBuf);
          if(bFailed)
          {
            goto exit;
          }
      }

      rc = pNode->m_pValues->GetPropertyCString("systemCPU", pBuf);
      if(HXR_OK == rc  &&  HX_MACHINE_UNKNOWN!=m_versionInfo.dwMachineType)
      {
          const char* pCPU = (const char*)pBuf->GetBuffer();
          // /First, strip leading whitespace chars:
          while (*pCPU  &&  isspace(*pCPU))
          {
            *pCPU++;
          }

          /*fixes PR 64428 (UseSystemCPU version)*/
          if (!m_bUseSystemCPU)
          {
            // /Fixes PR 81522 (systemCPU version): if user says not to
            // use their CPU info, treat it as unknown per SMIL2 spec:
            bFailed = FALSE; // /Setting to FALSE instead of comparing to
                         // "unknown" fixes CPU part of PR 64428(revisted)
            goto doneSysCPU;
          }

          // /The SMIL 2.0 draft of 12/2000 says:
          //  "The following list contains the suggested values for this
          //   test attribute (additional names may be supported by an
          //   implementation): alpha, arm, arm32, hppa1.1, m68k, mips,
          //   ppc, rs6000, vax, x86, unknown."
          switch (m_versionInfo.dwMachineType)
          {
            case HX_MACHINE_ALPHA:
            {
                if (0 != strcmp("alpha", pCPU))
                {
                  bFailed = TRUE;
                }
            }
            break;

                case HX_MACHINE_SYMEMULATOR:
                case HX_MACHINE_ARM:
                {
                    if (0 != strcmp("arm", pCPU))
                    {
                        if (0 != strcmp("arm32", pCPU))
                        {
                            bFailed = TRUE;
                        }
                    }
                }
                break;

/* /XXX- these aren't supported in HXGetWinVer():
            case :
            {
                if (0 != strcmp("hppa1.1", pCPU))
                {
                  bFailed = TRUE;
                }
            }
            break;
*/

            case HX_MACHINE_68K:
            {
                if (0 != strcmp("m68k", pCPU))
                {
                  bFailed = TRUE;
                }
            }
            break;

            case HX_MACHINE_MIPS:
            {
                if (0 != strcmp("mips", pCPU))
                {
                  bFailed = TRUE;
                }
            }
            break;

            case HX_MACHINE_PPC:
            {
                if (0 != strcmp("ppc", pCPU))
                {
                  bFailed = TRUE;
                }
            }
            break;

/* /XXX- this isn't supported in HXGetWinVer():
            case :
            {
                if (0 != strcmp("rs6000", pCPU))
                {
                  bFailed = TRUE;
                }
            }
            break;
*/

            // /This one is not in the SMIL 2.0 spec; I added it as
            // permitted by the spec:
            case HX_MACHINE_SPARC:
            {
                if (0 != strcmp("sparc", pCPU))
                {
                  bFailed = TRUE;
                }
            }
            break;

/* /XXX- this isn't supported in HXGetWinVer():
            case :
            {
                if (0 != strcmp("vax", pCPU))
                {
                  bFailed = TRUE;
                }
            }
            break;
*/

            case HX_MACHINE_486:
            case HX_MACHINE_586:
            case HX_MACHINE_686:
            {
                if (0 != strcmp("x86", pCPU))
                {
                  bFailed = TRUE;
                }
            }
            break;

            default:
            {
                // /Need to fill in others here and in HXGetWinVer().
            }
            break;
          }
          if (bFailed  &&  0 == strcmp("unknown", pCPU))
          {
            bFailed = FALSE; // /"unknown should play on any CPU"
          }

doneSysCPU:
          HX_RELEASE(pBuf);
          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;
}



BOOL
CSmilParser::customTestFailed(SMILNode* pNode)
{
    BOOL bFailed = FALSE; // /If no "customTest" attribute, we don't fail.
    IHXBuffer* pBuf = NULL;
    if(pNode->m_pValues)
    {
      HX_RESULT rc = pNode->m_pValues->GetPropertyCString("customTest",
                                              pBuf);
      if(HXR_OK == rc)
      {
          const char* pCustTestKey = (const char*)pBuf->GetBuffer();

          CSmilCustomTest* pCustTest = (CSmilCustomTest*)
                ((*m_pCustomTestMap)[pCustTestKey]);

          if (pCust