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

smldoc.cpp

/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: smldoc.cpp,v 1.10.4.8 2004/07/13 22:46:02 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>
#include <ctype.h>  /* for isspace() */
#if defined(_UNIX)  &&  (!(defined(_BEOS)))  &&  defined(USE_XWINDOWS)
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#endif
// include
#include "hxtypes.h"
#include "hxwintyp.h"
#include "hxcom.h"
#include "smiltype.h"
#include "hxcomm.h"
#include "ihxpckts.h"
#include "hxprefs.h"
#include "hxplugn.h"
#include "hxcore.h"
#include "hxwin.h"
#include "hxsite2.h"
#include "hxrendr.h"
#include "hxgroup.h"
#include "hxevent.h"
#include "hxvsurf.h"
#include "hxhyper.h"
#include "hxxml.h"
#include "hxvport.h"
#include "hxfiles.h"
#include "hxausvc.h"
#include "hxclsnk.h"
#if !defined(XXXEH_NEED_TO_ADD_51_PRCNT_OPAQUE_SITE_ON_TOP_TO_DISABLE)
#include "hxvctrl.h"
#endif
#include "hxmmrkr.h"
#include "hxinter.h"
#include "hxupgrd.h"
#include "hxsm2sm.h"
#include "hxerror.h"
// pncont
#include "hxslist.h"
#include "chxpckts.h"
#include "hxstring.h"
#include "hxmap.h"
#include "hxordval.h"
#include "hxstack.h"
#include "rtsputil.h"
// pnmisc
#include "hxurl.h"
#include "hxstrutl.h"
#include "hxwinver.h"
#include "hxtick.h"
#include "dllpath.h"
// rnxmllib
#include "hxxmlprs.h"
// coreres
#include "smilres.h"
// pxcomlib
#include "buffutil.h"
// rmasmil
#include "smlelem.h"
#include "smlparse.h"
#include "smlprstime.h"
#include "smlerror.h"
#include "smlutil.h"
// smlrendr
#include "smlrendr.h"
#include "smlrtype.h"
#include "animattr.h"
#include "animsand.h"
#include "evnthook.h"
#include "siteuser.h"
#include "sitewatc.h"
#include "layout.h"
#include "passivsw.h"
#include "layevent.h"
#include "smcallbk.h"
#include "xdata.h"
#include "smldoc.h"

#if defined(_WINDOWS)
#include "hxalloc.h"
#define IDC_HANDCURSOR 102
#elif defined (_MACINTOSH)
#include "hxmm.h"
#include "cresload.h"
extern FSSpec g_DLLFSpec;
#endif

#if defined(_UNIX)  &&  (!(defined(_BEOS)))  &&  defined(USE_XWINDOWS)
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>
/* USE_SHM doesn't work yet */
#ifdef USE_SHM
#undef USE_SHM
#endif /* USE_SHM */
#endif

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

#ifdef _WINDOWS
extern HINSTANCE g_hInstance;
#endif

// /This got removed, accidentally, by huge optimization HEAD check-in on
// 11/7/2001, causing show="new" and target="[someLPP]" (launching of LPP)
// to stop working.  Putting this back in fixes PR 69905:
#define XXXEH_USE_OPENWINDOW_METHOD

#define HANDLE_DISCRETE_MEDIA_IN_TrackDurationSet 1
// /XXXEH- talk to core devs; we need them to be able to
// deal with an added track that all of a sudden has its
// duration changed to zero; this seems to work fine for
// tracks that have a zero delay, but subsequent ones
// hang the system when their durations are set to zero:
#define XXXEH_WAITING_FOR_PLAYER_CORE_FIX_FOR_RESETTING_DUR_TO_ZERO
#if defined(XXXEH_WAITING_FOR_PLAYER_CORE_FIX_FOR_RESETTING_DUR_TO_ZERO)
#define ZERO_DUR    1 /* 1 millisecond to trick core. */
#else
#define ZERO_DUR    0
#endif

// /For case where destinationLevel < 10 on a new player and we need to set
// the shared-by-all-players volume to accomodate this, we need to limit it
// for now so source player's volume doesn't cut out:
#define XXXEH_MIN_DEST_AUDIOPLAYER_VOL    10

// /Clamp track soundLevel at some arbitrary, high value; MEH chose 1000% (10x)
// for max-allowed animation of soundLevel; use 1000 until someone complains
// and then ask them what would be better:
#define MAX_ALLOWED_SOUNDLEVEL  1000.0


// /#define XXXEH_DEBUG_HANDLESOURCE_AND_TRACKDURATIONSET
// /#define XXXEH_DEBUGOUT_TRACKSTOPPAUSERESUME
// /#define XXXEH_DEBUGOUT_RESOLVEGROUPDUR


/*
 * CSmilDocumentRenderer methods
 */

CSmilDocumentRenderer::CSmilDocumentRenderer(
    CSmilRenderer* pParent, IUnknown* pContext):
    m_pParent(pParent),
    m_pSmilParser(0),
    m_ulParseResult(HXR_OK),
    m_lRefCount(0),
    m_pRegionMap(0),
    m_pRootLayout(NULL),
    m_pViewportList(NULL),
    m_pRegPointMap(NULL),
#if defined(HELIX_FEATURE_SMIL2_TRANSITIONS)
    m_pTransitionMap(0),
#endif /* #if defined(HELIX_FEATURE_SMIL2_TRANSITIONS) */
    m_pSiteInfoByRendererMap(0),
    m_pSiteWatcherMap(0),
    m_pGroupInfoMap(0),
    m_pGroupMap(0),
    m_pDeferredSourceMap(0),
    m_pDeferrededRemoveTrackMap(NULL),
    m_bInTrackDurationSetCall(FALSE),
    m_bEventSinkWasSetup(FALSE),
    m_pPausedAndDisabledIDMap(NULL),
    m_pPausedAndDisabledBrightnessMap(NULL),
    m_pPrefetchTrackElementMap(0),
    m_pSiteInfoList(0),
    m_uCurrentGroupIndex(-1),
    m_pPlayToAssocList(0),
    m_pZOrderList(0),
    m_pSiteMgr(0),
    m_pScheduler(0),
    m_pEventList(0),
    m_pFragment(0),
    m_ulCurrentTime(0),
    m_ulEventListPosition(0),
    m_bFirstTimeSync(FALSE),
    m_bSettingFragment(FALSE),
    m_bFragFoundAndResolved(FALSE),
    m_ulFragmentTimeOffset(0),
    m_bInHyperlink(FALSE),
    m_pStatusMessage(0),
    m_bStatusMessageSet(FALSE),
    m_bMetadataPassedOffAlready(FALSE),
    m_pPersistentLayoutStream(NULL),
    m_bIsRootLayoutSetup(FALSE),
    m_ulNoLayoutRendererCount(0),
    m_pErrorMessages(NULL),
    m_bDestPlaystateIsPause(FALSE),
#ifdef _WINDOWS
    m_hPreHyperlinkCursor(0),
    m_hHyperlinkCursor(0),
    m_bNeedToSetHyperlinkCursor(FALSE),
#endif
#ifdef _MACINTOSH
    m_hHyperlinkCursor(0),
    m_bResetCursor(FALSE),
#endif
#if defined(_UNIX)  &&  (!(defined(_BEOS)))  &&  defined(USE_XWINDOWS)
    m_hHyperlinkCursor(0),
    m_hCurrentCursor(0),
    m_pDisplay(0),
    m_Window(0),
    m_pPixmapDisplay(NULL),
    m_pVisualInfo(NULL),
#endif
    m_bSiteLayoutComplete(FALSE),
    m_nFragmentTracks(0),
    m_dResizeXScale(1.0),
    m_dResizeYScale(1.0),
    m_pActiveTransitions(NULL),
    m_pActiveAnimations(NULL),
    m_pRegionMapIterator(NULL),
    m_pAnimationMap(NULL),
    m_pDelayedRendererCloseList(NULL),
    m_bSMILPresentationHasEnded(FALSE),
    m_pSMILDefaultNamespaceStr(NULL),
    m_pViewPortManager(NULL)
#ifdef XXXMEH_DO_VIEWPORT_TLC
    ,m_pViewPortSiteMap(NULL)
    ,m_bSMILViewportClose(FALSE)
#endif
    , m_pPlayerResumeStack(NULL)
#if defined(BE_SITE_SUPPLIER_20010205)
    , m_pIndependentPlayer(NULL)
    , m_pChildSiteInfoMap(NULL)
#endif /* BE_SITE_SUPPLIER_20010205 */
    , m_ulGroupIndex(0)
    , m_ulTrackIndex(0)
    , m_uGroupIndexWithin(0)
    , m_bSitesDetached(FALSE)
    , m_ulPersistentComponentID(0)
    , m_uPersistentGroupID(0)
    , m_uPersistentTrackID(0)
    , m_pPersistentProperties(NULL)
    , m_pPersistentParentRenderer(NULL)
    , m_pExternalMediaMarkerList(NULL)
    , m_bLayoutElementPresent(FALSE)
    , m_bEmptyLayout(FALSE)
    , m_bDoNotZoom(FALSE)
    , m_AnimationIterator(NULL)
    , m_pAnimSiteRedrawMap(NULL)
    , m_pAnimRegionRecomputeMap(NULL)
    , m_bAnimateRootLayout(FALSE)
    , m_pAnimTopLayoutMap(NULL)
    , m_usAnimBaseGroupIndex(0)
    , m_ulAnimDuration(0)
    , m_ulCurGroupDuration(0)
    , m_pSoundLevelMutex(NULL)
    , m_bCloseCalled(FALSE)
    , m_bRootLayoutSiteDetached(FALSE)
    , m_pHandlePendingSchedulingCallback(NULL)
    , m_pHyperlinkCallback(NULL)
    , m_pMediaID2RendererMap(NULL)
    , m_pMediaID2RendererSiteMap(NULL)
    , m_pMediaID2RendererSiteWatcherMap(NULL)
    , m_pEventSinkList(NULL)
    , m_ulPktnum(0)
    , m_usOldXPos(0)
    , m_usOldYPos(0)
{
    m_pContext = pContext;
    HX_ASSERT(m_pContext);
    if(m_pContext)
    {
      m_pContext->AddRef();
      
      HX_VERIFY(HXR_OK == m_pContext->
              QueryInterface(IID_IHXSiteManager,(void**)&m_pSiteMgr));
      
      HX_VERIFY(HXR_OK == m_pContext->
              QueryInterface(IID_IHXScheduler, (void**)&m_pScheduler));
      
      if (HXR_OK != m_pContext->QueryInterface(IID_IHXStatusMessage,
                                     (void**)&m_pStatusMessage))
      {
          // not an error, just a feature waiting to happen...
          m_pStatusMessage = NULL;
      }
#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)
        // QI for IHXViewPortManager
        m_pContext->QueryInterface(IID_IHXViewPortManager,
                                   (void**) &m_pViewPortManager);
        if (m_pViewPortManager)
        {
            // Add ourselves as a view port sink
            IHXViewPortSink* pSink = NULL;
            QueryInterface(IID_IHXViewPortSink, (void**) &pSink);
            if (pSink)
            {
                m_pViewPortManager->AddViewPortSink(pSink);
            }
            HX_RELEASE(pSink);
        }
#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */
        // Add ourselves as a media marker sink
        // TRUE adds us, FALSE removes us
        addRemoveMediaMarkerSink(TRUE);
        // Add ourselves as an error sink
        addRemoveErrorSink(TRUE);
        // QI for IHXErrorMessages - ok if not implemented
        m_pContext->QueryInterface(IID_IHXErrorMessages,
                                   (void**) &m_pErrorMessages);
    }

#ifdef _WINDOWS
    m_hHyperlinkCursor = LoadCursor(g_hInstance,
        MAKEINTRESOURCE(IDC_HANDCURSOR));
#endif
#ifdef _MACINTOSH
    const short HAND_CURSOR  = 1313;
    m_pResourceLoader = CResourceLoader::CreateInstance(g_DLLFSpec);
    m_hHyperlinkCursor = (CursHandle)m_pResourceLoader->LoadResource('CURS', HAND_CURSOR);
#endif
    
#ifdef _DEBUG
    BOOL bShowDependencies = FALSE;
    ::getBooleanPreference(m_pContext,
                           "showdependencies",
                           bShowDependencies);
    if(bShowDependencies)
    {
      // mark file boundary
#ifdef _WINDOWS
        const char* pszDepFile = "\\smildep.txt";
#else
        const char* pszDepFile = "smildep.txt";
#endif
      FILE* fp = fopen(pszDepFile, "a");
      if(fp)
      {
          fprintf(fp, "===========================\n");
          fclose(fp);
      }
    }
#endif
}

CSmilDocumentRenderer::~CSmilDocumentRenderer()
{
    HX_RELEASE(m_pSiteMgr);

    HX_DELETE(m_pRootLayout);

#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)
    closeViewports();
    removeViewports();
    HX_DELETE(m_pViewportList);
    HX_RELEASE(m_pViewPortManager);
#ifdef XXXMEH_DO_VIEWPORT_TLC
    clearViewportMap();
#endif
#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */
    HX_DELETE(m_pRegPointMap);

    HX_RELEASE(m_pPersistentLayoutStream);
    HX_RELEASE(m_pErrorMessages);

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)
    removeActiveAnimations();
#endif /* #if defined(HELIX_FEATURE_SMIL2_ANIMATION) */
    HX_DELETE(m_pActiveAnimations);
    HX_DELETE(m_pAnimationMap);
    HX_DELETE(m_pAnimSiteRedrawMap);
    HX_DELETE(m_pAnimRegionRecomputeMap);
    HX_DELETE(m_pAnimTopLayoutMap);

    deleteExternalMediaMarkerList();

    HX_RELEASE(m_pSMILDefaultNamespaceStr);

    if (m_pPrefetchTrackElementMap)
    {
      CHXMapLongToObj::Iterator i;
      for (i = m_pPrefetchTrackElementMap->Begin();
            i != m_pPrefetchTrackElementMap->End(); ++i)
      {
          CHXString* pId = (CHXString*)(*i);
          HX_DELETE(pId);
      }
      HX_DELETE(m_pPrefetchTrackElementMap);
    }

    if (m_pIndependentPlayer)
    {
      HX_DELETE(m_pIndependentPlayer);
    }
    if (m_pChildSiteInfoMap)
    {
      HX_ASSERT(m_pChildSiteInfoMap->IsEmpty()); // /Should be empty!...
      // ...but in case it's not empty, go ahead and delete its nodes:
      CHXMapLongToObj::Iterator i = m_pChildSiteInfoMap->Begin();
      for(; i != m_pChildSiteInfoMap->End(); ++i)
      {
          SMILChildSiteInfo* pChildSiteInfo = (SMILChildSiteInfo*)(*i);
          delete pChildSiteInfo;
      }
      HX_DELETE(m_pChildSiteInfoMap);
    }

#if defined(_UNIX)  &&  (!(defined(_BEOS)))  &&  defined(USE_XWINDOWS)
    CHXMapPtrToPtr::Iterator i = m_siteToXDataMap.Begin();
    for(; i != m_siteToXDataMap.End(); ++i)
    {
        XData* xData = (XData*)(*i);
      delete xData;
    }
    if (m_pVisualInfo)
    {
      XFree(m_pVisualInfo);
      m_pVisualInfo = NULL;
    }

    if (m_pDisplay && m_hHyperlinkCursor)
    {
      XFreeCursor(m_pDisplay, m_hHyperlinkCursor);
      m_hHyperlinkCursor = 0;
    }
#endif    
#if defined(_MACINTOSH)
    if (m_hHyperlinkCursor)
    {
      m_pResourceLoader->UnloadResource((Handle)m_hHyperlinkCursor);
      m_hHyperlinkCursor = NULL;
      
      HX_RELEASE(m_pResourceLoader);
    }

    if (m_bResetCursor)
    {
      ::InitCursor();
    }
#endif
    HX_RELEASE(m_pSoundLevelMutex);
    clearRendererMap();
    HX_DELETE(m_pMediaID2RendererMap);
    clearRendererSiteMap();
    HX_DELETE(m_pMediaID2RendererSiteMap);
    clearRendererSiteWatcherMap();
    HX_DELETE(m_pMediaID2RendererSiteWatcherMap);

    destroyAllRegions();

    CleanUpSiteInfoContainers();

    HX_DELETE(m_pSiteWatcherMap);
}

/*
 * IUnknown methods
 */
STDMETHODIMP
CSmilDocumentRenderer::QueryInterface(REFIID riid, void** ppvObj)
{
    if(IsEqualIID(riid, IID_IUnknown))
    {
      AddRef();
      *ppvObj = this;
      return HXR_OK;
    }
    else if(IsEqualIID(riid, IID_IHXSiteUser))
    {
      HX_ASSERT(0  &&  "Huh?!  CSmilDocumentRenderer doesn't implement IHXSiteUser");
      AddRef();
      *ppvObj = (IHXSiteUser*)this;
      return HXR_OK;
    }
    else if(IsEqualIID(riid, IID_IHXRendererAdviseSink))
    {
      AddRef();
      *ppvObj = (IHXRendererAdviseSink*)this;
      return HXR_OK;
    }
    else if(IsEqualIID(riid, IID_IHXGroupSink))
    {
      AddRef();
      *ppvObj = (IHXGroupSink*)this;
      return HXR_OK;
    }
    else if(IsEqualIID(riid, IID_IHXClientAdviseSink))
    {
      AddRef();
      *ppvObj = (IHXClientAdviseSink*)this;
      return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXPrefetchSink))
    {
      AddRef();
      *ppvObj = (IHXPrefetchSink*)this;
      return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXTrackSink))
    {
      AddRef();
      *ppvObj = (IHXTrackSink*)this;
      return HXR_OK;
    }
#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)
    else if (IsEqualIID(riid, IID_IHXViewPortSink))
    {
      AddRef();
      *ppvObj = (IHXViewPortSink*) this;
      return HXR_OK;
    }
#ifdef XXXMEH_DO_VIEWPORT_TLC
    else if (IsEqualIID(riid, IID_IHXViewPortSupplier))
    {
      AddRef();
      *ppvObj = (IHXViewPortSupplier*) this;
      return HXR_OK;
    }
#endif
#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */
#if defined(BE_SITE_SUPPLIER_20010205)
    else if (IsEqualIID(riid, IID_IHXSiteSupplier))
    {
      AddRef();
      *ppvObj = (IHXSiteSupplier*) this;
      return HXR_OK;
    }
#endif /* BE_SITE_SUPPLIER_20010205 */
    else if (IsEqualIID(riid, IID_IHXMediaMarkerSink))
    {
      AddRef();
      *ppvObj = (IHXMediaMarkerSink*) this;
      return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXEventSink))
    {
      AddRef();
      *ppvObj = (IHXEventSink*) this;
      return HXR_OK;
    }
    else if (IsEqualIID(riid, IID_IHXErrorSink))
    {
      AddRef();
      *ppvObj = (IHXErrorSink*) this;
      return HXR_OK;
    }

    *ppvObj = NULL;
    return HXR_NOINTERFACE;
}

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

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

    delete this;
    return 0;
}

HX_RESULT
CSmilDocumentRenderer::close(CSmilRenderer* pParent)
{
    MLOG_LEAKCHECK("CSmilDocumentRenderer::close() t=%lu\n", HX_GET_BETTERTICKCOUNT());

    if (m_pHandlePendingSchedulingCallback &&
      m_pHandlePendingSchedulingCallback->m_bIsCallbackPending)
    {
      m_pHandlePendingSchedulingCallback->m_bIsCallbackPending = FALSE;
      m_pScheduler->Remove(m_pHandlePendingSchedulingCallback->m_PendingHandle);
      m_pHandlePendingSchedulingCallback->m_PendingHandle = 0;
    }
    HX_RELEASE(m_pHandlePendingSchedulingCallback);

    if (m_pHyperlinkCallback &&
      m_pHyperlinkCallback->IsCallbackPending())
    {
      m_pHyperlinkCallback->SetCallbackPending(FALSE);
      m_pScheduler->Remove(m_pHyperlinkCallback->GetCallbackHandle());
      m_pHyperlinkCallback->SetCallbackHandle(0);
    }
    HX_RELEASE(m_pHyperlinkCallback);

    if (m_pDelayedRendererCloseList)
    {
      CHXSimpleList::Iterator i = m_pDelayedRendererCloseList->Begin();
      for (; i != m_pDelayedRendererCloseList->End(); ++i)
      {
          SMILDelayedRendererClose* pClose= (SMILDelayedRendererClose*)(*i);
          //HX_ASSERT(FALSE); ??
          actualRendererClosed(pClose->m_pRenderer, pClose->m_pInfo);
          HX_RELEASE(pClose->m_pRenderer);
          HX_RELEASE(pClose->m_pInfo);
          HX_DELETE(pClose);
      }
    }
    HX_DELETE(m_pDelayedRendererCloseList);

    if (m_pRootLayout && m_pRootLayout->m_pSiteUser)
    {
        IUnknown* pThisUnk = NULL;
        m_pRootLayout->m_pSiteUser->QueryInterface(IID_IUnknown, (void**) &pThisUnk);
        if (pThisUnk)
        {
            pParent->HandleRemoveLayoutSiteGroup(pThisUnk);
        }
        HX_RELEASE(pThisUnk);
    }
    m_pParent = NULL;

    if(m_pEventList)
    {
      CHXSimpleList::Iterator i = m_pEventList->Begin();
      for(; i != m_pEventList->End(); ++i)
      {
          CSmilLayoutEvent* pEvent = (CSmilLayoutEvent*)(*i);
          delete pEvent;
      }
    }
    HX_DELETE(m_pEventList);

    removeAllPlayToAssoc();

    deleteExternalMediaMarkerList();

//    HX_DELETE(m_pRootLayout);

#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)
    closeViewports();
    removeViewports();
    HX_DELETE(m_pViewportList);
#ifdef XXXMEH_DO_VIEWPORT_TLC
    clearViewportMap();
#endif
    // Remove ourselves as a view port sink
    if (m_pViewPortManager)
    {
        IHXViewPortSink* pSink = NULL;
        QueryInterface(IID_IHXViewPortSink, (void**) &pSink);
        if (pSink)
        {
            m_pViewPortManager->RemoveViewPortSink(pSink);
        }
        HX_RELEASE(pSink);
    }
    HX_RELEASE(m_pViewPortManager);
#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */
    HX_DELETE(m_pRegPointMap);

    HX_RELEASE(m_pSMILDefaultNamespaceStr);

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)
    removeActiveAnimations();
#endif /* #if defined(HELIX_FEATURE_SMIL2_ANIMATION) */
    HX_DELETE(m_pActiveAnimations);
    HX_DELETE(m_pAnimSiteRedrawMap);
    HX_DELETE(m_pAnimRegionRecomputeMap);
    HX_DELETE(m_pAnimTopLayoutMap);

    // Remove ourselves as a media marker sink
    // TRUE adds us, FALSE removes us
    addRemoveMediaMarkerSink(FALSE);
    // Are we setup as an event sink?
    if (m_bEventSinkWasSetup)
    {
        // Remove ourselves as an event sink,
        // TRUE adds us, FALSE removes us
        addRemoveEventSink(FALSE);
    }
    // Remove any renderer event sinks we have setup
    removeAllEventSinks();
    HX_DELETE(m_pEventSinkList);
    // Remove ourselve as an error sink
    addRemoveErrorSink(FALSE);

    HX_RELEASE(m_pErrorMessages);

    if (m_bRootLayoutSiteDetached)
    {
        destroyAllRegions();
    }
    
#if defined(HELIX_FEATURE_SMIL2_TRANSITIONS)
    if(m_pTransitionMap)
    {
      CHXMapStringToOb::Iterator i = m_pTransitionMap->Begin();
      for(; i != m_pTransitionMap->End(); ++i)
      {
          CSmilTransitionInfo* pTrans = (CSmilTransitionInfo*)(*i);
          delete pTrans;
      }
    }
    HX_DELETE(m_pTransitionMap);
#endif /* #if defined(HELIX_FEATURE_SMIL2_TRANSITIONS) */

    if (m_pActiveTransitions)
    {
      CHXSimpleList::Iterator i = m_pActiveTransitions->Begin();
      for (;i != m_pActiveTransitions->End(); ++i)
      {
          SMILTransitionState* pState = (SMILTransitionState*)*i;
          HX_DELETE(pState);
      }
    }
    HX_DELETE(m_pActiveTransitions);

    if(m_pGroupInfoMap)
    {
      CHXMapLongToObj::Iterator i = m_pGroupInfoMap->Begin();
      for(; i != m_pGroupInfoMap->End(); ++i)
      {
          SMILGroupInfo* pGroupInfo = (SMILGroupInfo*)(*i);
          delete pGroupInfo;
      }
    }
    HX_DELETE(m_pGroupInfoMap);

    if(m_pDeferredSourceMap)
    {
      CHXMapStringToOb::Iterator i = m_pDeferredSourceMap->Begin();
      for(; i != m_pDeferredSourceMap->End(); ++i)
      {
          SMILDeferredSourceInfo* pInfo = (SMILDeferredSourceInfo*)(*i);
          delete pInfo;
      }
    }
    HX_DELETE(m_pDeferredSourceMap);

    if(m_pDeferrededRemoveTrackMap)
    {
      CHXMapStringToOb::Iterator i = m_pDeferrededRemoveTrackMap->Begin();
      for(; i != m_pDeferrededRemoveTrackMap->End(); ++i)
      {
          CHXString* pID = (CHXString*)(*i);
          delete pID;
      }
    }
    HX_DELETE(m_pDeferrededRemoveTrackMap);

    if (m_pPausedAndDisabledIDMap)
    {
      CHXMapStringToOb::Iterator i = m_pPausedAndDisabledIDMap->Begin();
      for(; i != m_pPausedAndDisabledIDMap->End(); ++i)
      {
          CHXString* pID = (CHXString*)(*i);
          delete pID;
      }
    }
    HX_DELETE(m_pPausedAndDisabledIDMap);
    
    if (m_pPausedAndDisabledBrightnessMap)
    {
      CHXMapStringToOb::Iterator i =
            m_pPausedAndDisabledBrightnessMap->Begin();
      for (; i != m_pPausedAndDisabledBrightnessMap->End(); ++i)
      {
          float* pf = (float*)(*i);
          delete pf;
      }
    }
    HX_DELETE(m_pPausedAndDisabledBrightnessMap);

    if(m_pStatusMessage)
    {
      m_pStatusMessage->SetStatus(NULL);
    }
    HX_RELEASE(m_pStatusMessage);

    if (m_pPlayerResumeStack)
    {
      CSmilPlayerResumePos* pResumePos =
            (CSmilPlayerResumePos*)m_pPlayerResumeStack->Pop();
      while (pResumePos)
      {
          delete pResumePos;
          pResumePos = (CSmilPlayerResumePos*)m_pPlayerResumeStack->Pop();
      }
    }
    HX_DELETE(m_pPlayerResumeStack);

    if (m_bRootLayoutSiteDetached)
    {
        CleanUpSiteInfoContainers();

        HX_DELETE(m_pSiteWatcherMap);
    }

    HX_DELETE(m_pSmilParser);
    HX_VECTOR_DELETE(m_pFragment);
    HX_RELEASE(m_pScheduler);
    HX_RELEASE(m_pContext);
    clearRendererMap();
    HX_DELETE(m_pMediaID2RendererMap);
    clearRendererSiteMap();
    HX_DELETE(m_pMediaID2RendererSiteMap);
    clearRendererSiteWatcherMap();
    HX_DELETE(m_pMediaID2RendererSiteWatcherMap);

#ifdef _MACINTOSH
    if (m_bResetCursor)
    {
      m_bResetCursor = FALSE;
      ::InitCursor();
    }
#endif

    // Set the flag indicating that close has been called
    m_bCloseCalled = TRUE;

    // Release the sound level mutex
    HX_RELEASE(m_pSoundLevelMutex);

    return HXR_OK;
}

HX_RESULT
CSmilDocumentRenderer::onHeader(IHXValues* pHeader)
{
    m_bSMILPresentationHasEnded = FALSE;

    m_pRegionMap = new CHXMapStringToOb;
    m_pSiteInfoByRendererMap = new CHXMapPtrToPtr;

    m_pSmilParser = new CSmilParser(m_pContext);
    m_pSmilParser->init();
    
    if (m_pPersistentParentRenderer &&
      HXR_OK == m_pPersistentParentRenderer->GetElementProperties(m_uPersistentGroupID,
                                                    m_uPersistentTrackID,
                                                    m_pPersistentProperties))
    {
      m_pSmilParser->InitPersistent(m_ulPersistentComponentID, m_pPersistentProperties);
    }

    // Create a root-layout object
    HX_DELETE(m_pRootLayout);
    m_pRootLayout = new CSmilBasicRootLayout();

    // The SMIL default namespace is stored in the
    // "MimeType" property of the stream header
    if (pHeader)
    {
        HX_RELEASE(m_pSMILDefaultNamespaceStr);
      if (m_pParent  &&  !m_pParent->isStreamProxiedByOtherRenderer())
      {
          pHeader->GetPropertyCString("MimeType", m_pSMILDefaultNamespaceStr);
      }
      else // /Use value passed in via IHXSmilToSmilRendererCommunicator
      {
          HX_ASSERT(m_pParent);
          if (m_pParent  &&  m_pParent->m_pVersionNamespaceFromProxyRenderer)
          {
            m_pSMILDefaultNamespaceStr =
                  m_pParent->m_pVersionNamespaceFromProxyRenderer;
            m_pSMILDefaultNamespaceStr->AddRef();
          }
      }
    }

    // Get the group we are starting on
    m_usAnimBaseGroupIndex = getCurrentGroup();

    return HXR_OK;
}

HX_RESULT
CSmilDocumentRenderer::getErrorInfo(REF(UINT32) ulLineNumber,
                            REF(UINT32) ulColumnNumber,
                            REF(IHXBuffer*) pErrorText)
{
    ulLineNumber = m_pSmilParser->m_ulErrorLineNumber;
    ulColumnNumber = m_pSmilParser->m_ulErrorColumnNumber;
    pErrorText = m_pSmilParser->m_pErrorText;
    if(pErrorText)
    {
      pErrorText->AddRef();
    }

    return HXR_OK;
}

HX_RESULT CSmilDocumentRenderer::getErrorInterface(REF(IHXErrorMessages*) rpErrMsgs)
{
    HX_RESULT retVal = HXR_OK;

    if (m_pErrorMessages)
    {
        HX_RELEASE(rpErrMsgs);
        rpErrMsgs = m_pErrorMessages;
        rpErrMsgs->AddRef();
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT
CSmilDocumentRenderer::onPacket(IHXBuffer* pBuffer, BOOL bIsLast)
{
    HX_RESULT rc = HXR_OK;

    BOOL bDumpToFile = FALSE;
    ::getBooleanPreference(m_pContext,
                           "NonObviousKeyName",
                           bDumpToFile);
    BOOL bShowPacketBoundaries = FALSE;
    ::getBooleanPreference(m_pContext,
                           "NonObviousKeyNameOption1",
                           bShowPacketBoundaries);
    if (bDumpToFile)
    {
#ifdef _WINDOWS
        const char* pszFile = "c:\\smildump.txt";
#else
        const char* pszFile = "smildump.txt";
#endif
        FILE* pFoo = fopen(pszFile, m_ulPktnum > 0L ? "a" : "w");
        if (pFoo)
        {
            char* pBuf = (char*) pBuffer->GetBuffer();
            if(pBuf && strlen(pBuf) > 0)
            {
                if (bShowPacketBoundaries)
                {
                    fprintf(pFoo, "[[[packet # %lu]]]:{{{", m_ulPktnum);
                }
                fwrite(pBuf, 1, pBuffer->GetSize(), pFoo);
                if (bShowPacketBoundaries)
                {
                    fprintf(pFoo, "}}}\n");
                }
            }
            else
            {
                if (bShowPacketBoundaries)
                {
                    fprintf(pFoo, "\n[[[Packet %lu empty in onPacket()]]]\n",
                            m_ulPktnum);
                }
            }
        }
        fclose(pFoo);
        if (bIsLast)
            m_ulPktnum = 0L;
        else
            m_ulPktnum++;
    }
    rc = m_pSmilParser->parse(pBuffer, bIsLast);

    return rc;
}

HX_RESULT CSmilDocumentRenderer::setDocument(const char* pFragment)
{
    BOOL bDisableValidation = FALSE;
    ::getBooleanPreference(m_pContext,
                           "DisableSMILValidation",
                           bDisableValidation);
    HX_RESULT rc = HXR_OK;
    if (!bDisableValidation)
    {
#if defined(HELIX_FEATURE_SMIL2_VALIDATION)
        rc = m_pSmilParser->validateAgainstDTD();
#endif /* #if defined(HELIX_FEATURE_SMIL2_VALIDATION) */
    }
    if (SUCCEEDED(rc))
    {
        rc = m_pSmilParser->createElements();
        if(HXR_OK == rc)
        {
            rc = m_pSmilParser->setAllElementHandlers(this);
            rc = handleElements();

            if (SUCCEEDED(rc))
            {
                rc = postParseSetup();
            }

            // The player may need to know about the URLs 
            // we will hurl in advance. Therefore, we will
            // throw all the potential URLs we may throw
            // to the IHXHyperNavigateHint interface.
            SendHyperlinkHints();

            // Handle any references to external media marker files
            m_pSmilParser->handleAllXMMFReferences();

            BOOL bContainsSource = m_pSmilParser->presentationContainsASource();
          BOOL bContainsInitiallyScheduledTrack =
                m_pSmilParser->presentationContainsInitiallyScheduledTrack();
            ULONG32 ulDurIfNoInitialTracksScheduled =
                    m_pSmilParser->presentationDurIfNoInitialTracksScheduled();

            // If we have a source in the tree but no source has
            // been scheduled, then we'll go ahead and create a group
            // XXXMEH - temporary fix. The core does not call DoneChangeLayout
            // on the TLC if there is no source scheduled. The real fix for
            // this should be done in the core/TLC but may be involved.
            // This fix should do in the meantime.
            if (!bContainsSource || !bContainsInitiallyScheduledTrack)
            {
                setupDummySource();
            }

            IHXPlayer* pPlayer = m_pParent->getPlayer();
            IHXGroupManager* pMgr = 0;

            if(HXR_OK == pPlayer->QueryInterface(IID_IHXGroupManager, (void**)&pMgr))
            {
                if(pFragment)
                {
                    m_pFragment = new_string(pFragment);
                    m_bSettingFragment = TRUE;
                    m_nFragmentTracks = 0;
                    UINT16 uFragmentGroup = 
                        m_pSmilParser->getFragmentGroup(m_pFragment);
                    pMgr->SetCurrentGroup(uFragmentGroup);
                }
                else if (bContainsInitiallyScheduledTrack)
                {
                if (m_pParent->m_bUseNestedMeta && m_uCurrentGroupIndex == -1)
                {
                  pMgr->GetCurrentGroup((UINT16&)m_uCurrentGroupIndex);
                }
                    else
                    {
                        pMgr->SetCurrentGroup(0);
                    }
                }
            // /No sources were scheduled to play, so we have to manually
            // keep the presentation open for others like animate elements
            // (which can animate regions and thus no sources are needed
            // for a SMIL presentation to be (arguably) interesting.  Also,
            // a track (source) could begin on an event during playback:
                else if (ulDurIfNoInitialTracksScheduled>0  &&
                  SMILTIME_INVALID != ulDurIfNoInitialTracksScheduled)
                {
                    HX_ASSERT(m_pPersistentLayoutStream);
                    if (m_pPersistentLayoutStream)
                    {
                        IHXValues* pStreamProps = NULL;
                        if(HXR_OK == m_pPersistentLayoutStream->GetProperties(pStreamProps))
                        {
                            pStreamProps->SetPropertyULONG32("duration",
                                    ulDurIfNoInitialTracksScheduled);
                            m_pPersistentLayoutStream->SetProperties(pStreamProps);
                            HX_RELEASE(pStreamProps);
                        }
                    }
                    // XXXMEH - since there are no sources, the only sites will
                    // be region sites, so unless they have an explicit
                    // showBackground="whenActive", then we need to show them here
                    showAllSites(m_pRootLayout, TRUE);
                }

                pMgr->Release();
            }
        }
    }

    return rc;
}

HX_RESULT CSmilDocumentRenderer::setupDummySource()
{
    HX_RESULT retVal = HXR_FAIL;

    // Our dummy source is a <brush>, so if <brush> is
    // not supported, then we fail.
#if defined(HELIX_FEATURE_SMIL2_BRUSH)
    if (m_pParent)
    {
        // Get the player
        IHXPlayer* pPlayer = m_pParent->getPlayer();
        if (pPlayer)
        {
            // Get the group manager
            IHXGroupManager* pMgr = NULL;
            pPlayer->QueryInterface(IID_IHXGroupManager, (void**) &pMgr);
            if (pMgr)
            {
                // Get the current group
                UINT16 usGroup = 0;
              if (m_pParent->m_bUseNestedMeta && m_uCurrentGroupIndex == -1)
              {
                pMgr->GetCurrentGroup(usGroup);
              }
                // Get an IHXCommonClassFactory
                IHXCommonClassFactory* pFactory = m_pParent->getFactory();
                if (pFactory)
                {
                    // Create an IHXValues for the track properties
                    IHXValues* pValues = NULL;
                    pFactory->CreateInstance(CLSID_IHXValues, (void**) &pValues);
                    if (pValues)
                    {
                        // Create an IHXValues for the 822 headers
                        IHXValues* pReqHeaders = NULL;
                        pFactory->CreateInstance(CLSID_IHXValues, (void**) &pReqHeaders);
                        if (pReqHeaders)
                        {
                            // Create the base of our src string
                            CHXString cSrc("data:text/brush;base64,");
                            // Now we need to create our brush string
                            CHXString cBrushText("<brush color=\"black\"/>");
                            // 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;
                            }
                            HX_VECTOR_DELETE(pStr64);
                            // Assign the "url" property
                            setProperty(pValues, "url", (const char*) cSrc);
                            // Assign the "id" property
                            setProperty(pValues, "id", "dummy_brush_07241965");
                            // Assign the delay property
                            pValues->SetPropertyULONG32("delay", 0);
                            // Assign the duration property to 1ms
                            pValues->SetPropertyULONG32("duration", 1);
                            
                            //Set the PersistentComponentID.
                            pValues->SetPropertyULONG32("PersistentComponentID", m_ulPersistentComponentID);
                            //Tell the core this we don't want any
                            //TrackAdded callbacks because we don't
                            //have any groups.
                            pValues->SetPropertyULONG32("NoGroupsPresent", 1);
                            // Add the "NullBrush" property to the request headers
                            setProperty(pReqHeaders, "NullBrush", "1");
                            // Get the current group
                            IHXGroup* pGroup = NULL;
                            pMgr->GetGroup(usGroup, pGroup);
                            if (pGroup)
                            {
                                // Get the IHXGroup2 interface
                                IHXGroup2* pGroup2 = NULL;
                                pGroup->QueryInterface(IID_IHXGroup2, (void**) &pGroup2);
                                if (pGroup2)
                                {
                                    // Add the track
                                    pGroup2->AddTrack2(pValues, pReqHeaders);
                                }
                                HX_RELEASE(pGroup2);
                            }
                            HX_RELEASE(pGroup);
                        }
                        HX_RELEASE(pReqHeaders);
                    }
                    HX_RELEASE(pValues);
                }
            }
            HX_RELEASE(pMgr);
        }
    }
#endif /* #if defined(HELIX_FEATURE_SMIL2_BRUSH) */

    return retVal;
}

HX_RESULT
CSmilDocumentRenderer::handleElements()
{
    HX_RESULT rc = HXR_OK;

    if(m_pSmilParser)
    {
      //if(m_ulParseResult != HXR_STREAM_DONE)
      //{
          while(HXR_OK == rc)
          {
            rc = m_pSmilParser->handleNextElement(this);
          }
          m_ulParseResult = rc;
      //}
    }

    return HXR_OK;
}

HX_RESULT CSmilDocumentRenderer::postParseSetup()
{
    HX_RESULT retVal = HXR_OK;

    // There are several things we cannot setup until
    // we have received all of the CSmilElement's handleElement()
    // methods up front.
    //
    // The first is that we want to set the SiteNeverBlts property
    // of the site, but in order to do that, we need to know all
    // the sources and all the animate elements.
    if (m_pRegionMap)
    {
        // Run through the map
        POSITION pos = m_pRegionMap->GetStartPosition();
        while (pos)
        {
            const char* pszKey = NULL;
            void*       pVal   = NULL;
            m_pRegionMap->GetNextAssoc(pos, pszKey, pVal);
            if (pVal)
            {
                // Get the region pointer
                CSmilBasicRegion* pRegion = (CSmilBasicRegion*) pVal;
                // Check if this region's site should
                // have its SiteNeverBlts property set to "1".
                // It should if:
                // a) the backgroundColor is transparent
                // b) backgroundColor is never animated
                // c) backgroundColor is never overridden in a source
                if (isTransparent(pRegion->m_ulBackgroundColor) &&
                    !isAttributeAnimated((const char*) pRegion->m_region,
                                         kAttrNameBackgroundColor) &&
                    !isRegionBackgroundColorOverridden(pRegion))
                {
                    setSiteProperty(pRegion->m_pSite, "SiteNeverBlts", "1");
                }
            }
        }
    }

    return retVal;
}

HX_RESULT
CSmilDocumentRenderer::parseDimension(const char* pDimensionString,
                              REF(UINT32) ulValue,
                              REF(BOOL) bIsPercent)
{
    HX_RESULT rc = HXR_OK;

    if(!pDimensionString || strlen(pDimensionString) == 0)
    {
      ulValue = 0L;
      //[SMIL 1.0 compliance] Helps fix PR 16542.  The caller of this
      // function should look at this return value to determine whether
      // or not the string was empty (and thus 0 is returned in ulValue) or
      // whether "0" or "0%" IS the string (and thus 0 is returned in
      // ulValue along with HXR_OK):
      rc = HXR_FAIL;
    }
    else
    {
      char* pEndPtr = 0;
      ulValue = (UINT32)strtod(pDimensionString, &pEndPtr);
      if(pEndPtr && strcmp(pEndPtr, "%") == 0)
      {
          bIsPercent = TRUE;
      }
      else
      {
          bIsPercent = FALSE;
      }
    }
    return rc;
}

HX_RESULT CSmilDocumentRenderer::computeBoxDimension(CSmilBasicBox* pBox,
                                                     BoxDimension   eDim)
{
    HX_RESULT retVal = HXR_OK;

    if (pBox)
    {
        // Clear the flag that says whether or
        // not to compute our dimension based on
        // our children
        BOOL bPostResolve = FALSE;
        // Have we already resolved our dimension?
        if (!pBox->isResolved(eDim))
        {
            // We have NOT resolved our dimension yet,
            // so we need to attempt to resolve it
            HX_RESULT rv = pBox->computeDimension(eDim);
            if (FAILED(rv))
            {
                // We could not resolve our dimension
                // so we will have to try and resolve it
                // from our children.
                bPostResolve = TRUE;
            }
        }

        if (pBox->m_pChildList)
        {
            // Now we run through our children, computing
            // their dimensions
            LISTPOSITION pos = pBox->m_pChildList->GetHeadPosition();
            while (pos && SUCCEEDED(retVal))
            {
                CSmilBasicBox* pListBox = (CSmilBasicBox*) pBox->m_pChildList->GetNext(pos);
                if (pListBox)
                {
                    retVal = computeBoxDimension(pListBox, eDim);
                }
            }
            if (SUCCEEDED(retVal) && bPostResolve)
            {
                retVal = pBox->resolveFromChildren(eDim);
            }
        }
    }

    return retVal;
}

void CSmilDocumentRenderer::clearResolvedFlags(CSmilBasicBox* pBox)
{
    if (pBox)
    {
        // Clear the flags for this box
        pBox->m_bWidthResolved  = FALSE;
        pBox->m_bHeightResolved = FALSE;
        // Recurse through the children
        if (pBox->m_pChildList)
        {
            LISTPOSITION pos = pBox->m_pChildList->GetHeadPosition();
            while (pos)
            {
                CSmilBasicBox* pListBox =
                    (CSmilBasicBox*) pBox->m_pChildList->GetNext(pos);
                clearResolvedFlags(pListBox);
            }
        }
    }
}

void CSmilDocumentRenderer::resetSites(CSmilBasicBox* pBox, BOOL bOptimizeRedraws)
{
    if (pBox)
    {
        IHXSite* pSite     = pBox->m_pSite;
        BOOL      bIsRegion = FALSE;
        if (pBox->m_pParent)
        {
            // This box has a parent, so it has to be a region
            bIsRegion = TRUE;
        }
        if (pSite)
        {
            // Get the current position of the site
            HXxPoint cPos = {0, 0};
            pSite->GetPosition(cPos);
            // Set the new position of the site
            HXxPoint cNewPos = {pBox->m_Rect.left, pBox->m_Rect.top};
#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)
#if defined(XXXMEH_DO_VIEWPORT_TLC) && defined(_WINDOWS)
            if (pBox->m_pParent && !pBox->m_pParent->m_pParent)
            {
                // This has to be a region, so cast
                CSmilBasicRegion* pReg = (CSmilBasicRegion*) pBox;
                if (!pReg->m_bUnderRootLayout)
                {
                    cNewPos.x += GetSystemMetrics(SM_CXFIXEDFRAME);
                    cNewPos.y += GetSystemMetrics(SM_CYFIXEDFRAME) + GetSystemMetrics(SM_CYCAPTION);
                }
            }
#endif
#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */
            // If the new position is different from
            // the old position, then reset the position
            if (cPos.x != cNewPos.x ||
                cPos.y != cNewPos.y)
            {
                if (bIsRegion)
                {
                    CSmilBasicRegion* pReg = (CSmilBasicRegion*) pBox;
                    MLOG_LAYOUT(m_pErrorMessages,
                                "CSmilDocumentRenderer::resetSites() region=%s site->SetPosition(%ld,%ld)\n",
                                (const char*) pReg->m_region, cNewPos.x, cNewPos.y);
                }
                pSite->SetPosition(cNewPos);
                // Optimize out any site redraws if necessary
                if (bOptimizeRedraws)
                {
                    // If we are calling SetPosition() on this
                    // site, then the site code will automatically
                    // force a redraw on this site. Therefore, if
                    // we were planning on doing a ForceRedraw() on
                    // this site as a result of some animation, then
                    // it's no longer necessary
                    removePendingAnimationRedraw(pSite);
                }
            }
            // Get the current size of the site
            HXxSize cSize = {0, 0};
            pSite->GetSize(cSize);
            // Set the new size of the site
            HXxSize cNewSize = {HXxRECT_WIDTH(pBox->m_Rect),
                                HXxRECT_HEIGHT(pBox->m_Rect)};
            // If this is different from the size
            // of the rect, then resize
            if (cSize.cx != cNewSize.cx ||
                cSize.cy != cNewSize.cy)
            {
                if (bIsRegion)
                {
                    CSmilBasicRegion* pReg = (CSmilBasicRegion*) pBox;
                    MLOG_LAYOUT(m_pErrorMessages,
                                "CSmilDocumentRenderer::resetSites() region=%s site->SetSize(%ld,%ld)\n",
                                (const char*) pReg->m_region, cNewSize.cx, cNewSize.cy);
                }
                pSite->SetSize(cNewSize);
                // Optimize out any site redraws if necessary
                if (bOptimizeRedraws)
                {
                    // If we are calling SetSize() on this
                    // site, then the site code will automatically
                    // force a redraw on this site. Therefore, if
                    // we were planning on doing a ForceRedraw() on
                    // this site as a result of some animation, then
                    // it's no longer necessary
                    removePendingAnimationRedraw(pSite);
                }
                // If this is a region, then we need to
                // resize any renderers which are currently
                // playing as well.
                if (bIsRegion)
                {
                    CSmilBasicRegion* pRegion = (CSmilBasicRegion*) pBox;
                    resetRendererSites(pRegion);
                }
            }
        }
        // Recurse through the children
        if (pBox->m_pChildList)
        {
            LISTPOSITION pos = pBox->m_pChildList->GetHeadPosition();
            while (pos)
            {
                CSmilBasicBox* pListBox =
                    (CSmilBasicBox*) pBox->m_pChildList->GetNext(pos);
                resetSites(pListBox, bOptimizeRedraws);
            }
        }
    }
}

void CSmilDocumentRenderer::resizeTopLevelBox(CSmilBasicBox* pBox,
                                              HXxSize        cOrigSize,
                                              HXxSize        cSize)
{
    // A box is only top-level if it has no parent box
    if (pBox && !pBox->m_pParent)
    {
        // Only resize if the current size is different
        if (cSize.cx != HXxRECT_WIDTH(pBox->m_Rect) ||
            cSize.cy != HXxRECT_HEIGHT(pBox->m_Rect))
        {
            MLOG_LAYOUT(m_pErrorMessages,
                        "CSmilDocumentRenderer::resizeTopLevelBox(0x%08x,(%ld,%ld),(%ld,%ld))\n",
                        pBox, cOrigSize.cx, cOrigSize.cy, cSize.cx, cSize.cy);
            // Clear the resolved flags
            clearResolvedFlags(pBox);
            // Normally, we have to resolve a top-level box's
            // dimensions just like anything else, but top-level
            // boxes can be explicitly resized by the user by
            // resizing the window. Therefore, we explicitly
            // set the rectangle for this top-level box
            pBox->m_Rect.left   = 0;
            pBox->m_Rect.top    = 0;
            if (pBox->m_eResizeBehavior == ResizePercentOnly ||
                (pBox->m_eResizeBehavior == ResizeZoom && m_bDoNotZoom))
            {
                // If we are NOT zooming, then we simply use
                // the new size. Also, this could be called in
                // the case where we are using the media size
                // to figure out the root-layout size. In that
                // case, we don't zoom either.
                pBox->m_Rect.right  = cSize.cx;
                pBox->m_Rect.bottom = cSize.cy;
            }
            else
            {
                // If we are zooming, then we re-calculate 
                // using the original top-level size and then
                // scale the whole thing.
                pBox->m_Rect.right  = cOrigSize.cx;
                pBox->m_Rect.bottom = cOrigSize.cy;
            }
            // We force resolution of the top-level site
            pBox->m_bWidthResolved  = TRUE;
            pBox->m_bHeightResolved = TRUE;
            // Compute left and right of the region rects
            computeBoxDimension(pBox, BoxDimensionWidth);
            // Compute top and bottom of the region rects
            computeBoxDimension(pBox, BoxDimensionHeight);
            // If we have zoom resize behavior, then we need to
            // compute and set the zoom scale factors and then
            // scale the rects
            if (pBox->m_eResizeBehavior == ResizeZoom && !m_bDoNotZoom)
            {
                // Compute the x scale factor
                double dScaleX = 1.0;
                if (cOrigSize.cx > 0)
                {
                    dScaleX = ((double) cSize.cx) / ((double) cOrigSize.cx);
                }
                // Compute the y scale factor
                double dScaleY = 1.0;
                if (cOrigSize.cy > 0)
                {
                    dScaleY = ((double) cSize.cy) / ((double) cOrigSize.cy);
                }
                // Set all the scale factors
                zoomRect(pBox, dScaleX, dScaleY);
            }
            // Turn on site composition if it's not already on
            // We don't want to turn it off if it's already on
            BOOL bInCompositionMode = isSiteCompositionModeON();
            if (!bInCompositionMode)
            {
                turnSiteCompositionModeON();
            }
            // Lock the composition if necessary
            BOOL bIsLocked = isSiteCompositionLocked();
            if (!bIsLocked)
            {
                lockSiteComposition();
            }
            // Now run through and adjust the size of the sites
            resetSites(pBox, FALSE);
            // Unlock the composition if necessary
            if (!bIsLocked)
            {
                unlockSiteComposition();
            }
            // If we're supposed to turn composition mode off,
            // then do it now. Otherwise, if we were already
            // in composition mode, then blt the site composition
            if (!bInCompositionMode)
            {
                turnSiteCompositionModeOFF();
            }
        }
    }
}

void CSmilDocumentRenderer::zoomRect(CSmilBasicBox* pBox,
                                     double         dScaleX,
                                     double         dScaleY)
{
    if (pBox)
    {
        // Set the zoom scale factors for this box
        pBox->m_dZoomScaleFactorX = dScaleX;
        pBox->m_dZoomScaleFactorY = dScaleY;
        // Save off the unscaled rect
        pBox->m_RectNoZoom = pBox->m_Rect;
        // Apply these scale factors to the rect (with rounding)
        // Make sure we don't round the left/right and top/bottom
        // independently - this can result in spurious width/height
        // changes, even when the actual floating point scaled width
        // and height doesn't change.
        double dWidth       = (double) HXxRECT_WIDTH(pBox->m_Rect);
        double dHeight      = (double) HXxRECT_HEIGHT(pBox->m_Rect);
        pBox->m_Rect.left   = (INT32) floor((((double) pBox->m_Rect.left)   * dScaleX) + 0.5);
        pBox->m_Rect.top    = (INT32) floor((((double) pBox->m_Rect.top)    * dScaleY) + 0.5);
        INT32 lScaledWidth  = (INT32) floor(dWidth * dScaleX + 0.5);
        INT32 lScaledHeight = (INT32) floor(dHeight * dScaleY + 0.5);
        pBox->m_Rect.right  = pBox->m_Rect.left + lScaledWidth;
        pBox->m_Rect.bottom = pBox->m_Rect.top  + lScaledHeight;
        // Run through any renderer site children and update
        // the scale factors of their site watchers
        if (pBox->m_pChildRendererSiteList &&
            m_pSiteWatcherMap)
        {
            LISTPOSITION pos = pBox->m_pChildRendererSiteList->GetHeadPosition();
            while (pos)
            {
                IHXSite* pRndSite = (IHXSite*) pBox->m_pChildRendererSiteList->GetNext(pos);
                if (pRndSite)
                {
                    // Look up the site watcher in the site watcher map
                    void* pVoid = NULL;
                    if (m_pSiteWatcherMap->Lookup((void*) pRndSite, pVoid) && pVoid)
                    {
                        CSmilSiteWatcher* pWatcher = (CSmilSiteWatcher*) pVoid;
                        pWatcher->SetZoomScaleFactors(dScaleX, dScaleY);
                    }
                }
            }
        }
        // Is this a region? If it has a parent, then it 
        // must be a region
        if (pBox->m_pParent)
        {
            // XXXMEH - these are holdover members which currently
            // need to be set, but should be taken out soon
            CSmilBasicRegion* pRegion = (CSmilBasicRegion*) pBox;
            pRegion->m_rect           = pRegion->m_Rect;
            pRegion->m_originalRect   = pRegion->m_Rect;
        }
        // Recurse through the children
        if (pBox->m_pChildList)
        {
            LISTPOSITION pos = pBox->m_pChildList->GetHeadPosition();
            while (pos)
            {
                CSmilBasicBox* pListBox =
                    (CSmilBasicBox*) pBox->m_pChildList->GetNext(pos);
                zoomRect(pListBox, dScaleX, dScaleY);
            }
        }
    }
}

void CSmilDocumentRenderer::recomputeBoxLayout(CSmilBasicBox* pBox, BOOL bOptimizeRedraws)
{
    if (pBox)
    {
        // Clear the resolved flags
        clearResolvedFlags(pBox);
        // Compute left and right of the region rects
        computeBoxDimension(pBox, BoxDimensionWidth);
        // Compute top and bottom of the region rects
        computeBoxDimension(pBox, BoxDimensionHeight);
        // We may currently be zoomed. If so, then
        // adjust for the zoom
        if (pBox->m_eResizeBehavior == ResizeZoom && !m_bDoNotZoom &&
            (pBox->m_dZoomScaleFactorX != 1.0 ||
             pBox->m_dZoomScaleFactorY != 1.0))
        {
            // Set all the scale factors
            zoomRect(pBox, pBox->m_dZoomScaleFactorX, pBox->m_dZoomScaleFactorY);
        }
        // Now run through and adjust the size of the sites
        resetSites(pBox, bOptimizeRedraws);
    }
}

HX_RESULT
CSmilDocumentRenderer::handleRegion(CSmilRegion* pElement)
{
    HX_RESULT retVal = HXR_OK;

    if (pElement)
    {
        // Create the CSmilBasicRegion element
        CSmilBasicRegion* pRegion = new CSmilBasicRegion(pElement);
        if (pRegion)
        {
            // Set the parent child relationships
            retVal = setRegionParentChild(pRegion);
            if (SUCCEEDED(retVal))
            {
                // Add this region to the map - m_pRegionMap is
                // the "owner" of the CSmilBasicRegion, so when it
                // goes away, all of the CSmilBasicRegion's go away.
                if (m_pRegionMap)
                {
                    m_pRegionMap->SetAt((const char*) pElement->m_pNode->m_id,
                                        (void*) pRegion);
                }
            }
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT
CSmilDocumentRenderer::handleRegPoint(CSmilRegPoint* pElement)
{
    HX_RESULT retVal = HXR_OK;

    if (pElement && pElement->m_pNode)
    {
        // Does the map already exist
        if (!m_pRegPointMap)
        {
            // Create the map
            m_pRegPointMap = new CHXMapStringToOb();
        }
        if (m_pRegPointMap)
        {
            // Add this regPoint to the map
            m_pRegPointMap->SetAt((const char*) pElement->m_pNode->m_id,
                                  (void*) pElement);
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)

HX_RESULT
CSmilDocumentRenderer::handleViewport(CSmilViewport* pElement)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pElement)
    {
        // Does the viewport list exist yet?
        if (!m_pViewportList)
        {
            // Create the list
            m_pViewportList = new CHXSimpleList();
        }
        if (m_pViewportList)
        {
            // Create a viewport object
            CSmilBasicViewport* pPort = new CSmilBasicViewport(pElement);
            if (pPort)
            {
                // Add it to the list
                m_pViewportList->AddTail((void*) pPort);
                // Clear the return value
                retVal = HXR_OK;
            }
        }
    }

    return retVal;
}

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

#if defined(HELIX_FEATURE_SMIL2_TRANSITIONS)

HX_RESULT CSmilDocumentRenderer::handleTransition(CSmilTransition* pTrans)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pTrans)
    {
        // Create the transition map if necessary
        if (!m_pTransitionMap)
        {
            m_pTransitionMap = new CHXMapStringToOb();
        }
        if (m_pTransitionMap)
        {
            // Create a transition info object
            CSmilTransitionInfo* pInfo = new CSmilTransitionInfo(pTrans, m_pContext);
            if (pInfo)
            {
                // Put this info object in the map
                m_pTransitionMap->SetAt((const char*) pTrans->m_pNode->m_id,
                                        (void*) pInfo);
                // Clear the return value
                retVal = HXR_OK;
            }
        }
    }

    return retVal;
}

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

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)

HX_RESULT CSmilDocumentRenderer::handleAnimate(CSmilAnimateElement* pAnimate)
{
    MLOG_ANIM(m_pErrorMessages,"CSmilDocumentRenderer::handleAnimate() id=%s tel=%s t=%lu\n",
                (const char*) pAnimate->m_pNode->m_id,
                (const char*) *pAnimate->m_pTargetElementID,
                pAnimate->m_ulDelay);
    HX_RESULT retVal = HXR_OK;

    if (pAnimate)
    {
        // XXXMEH - try here to schedule other animations and
        // elements that may be using this animate element as
        // a sync-arc. This should get us through interop, but this
        // needs to be re-examined later.
        m_pSmilParser->durationResolved(pAnimate->m_pNode->m_id,
                                        pAnimate->m_ulActiveDuration);
        // Add this to the animation map
        if (!m_pAnimationMap)
        {
            m_pAnimationMap = new CHXMapStringToOb();
        }
        if (m_pAnimationMap)
        {
            m_pAnimationMap->SetAt(pAnimate->m_pNode->m_id, (void*) pAnimate);
        }
        // Update the duration contributed to
        // only by animations
        UINT32 ulAnimEnd = pAnimate->m_ulDelay + pAnimate->m_ulActiveDuration;
        if (ulAnimEnd > m_ulAnimDuration)
        {
            m_ulAnimDuration = ulAnimEnd;
        }
        // See if we've already scheduled an event for
        // this animate element
        CSmilAnimateEvent* pAnimEvent = getAnimateEvent(pAnimate);
        if (pAnimEvent)
        {
            // We HAVE already scheduled an event
            // for this animate element. This is most likely
            // an interactive animation which can begin
            // several times. We should adjust the time for
            // this event instead of adding a new event.
            //
            // Remove the event from the queue
            removeEvent(pAnimEvent);
            // Update the event time
            pAnimEvent->m_ulEventTime = pAnimate->m_ulDelay;
            // Re-insert into the queue with the new time
            insertEvent(pAnimEvent);
        }
        else
        {
            // We have NOT already scheduled an event
            // for this animate element
            //
            // Schedule the begin animation event
            UINT16 usGroupIndexShifted = m_usAnimBaseGroupIndex + pAnimate->m_pNode->m_nGroup;
            CSmilAnimateEvent* pEvent = new CSmilAnimateEvent(usGroupIndexShifted,
                                                              pAnimate->m_ulDelay,
                                                              FALSE,
                                                              pAnimate,
                                                              this,
                                                              m_pSmilParser);
            if (pEvent)
            {
                // Put the event only the timeline
                insertEvent(pEvent);
            }
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

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

HX_RESULT CSmilDocumentRenderer::handleRemoveTimeUpdate(CSmilElement* pElement,
                                                        UINT32        ulRemoveTime)
{
    MLOG_EVENT(m_pErrorMessages,
               "handleRemoveTimeUpdate(0x%08x,%lu)\n\tmediaID=%s\n\ttick=%lu\n",
               pElement, ulRemoveTime,
               (const char*) pElement->m_pNode->m_id,
               HX_GET_BETTERTICKCOUNT());
    HX_RESULT retVal = HXR_OK;

    if (pElement && pElement->m_pNode &&
        m_pEventList && m_pEventList->GetCount() > 0)
    {
        // Search through the event queue to find
        // the hide event for this element.
        CHXSimpleList cTmp;
        const char*   pszID = (const char*) pElement->m_pNode->m_id;
        LISTPOSITION  pos   = m_pEventList->GetHeadPosition();
        while (pos)
        {
            BOOL              bRemoved   = FALSE;
            CSmilLayoutEvent* pListEvent = (CSmilLayoutEvent*) m_pEventList->GetAt(pos);
            if (pListEvent && pListEvent->m_type == CSmilLayoutEvent::eHideSite)
            {
                CSmilShowSiteEvent* pListHide = (CSmilShowSiteEvent*) pListEvent;
                if (!strcmp(pszID, pListHide->getMediaID()) &&
                    pListHide->m_ulEventTime != ulRemoveTime)
                {
                    MLOG_EVENT(m_pErrorMessages,
                               "\tRemoving event from list (%s,%lu,%u,%lu,%lu,%s,%s) tick=%lu\n",
                               pListHide->getEventTypeName(),
                               pListHide->m_ulEventTime,
                               pListHide->m_uGroupIndex,
                               pListHide->m_bOnlyHideSite,
                               pListHide->m_bIgnorEvent,
                               pListHide->getMediaID(),
                               pListHide->getRegionID(),
                               HX_GET_BETTERTICKCOUNT());
                    // Remove the event from the list
                    pos = m_pEventList->RemoveAt(pos);
                    // Set the flag
                    bRemoved = TRUE;
                    // Save onto the temporary list
                    cTmp.AddTail((void*) pListHide);
                }
            }
            if (!bRemoved)
            {
                m_pEventList->GetNext(pos);
            }
        }
        // Did we remove any events?
        if (cTmp.GetCount() > 0)
        {
            // Loop through the removed events
            pos = cTmp.GetHeadPosition();
            while (pos)
            {
                CSmilShowSiteEvent* pHideEvent = (CSmilShowSiteEvent*) cTmp.GetNext(pos);
                if (pHideEvent)
                {
                    // Set the new event time
                    pHideEvent->m_ulEventTime = ulRemoveTime;
                    // Get the this event's group duration
                    UINT32 ulGroupDur = 0;
                    if (getGroupDuration(pHideEvent->m_uGroupIndex, ulGroupDur))
                    {
                        // Does this media have fill="remove"?
                        FillType eFill = getMediaFillBehavior((const char*) pHideEvent->getMediaID());
                        // We got the group duration.
                        // Is our new hide time scheduled for the
                        // group duration?
                        if (pHideEvent->m_ulEventTime == ulGroupDur &&
                            eFill                     != FillRemove)
                        {
                            // Yes, it is, so set the flag that
                            // says to ignore the hide
                            pHideEvent->m_bIgnorEvent = TRUE;
                        }
                        else
                        {
                            // No, the hide is NOT scheduled for the group
                            // duration, so clear the flag that
                            // says to ignore the hide
                            pHideEvent->m_bIgnorEvent = FALSE;
                        }
                    }
                    // Re-insert into the event queue
                    MLOG_EVENT(m_pErrorMessages,
                               "\tInserting event into list (%s,%lu,%u,%lu,%lu,%s,%s) tick=%lu\n",
                               pHideEvent->getEventTypeName(),
                               pHideEvent->m_ulEventTime,
                               pHideEvent->m_uGroupIndex,
                               pHideEvent->m_bOnlyHideSite,
                               pHideEvent->m_bIgnorEvent,
                               pHideEvent->getMediaID(),
                               pHideEvent->getRegionID(),
                               HX_GET_BETTERTICKCOUNT());
                    insertEvent(pHideEvent);
                }
            }
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT CSmilDocumentRenderer::handleExternalMediaMarkerFile(CSmilElement* pElement,
                                                               const char*   pszID,
                                                               const char*   pszExternalFileName,
                                                               const char*   pszRelativeSource)
{
    HX_RESULT retVal = HXR_OK;

    if (pElement && pszID && pszExternalFileName && pszRelativeSource)
    {
        // First convert the URL of the source
        CHXString cSrc;
        convertURL((const char*) m_pParent->getURLRoot(),
                   (const char*) m_pParent->getURLPrefix(),
                   (const char*) pszRelativeSource,
                   cSrc);
        // Now generate a new root and prefix
        CHXString cNewPrefix;
        CHXString cNewRoot;
        char*     pszNewFrag = NULL;
        CHXURL::GeneratePrefixRootFragment((const char*) cSrc,
                                           cNewPrefix, 
                                           cNewRoot,
                                           pszNewFrag);
        HX_VECTOR_DELETE(pszNewFrag);
        // Now make the external media marker file name
        // relative to the source
        CHXString cExtFile;
        convertURL((const char*) cNewRoot,
                   (const char*) cNewPrefix,
                   pszExternalFileName,
                   cExtFile);
        // Now check to see if we have already done a TrackAdded for
        // this external media marker file name
        CExternalMediaMarkerInfo* pInfo = getExternalMediaMarkerInfo(cExtFile);
        if (!pInfo)
        {
            // This file is not already present
            // Create a ExternalMediaMarkerInfo struct
            pInfo = new CExternalMediaMarkerInfo();
            if (pInfo)
            {
                // Create the list
                pInfo->m_pIDList = new CHXSimpleList();
                if (pInfo->m_pIDList)
                {
                    // Add the id
                    char* pID = new char [strlen(pszID) + 1];
                    if (pID)
                    {
                        // Copy the string
                        strcpy(pID, pszID); /* Flawfinder: ignore */
                        // Add this id to the list
                        pInfo->m_pIDList->AddTail((void*) pID);
                        // Create the string
                        pInfo->m_pszAddTrackURL = new char [cExtFile.GetLength() + 1];
                        if (pInfo->m_pszAddTrackURL)
                        {
                            // Copy the string
                            strcpy(pInfo->m_pszAddTrackURL, (const char*) cExtFile); /* Flawfinder: ignore */
                            // Add this info to the list
                            if (!m_pExternalMediaMarkerList)
                            {
                                m_pExternalMediaMarkerList = new CHXSimpleList();
                            }
                            if (m_pExternalMediaMarkerList)
                            {
                                m_pExternalMediaMarkerList->AddTail((void*) pInfo);
                            }
                            // Create an IHXValues
                            IHXCommonClassFactory* pFact   = m_pParent->getFactory();
                            IHXValues*             pValues = NULL;
                            retVal = pFact->CreateInstance(CLSID_IHXValues, (void**) &pValues);
                            if (SUCCEEDED(retVal))
                            {
                                // Set the persistent component ID
                                pValues->SetPropertyULONG32("PersistentComponentID", m_ulPersistentComponentID);
                                // Set the URL
                                setProperty(pValues, "url", cExtFile);
                                // Set a flag saying this is an external media marker
                                setProperty(pValues, "ExternalMarkerFile", "1");
                                // We don't really have an id for this, but we 
                                // will create one from the pointer of the info struct
                                char szTmpID[48]; /* Flawfinder: ignore */
                                sprintf(szTmpID, "xmmf_0x%08x", pInfo); /* Flawfinder: ignore */
                                setProperty(pValues, "id",       (const char*) szTmpID);
                                setProperty(pValues, "repeatid", (const char*) szTmpID);
                                // XXXMEH - are there any other properties we need to set?
                                IHXGroup* pGroup = NULL;
                                if (m_pGroupMap &&
                                    m_pGroupMap->Lookup(pElement->m_pNode->m_nGroup, (void*&) pGroup) &&
                                    pGroup)
                                {
                                    // Add this external file as a track
                                    pGroup->AddTrack(pValues);
                                }
                                else
                                {
                                    retVal = HXR_FAIL;
                                }
                            }
                            HX_RELEASE(pValues);
                        }
                        else
                        {
                            retVal = HXR_FAIL;
                        }
                    }
                    else
                    {
                        retVal = HXR_FAIL;
                    }
                }
                else
                {
                    retVal = HXR_FAIL;
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
            if (FAILED(retVal))
            {
                HX_DELETE(pInfo);
            }
        }
        else
        {
            // We already have done an AddTrack for this file,
            // so we simply need to add this id to the list,
            // if it is not already present.
            if (!isIDMappedToExternalMediaMarkerFile(pInfo, pszID))
            {
                // Add the id
                char* pID = new char [strlen(pszID) + 1];
                if (pID)
                {
                    // Copy the string
                    strcpy(pID, pszID); /* Flawfinder: ignore */
                    // Add this id to the list
                    pInfo->m_pIDList->AddTail((void*) pID);
                }
            }
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT
CSmilDocumentRenderer::handleRootLayout(CSmilRootLayout* pElement)
{
    HX_RESULT retVal = HXR_OK;

    if (pElement)
    {
        // Set the parser root-layout into the object
        m_pRootLayout->SetParserRootLayout(pElement);
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT
CSmilDocumentRenderer::handleAddGroup(CSmilAddGroup* pElement)
{
    HX_RESULT rc = HXR_OK;
    if(!m_pGroupMap)
    {
      m_pGroupMap = new CHXMapLongToObj;
    }

    IHXPlayer* pPlayer = m_pParent->getPlayer();
    IHXGroupManager* pMgr = 0;
    if(HXR_OK == pPlayer->QueryInterface(IID_IHXGroupManager, (void**)&pMgr))
    {
      IHXGroup*   pGroup = NULL;
      IHXGroup2*  pGroup2 = NULL;
      IHXPrefetch*      pPrefetch = NULL;

      if (m_pParent->m_bUseNestedMeta && 0 == m_ulGroupIndex)
      {
          rc = pMgr->GetCurrentGroup(m_uGroupIndexWithin);
          rc = pMgr->GetGroup(m_uGroupIndexWithin, pGroup);
      }
      else
      {
          rc = pMgr->CreateGroup(pGroup);
      }

      if (HXR_OK == rc)
      // release pGroup when m_pGroupMap is destructed
      {
          CHXHeader* pGroupValues = new CHXHeader;
          pGroupValues->AddRef();

          if(pElement->m_ulDuration != (UINT32)-1)
          {
            pGroupValues->SetPropertyULONG32("duration", pElement->m_ulDuration);
          }

          pGroupValues->SetPropertyULONG32("total_tracks", pElement->m_nTotalTracks);
          pGroupValues->SetPropertyULONG32("initial_tracks", pElement->m_nInitTracks);
          
          pGroupValues->SetPropertyULONG32("PersistentComponentID", m_ulPersistentComponentID);

          // copy all the node values to group values
          IHXValues* pValues = pElement->m_pValues;
          if(pValues)
          {
            IHXBuffer* pBuf = NULL;
            const char* pName = NULL;
            HX_RESULT res = 
                pValues->GetFirstPropertyCString(pName, pBuf);
            while(HXR_OK == res)
            {
                pGroupValues->SetPropertyCString(pName, pBuf);

                HX_RELEASE(pBuf);
                res = pValues->GetNextPropertyCString(pName, pBuf);
            }
          }
          
          pGroup->SetGroupProperties(pGroupValues);      
          if (HXR_OK == pGroup->QueryInterface(IID_IHXGroup2, (void**)&pGroup2))
          {
            pGroup2->SetPersistentComponentProperties(m_ulPersistentComponentID,
                                            pGroupValues);
          }
          HX_RELEASE(pGroupValues);

          if (m_pParent->m_bUseNestedMeta && 0 == m_ulGroupIndex)
          {
            GroupAdded(m_uGroupIndexWithin, pGroup);
          }
          else
          {
            pMgr->AddGroup(pGroup);
          }
          
          m_ulTrackIndex = 0;
          m_ulGroupIndex++;

          (*m_pGroupMap)[pElement->m_nGroup] = pGroup;

          if (HXR_OK == pGroup->QueryInterface(IID_IHXPrefetch, (void**)&pPrefetch))
          {
                // Add ourselves as a prefetch sink
            pPrefetch->AddPrefetchSink(this);
                // Add ourselves as a track sink
                IHXTrackSink* pSink = NULL;
                QueryInterface(IID_IHXTrackSink, (void**) &pSink);
                if (pSink)
                {
                    // XXXMEH - where is the right place to remove
                    // ourselves as a track sink?
                    pGroup2->AddTrackSink(pSink);
                }
                HX_RELEASE(pSink);
          }
          HX_RELEASE(pPrefetch);
          HX_RELEASE(pGroup2);
      }
    }
    HX_RELEASE(pMgr);

    return rc;
}


HX_RESULT
CSmilDocumentRenderer::GetCurGroupDuration(REF(UINT32) ulCurGrpDur)
{
    HX_RESULT pnrslt = HXR_OK;
    ulCurGrpDur = m_ulCurGroupDuration;
    return pnrslt;
}

// /This is needed for fixing PR 56686 (and others) where the presentation
// duration is not high enough to account for time-container-extended
// duration.  E.g.,
//   <body><par><img dur="1s"/><excl dur="20s"></excl></par></body>
// should play for 20s even though the excl is empty, but the core didn't
// used to have any way of knowing about this since only media durations
// are made know to the core (via addTrack()).  The below method bumps up
// the presentation duration if a higher-than-m_ulCurGroupDuration value
// is declared by the outer time container (body child):
// /Also, made it so we *always* set this duration, even if we decide it
// is lower (see below):
HX_RESULT
CSmilDocumentRenderer::resolveGroupDurToOuterTimeContainerDur(
      UINT32 ulGroupIndex, UINT32 ulGroupTimeContainerDuration)
{

#if defined(_DEBUG)  &&  defined(XXXEH_DEBUGOUT_RESOLVEGROUPDUR)
{
FILE* f1 = ::fopen("c:\\smil2AddDuration.txt", "a+");
::fprintf(f1, "CSmilDocumentRenderer::resolveGroupDurToOuterTimeContainerDur"
      "(group=%lu, groupTimeContainerDur=%lu), m_ulCurGroupDuration=%lu%s\n",
      ulGroupIndex, ulGroupTimeContainerDuration, m_ulCurGroupDuration,
      m_pGroupInfoMap!=NULL?"":", (skipped: m_pGroupInfoMap is NULL)");
::fclose(f1);
}
#endif

    HX_RESULT pnrslt = HXR_OK;
    SMILGroupInfo* pGroupInfo = NULL;
    if (m_pGroupInfoMap  &&  // /Check for NULL fixes PR 65874.
          m_pGroupInfoMap->Lookup(ulGroupIndex, (void*&)pGroupInfo))
    {
      // /Changed '>' to '>=' to fix "PR55117-related_andPR6XXX5...": re-
      // force core to adjust presentation duration after all media has
      // been added:
      // /For PR59584+,  we *always* set this duration, even if we decide it
      // is lower, because we may reset the last track's duration to something
      // less and the core will then adjust without accounting for the frozen
      // period that that track may need, e.g., if last-in-seq is a par w/discrete
      // image in it, the core will first think it's 5sec long, but we reset its
      // duration to be .001sec and freeze it for the remainder of its par
      // parent (that has an explicit dur or end): removed if():
#if defined(XXXEH_ONLY_SET_IF_SAME_OR_HIGHER_20011024)
      if (ulGroupTimeContainerDuration >=
            // /Changing this from pGroupInfo->m_ulDuration to
            // m_ulCurGroupDuration fixes PR 56686:
            m_ulCurGroupDuration // /was:  pGroupInfo->m_ulDuration)
            // /Helps fix PR 62688; group duration is now resolved to
            // non-indefinite time, so let's update group duration:
            ||  (WAY_IN_THE_FUTURE <= m_ulCurGroupDuration  &&
            WAY_IN_THE_FUTURE > ulGroupTimeContainerDuration) )
#endif
      {
          if (ulGroupTimeContainerDuration > WAY_IN_THE_FUTURE)
          {
            ulGroupTimeContainerDuration = WAY_IN_THE_FUTURE;
          }
          // duration is the end of group
          pGroupInfo->m_ulDuration = ulGroupTimeContainerDuration;

          pGroupInfo->m_bDurationSet = TRUE;
          ignoreLastHideEvent(ulGroupIndex, pGroupInfo);

          // /Update the "persistent layout stream" duration
          if (m_pPersistentLayoutStream)
          {
            IHXValues* pStreamProps = NULL;
            m_pPersistentLayoutStream->GetProperties(pStreamProps);
            if (pStreamProps)
            {
                pStreamProps->SetPropertyULONG32("duration",
                      ulGroupTimeContainerDuration);
                m_pPersistentLayoutStream->SetProperties(pStreamProps);

                    // /The following if()-block fixes PR 108178: the core will
                    // ignore the layoutstream duration property if we've
                    // ever given the group a duration property even if it was
                    // the flag-it-as-unresolved WAY_IN_THE_FUTURE value.
                    // So, we need to reset the group duration property if
                    // it's been set to some other value:
                    IHXPlayer* pPlayer = m_pParent->getPlayer();
                    IHXGroupManager* pMgr = 0;
                    if (HXR_OK == pPlayer->QueryInterface(IID_IHXGroupManager,
                             (void**)&pMgr))
                    {
                        IHXGroup* pGroup = NULL;
                        IHXGroup2* pGroup2 = NULL;
                        if (HXR_OK == pMgr->GetGroup(ulGroupIndex, pGroup) &&
                                HXR_OK == pGroup->QueryInterface(IID_IHXGroup2,
                                (void**)&pGroup2))
                        {
                            IHXValues* pGroupProperties = NULL;

                            pGroup2->GetPersistentComponentProperties(
                                  m_ulPersistentComponentID, pGroupProperties);
                            if(pGroupProperties)
                            {
                                UINT32 ulPrevGroupDur = 0;
                                if (HXR_OK ==
                                        pGroupProperties->GetPropertyULONG32("duration",
                                        ulPrevGroupDur)   &&  ulPrevGroupDur !=
                                        ulGroupTimeContainerDuration)
                                {
                                    pGroupProperties->SetPropertyULONG32("duration",
                                            ulGroupTimeContainerDuration);
                                }
                            }
                            HX_RELEASE(pGroupProperties);
                        }
                        HX_RELEASE(pGroup2);
                        HX_RELEASE(pGroup);
                    }
                    HX_RELEASE(pMgr);


                }
            HX_RELEASE(pStreamProps);
          }
          PersistentDurationSet(pGroupInfo->m_ulDuration,
                m_pSmilParser->m_ulPersistentComponentDelay,
                // /XXXEH- TODO: check w/Henry if this is OK:
                FALSE);
      }
    }
    else
    {
      pnrslt = HXR_UNEXPECTED;
    }
    return pnrslt;
}



void
CSmilDocumentRenderer::setProperty(IHXValues* pValues, 
    const char* pName, const char* pValue)
{
    IHXBuffer* pBuf = 0;
    IHXCommonClassFactory* pFactory = m_pParent->getFactory();
    if(HXR_OK == pFactory->CreateInstance(CLSID_IHXBuffer,
          (void**)&pBuf))
    {
      pBuf->Set((BYTE*)pValue, strlen(pValue)+1);
      pValues->SetPropertyCString(pName, pBuf);
      pBuf->Release();
    }
}

HX_RESULT CSmilDocumentRenderer::convertURL(const char* pszBaseRoot, const char* pszBasePrefix,
                                            const char* pURL, CHXString& newURL)
{
    HX_RESULT retVal = HXR_OK;

    CHXURL urlObj(pURL);
    IHXValues* pHeader = urlObj.GetProperties();
    if (pHeader)
    {
        IHXBuffer* pBuffer = NULL;
        HX_RESULT   rc      = pHeader->GetPropertyBuffer(PROPERTY_SCHEME, pBuffer);
        if (SUCCEEDED(rc))
        {
          // fully qualified URL
          newURL = pURL;
        }
        else
        {
          // relative URL
          // if it starts with '/', make it relative to the root of 
          // the URL prefix

          if(*pURL == '/')
          {
                newURL = pszBaseRoot;
                newURL += pURL;
          }
          else if (strnicmp(pURL, URL_COMMAND, sizeof(URL_COMMAND) - 1) == 0)
          {
              newURL = pURL;
          }
          else
          {
                newURL = pszBasePrefix;
                newURL += pURL;
          }
        }
        HX_RELEASE(pBuffer);
        HX_RELEASE(pHeader);
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT CSmilDocumentRenderer::handleSource(CSmilSource* pElement)
{
    MLOG_SRC(m_pErrorMessages,"handleSource() id=%s del=%lu dur=%lu m_ulCurrentTime=%lu\n",
             (const char*) pElement->m_pNode->m_id,
             pElement->m_ulDelay,
             pElement->m_ulDuration,
             m_ulCurrentTime);
    HX_RESULT rc = HXR_OK;

    CHXString urlString;
    convertURL((const char*) m_pParent->getURLRoot(),
               (const char*) m_pParent->getURLPrefix(),
               (const char*) pElement->m_src,
               urlString);

    BOOL bIsPrefetchElement = (pElement->m_pNode  &&
          SMILPrefetch == pElement->m_pNode->m_tag);

    BOOL bDurationPropSet = FALSE;

    double fDur = -1;

#if defined(_DEBUG)  &&  defined(XXXEH_DEBUG_HANDLESOURCE_AND_TRACKDURATIONSET)
{FILE* f1 = ::fopen("c:\\smil2AddDuration.txt", "a+");
::fprintf(f1, "handleSource(media id=%s, duration=%lu, delay=%lu%s, maxDuration=%lu, clipBegin=%lu)\n",
 (const char*)pElement->m_pNode->m_id, pElement->m_ulDuration,pElement->m_ulDelay,
 WAY_IN_THE_FUTURE==pElement->m_ulDelay?"(UNRESOLVED)":"", pElement->m_ulMaxDuration, pElement->m_ulClipBegin);::fclose(f1);}
#endif

    IHXGroup* pGroup = 0;

    HX_ASSERT(WAY_IN_THE_FUTURE > pElement->m_ulDelay);
    // /For PR 59584: don't add a track whose begin is indefinite:
    if (WAY_IN_THE_FUTURE <= pElement->m_ulDelay)
    {
      goto cleanup;
    }

    if(m_pGroupMap &&  pElement->m_pNode  &&
       m_pGroupMap->Lookup(pElement->m_pNode->m_nGroup, (void*&)pGroup))
    {
      IHXCommonClassFactory* pFactory = m_pParent->getFactory();
      IHXValues* pValues = 0;
      if(HXR_OK == pFactory->CreateInstance(CLSID_IHXValues,
            (void**)&pValues))
      {
          pValues->SetPropertyULONG32("PersistentComponentID", m_ulPersistentComponentID);         

          setProperty(pValues, "url", urlString);
          setProperty(pValues, "id", pElement->m_pNode->m_id);
          setProperty(pValues, "repeatid", pElement->m_pNode->m_repeatid);
          if (!bIsPrefetchElement)
          {
            setProperty(pValues, "playto", pElement->m_region);
            if(pElement->m_region.GetLength() > 0)
            {
                // save original region name in track values
                setProperty(pValues, "region", pElement->m_region);
            }
                // Set the fill string. We should get it from pElement->m_eActualFill,
                // since this is the value which takes fillDefault into account.
                
            // XXX HP set "fill" as ULONG32 is much efficient than string!!
            //        no convertion needed
            CHXString cFill;
                CSmilParser::getFillString(pElement->m_eActualFill, cFill);
            setProperty(pValues, "fill", cFill);

            pValues->SetPropertyULONG32("fill", pElement->m_eActualFill);

                CHXString cErase;
                CSmilParser::getEraseString(pElement->m_eErase, cErase);
                setProperty(pValues, "erase", cErase);

                if (pElement->m_beginTransition.GetLength() > 0)
                {
                    setProperty(pValues, "beginTransition", pElement->m_beginTransition);
                }

                if (pElement->m_endTransition.GetLength() > 0)
                {
                    setProperty(pValues, "endTransition", pElement->m_endTransition);
                }
          }

          setProperty(pValues, "track-hint",
            pElement->m_pNode->m_trackHint);

          pValues->SetPropertyULONG32("lexicalNum", pElement->m_pNode->m_ulTagStartLine);

            // Check the rn:accessErrorBehavior property of this element
            AccessErrorBehavior eErr =
                m_pSmilParser->getAccessErrorBehavior(pElement->m_pNode);
            if (eErr == AccessErrorBehaviorContinue)
            {
                // Set the property
                setProperty(pValues, "accessErrorBehavior", "continue");
                // Since we have rn:accessErrorBehavior="continue" behavior,
                // then we need to provide an alternative URL. We want a
                // broken window to show up. Since the GIF renderer has this
                // ability, then we will simply provide a corrupt inline GIF.
                setProperty(pValues, "altURL",
                            "data:image/gif,any_non_gif_junk");
            }
            else
            {
                // Set the property
                setProperty(pValues, "accessErrorBehavior", "stop");
            }

          UINT32 ulDelay = 0;
          if(pElement->m_ulDelay != (UINT32)-1)
          {
            pValues->SetPropertyULONG32("delay", pElement->m_ulDelay);
            ulDelay = pElement->m_ulDelay;
          }

          ULONG32 ulDurationUsedInProperties = (UINT32)-1;
          if(pElement->m_ulDuration != (UINT32)-1  &&
                // /We don't want to use explicitly-set duration if
                // min=".." was set; we'll await being told of the
                // intrinsic duration of the media and THEN decide what
                // our duration should be:
                (0 == pElement->m_ulMinActiveDur  &&
                !pElement->m_bUseMediaDurForMinDur)  &&
                // /We also don't want to declare a dur yet if
                // max="media" in case the authored dur is greater than
                // the intrinsic duration:
                (!pElement->m_bUseMediaDurForMaxDur))
          {
            if (WAY_IN_THE_FUTURE == pElement->m_ulDuration)
            {
                HX_ASSERT(pElement->m_ulDuration > ulDelay);
                fDur = double(float(pElement->m_ulDuration - ulDelay));
                // /Don't multiply by partialPlayFactor if duration
                // is indefinite.
            }
            else
            {
                HX_ASSERT(!pElement->m_bDurationIncludesDelayBeyondSyncbase);
                fDur = double(float(pElement->m_ulDuration));
                fDur *= pElement->m_pNode->m_fPartialPlayFactor;
            }
            pValues->SetPropertyULONG32("duration", ULONG32(float(fDur)) );
            bDurationPropSet = TRUE;
            ulDurationUsedInProperties = ULONG32(float(fDur));
          }
            // Save the value of pElement->m_ulDuration that
            // we set in the AddTrack IHXValues
            pElement->m_ulDurationInAddTrack = pElement->m_ulDuration;
          if(pElement->m_ulMaxDuration != (UINT32)-1)
          {
            // /"Fixes" PR 47476 by not setting maxduration if duration
            // prop is already set (to a same-or-lower value) for repeats.
            // Otherwise, the core ignores the duration and uses the
            // maxduration as the duration passed in through subsequent
            // RepeatedTrackDurationSet() callback:
            if (!bDurationPropSet  ||
                  pElement->m_pNode->m_repeatTag == RepeatUnknown  ||
                  ulDurationUsedInProperties > pElement->m_ulMaxDuration)
            {
                ULONG32 ulMaxDuration = pElement->m_ulMaxDuration;
                if (ulDelay>0  &&  pElement->m_ulMaxDuration >= WAY_IN_THE_FUTURE)
                {
                  // /This fixes PR 81821: indef presentation duration was
                  // displaying as 22:22:22:27.2 instead of nothing shown.
                  // NOTE: we'll never get here if ulDelay >= WAY_IN...:
                  ulMaxDuration = WAY_IN_THE_FUTURE - ulDelay;
                }
                pValues->SetPropertyULONG32("maxduration", ulMaxDuration);
            }
          }
          if(pElement->m_ulClipBegin != (UINT32)-1)
          {
            pValues->SetPropertyULONG32("start", pElement->m_ulClipBegin);
          }
          if(pElement->m_ulClipEnd != (UINT32)-1)
          {
            pValues->SetPropertyULONG32("end", pElement->m_ulClipEnd);
          }

          if (pElement->m_pNode->m_repeatTag != 
            RepeatUnknown)
          {
            pValues->SetPropertyULONG32("repeatTag", pElement->m_pNode->m_repeatTag);
          }

            // Set the soundLevel property of the track.
            //
            // We need to check to see if this element is playing to a <region>.
            // If it is, then we will use the soundLevel attribute of that <region>.
            UINT32 ulSoundLevel = 100;
          BOOL bNeedToHandleSndLevelDueToLinkRgnTarget = FALSE;
            if (pElement->m_region.GetLength() > 0)
            {
                CSmilBasicRegion* pRegion = getRegionByID(pElement->m_region);
                if (pRegion)
                {
                // /For PR 66650: if either source or destination level
                // of the link was not 100%, then we need to adjust one
                // up or down:
                if (100 != pElement->m_ulLinkTargetDestnLevel_pct  ||
                      100 != pElement->m_ulLinkTargetSourceLevel_pct)
                {
                  bNeedToHandleSndLevelDueToLinkRgnTarget = TRUE;
                  ulSoundLevel = pElement->m_ulLinkTargetDestnLevel_pct;
                }
                else
                {
                  ulSoundLevel = UINT32(pRegion->m_dSoundLevel + 0.5);
                }
                }
            }

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)

            // Also we need to check if an animation starts right at
            // the beginning of this track. If so, then we need to 
            // go ahead and set the soundLevel to be the first value
            // of the animation
            if (isAttributeAnimated(pElement->m_region, kAttrNameSoundLevel))
            {
                // We ARE animating the soundLevel of this region, so loop
                // through all the animation elements which are animating
                // this region's soundLevel. If there are multiple animations
                // going on at the same time, then we cannot attempt to
                // try and calculate these - we have to just estimate. We 
                // will take the min soundLevel.
                BOOL   bFound     = FALSE;
                UINT32 ulMinLevel = 0xFFFFFFFF;
                CSmilAnimateElement* pAnim = getFirstAnimationElement(pElement->m_region,
                                                                      kAttrNameSoundLevel);
                while (pAnim)
                {
                    // Does this animation start at the same time this
                    // element starts?
                    if (pAnim->m_ulDelay == pElement->m_ulDelay)
                    {
                        // It DOES start at the same time, so get the first
                        // value of the animation
                        if (pAnim->m_ppValue &&
                            pAnim->m_ulNumValues >= 1 &&
                            pAnim->m_ppValue[0])
                        {
                            // We did find at least one matching
                            // animation, so set the flag
                            bFound = TRUE;
                            // Get the first value
                            UINT32 ulTmp = (UINT32) (pAnim->m_ppValue[0]->GetValueDouble() + 0.5);
                            // If it's less than the min, update the min
                            if (ulTmp < ulMinLevel)
                            {
                                ulMinLevel = ulTmp;
                            }
                            
                        }
                    }
                    // Get the next animation
                    pAnim = getNextAnimationElement(pElement->m_region,
                                                    kAttrNameSoundLevel);
                }
                // If we found a matching animation, then set
                // the soundLevel
                if (bFound)
                {
                    ulSoundLevel = ulMinLevel;
                }
            }
#endif /* #if defined(HELIX_FEATURE_SMIL2_ANIMATION) */
          // /For PR 66650: if this element was created for a link-targetted
          // region, then we may need to adjust soundLevel of currently-
          // playing audio tracks:
          if (bNeedToHandleSndLevelDueToLinkRgnTarget)
          {
            // /Adjust all tracks' soundLevels (both source & dest)
            // regardless of value, but skip setting dest's soundLevel if
            // calculated level is 100:
            if (100 != ulSoundLevel)
            { 
                pValues->SetPropertyULONG32("soundLevel", ulSoundLevel); 
            } 
            // /then set all source tracks' soundLevels:
            AdjustSoundLevelsOfAllCurrentTracks(
                  pElement->m_pNode->m_nGroup,
                  pElement->m_ulLinkTargetSourceLevel_pct,
                  pElement);
          }
          else
          {
            pValues->SetPropertyULONG32("soundLevel", ulSoundLevel);
          }

          if (pElement->m_bIndefiniteDuration ||
            pElement->m_bIndefiniteEnd)
          {
            // /Adding this check fixes PR 73961:
            if (WAY_IN_THE_FUTURE != pElement->m_ulDuration) 
            {
                pValues->SetPropertyULONG32("indefiniteduration", TRUE);
            }
          }

          if (bIsPrefetchElement)
          {
            pValues->SetPropertyULONG32("PrefetchType",
                  pElement->m_typeOfPrefetchAmount);
            pValues->SetPropertyULONG32("PrefetchValue",
                  pElement->m_ulPrefetchAmount);
            HX_ASSERT(pElement->m_typeOfPrefetchAmount <
                  PrefetchMaxAllowedPlus1);
          }
          if (pElement->m_title.GetLength() > 0)
          {
            setProperty(pValues, "title",
                  (const char*)pElement->m_title);
          }
          if (pElement->m_author.GetLength() > 0)
          {
            setProperty(pValues, "author",
                  (const char*)pElement->m_author);
          }
          if (pElement->m_copyright.GetLength() > 0)
          {
            setProperty(pValues, "copyright",
                  (const char*)pElement->m_copyright);
          }
          if (pElement->m_abstract.GetLength() > 0)
          {
            setProperty(pValues, "abstract",
                  (const char*)pElement->m_abstract);
          }

          // add arbitrary name-value pairs to track
          if(pElement->m_pNode->m_pValues)
          {
            IHXValues* pSrcValues = pElement->m_pNode->m_pValues;
            const char* pName = 0;
            IHXBuffer* pValue = 0;

            HX_RESULT rCode = pSrcValues->GetFirstPropertyCString(
                pName, pValue);
            while(HXR_OK == rCode)
            {
                // skip the values we already have or have overridden...
                if((strcasecmp(pName, "url") != 0) &&
                   (strcasecmp(pName, "id") != 0) &&
                   (strcasecmp(pName, "playto") != 0) &&
                   (strcasecmp(pName, "fill") != 0)  &&
                   (strcasecmp(pName, "title") !=0)  &&
                   (strcasecmp(pName, "author") !=0)  &&
                   (strcasecmp(pName, "copyright") !=0)  &&
                   (strcasecmp(pName, "abstract") !=0)  )
                {
                  setProperty(pValues, pName, 
                      (const char*)pValue->GetBuffer());
                }
                HX_RELEASE(pValue);
                rCode = pSrcValues->GetNextPropertyCString(
                  pName, pValue);
            }
            HX_RELEASE(pValue);
          }

          {
            // determine whether this source has been initialized yet...
            BOOL            bHandled = FALSE;
            SMILPlayToAssoc*    pPlayToAssoc = NULL;

            if(m_pPlayToAssocList  &&
                  // /Helps fix PR 50660 (par version) and PR 62408:
                  // don't repeat one that's merely restarting:
                  !pElement->IsRestartingAndNotRepeating())
            {
                CHXSimpleList::Iterator i =m_pPlayToAssocList->Begin();
                for(;i!=m_pPlayToAssocList->End();++i)
                {
                  SMILPlayToAssoc* pThisAssoc = (SMILPlayToAssoc*)(*i);
                  if (pThisAssoc->m_repeatid == pElement->m_pNode->m_repeatid)
                  {
                      IHXGroup2* pGroup2 = NULL;
                      IHXTrack*     pHXTrack = NULL;

                      if (HXR_OK == pGroup->QueryInterface(IID_IHXGroup2, (void**)&pGroup2))
                      {
                        if (HXR_OK == pGroup2->GetIHXTrack(pThisAssoc->m_uTrackIndex, pHXTrack) &&
                            pHXTrack)
                        {
                            if (!bDurationPropSet  &&
                                  pThisAssoc->m_ulDuration !=
                                  (UINT32)-1  &&
                                  1.0 != pElement->m_pNode->
                                  m_fPartialPlayFactor)
                            {
                              double fDur = double(float(
                                    pThisAssoc->m_ulDuration));
                              fDur *= pElement->m_pNode->
                                    m_fPartialPlayFactor;
                              pElement->m_ulDuration =
                                    ULONG32(float(fDur));
                              pValues->SetPropertyULONG32(
                                    "duration",
                                    pElement->m_ulDuration);
                            }

                            bHandled = TRUE;
                            pHXTrack->AddRepeat(pValues);
                        }
                        HX_RELEASE(pHXTrack);
                      }
                      HX_RELEASE(pGroup2);

                      if (bHandled)
                      {
                        break;
                      }
                  }
                }

            }
            
            if (!bHandled)
            {
                SMILNode* pSyncNode = m_pSmilParser->getSyncAncestor(
                      pElement->m_pNode);
                if (bIsPrefetchElement)
                {
                  IHXPrefetch* pPrefetch = NULL;
                  if (HXR_OK == pGroup->QueryInterface(IID_IHXPrefetch,
                        (void**)&pPrefetch))
                  {
                            // Add the rn:handledBy attribute to the 
                            // prefetch IHXValues
                            const char* pszHandledByStr =
                                m_pSmilParser->getEnumAttrString(SMIL2AttrRNHandledBy,
                                                                 pElement->m_eHandledBy);
                            if (pszHandledByStr)
                            {
                                addStringProperty(pValues,
                                                  m_pContext,
                                                  "handledBy",
                                                  pszHandledByStr);
                            }
                      if (HXR_OK == pPrefetch->AddPrefetchTrack(pValues))
                      {
                                UINT32 ulTrackIndex = 0;
                                // /Don't cast a UINT16& to a UINT32& here otherwise Big-endian
                                // machines will not receive the right 2 bytes:
                                rc = pValues->GetPropertyULONG32("TrackIndex", ulTrackIndex);
                                pElement->m_pNode->m_nPrefetchTrackNum = (UINT16)ulTrackIndex;

                        HX_ASSERT(HXR_OK == rc);

                        if (!m_pPrefetchTrackElementMap)
                        {
                            m_pPrefetchTrackElementMap = new CHXMapLongToObj;
                        }

                        CHXString* pId = new CHXString;
                        *pId = pElement->m_pNode->m_id;
                        (*m_pPrefetchTrackElementMap)[pElement->m_pNode->m_nPrefetchTrackNum] = pId;

                      }
                  }
                  HX_RELEASE(pPrefetch);

                  // /Fixes PR 57135: prefetch element needs to be added
                  // to the timeline (i.e., to its parent) so its parent
                  // duration can resolve:
                  if ((UINT32)-1 != pElement->m_ulDuration)
                  {
                      HX_ASSERT(pElement->m_ulDuration == pElement->getPureDuration());
                      m_pSmilParser->durationResolved(
                            (const char*)pElement->m_pNode->m_id,
                            pElement->m_ulDuration);
                  }
                }
                else
                {
                        // Is the soundLevel animated?
                        if (isAttributeAnimated(pElement->m_region, kAttrNameSoundLevel)
                        // /This will be true if there exists an a or
                        // area element that messes with the audio
                        // level and has sourcePlaystate="play":
                        ||  m_pSmilParser->allTracksNeedReflushHint()
                        // /Or, is the flag set that tells us that
                        // this audio track might end on an event
                        // (including restarting on the fly):
                        ||  pElement->m_bAudioDeviceReflushHintNeeded)
                        {
                      // /Track may be ended early (on-the-fly) OR
                            // The soundLevel on this track WILL be 
                            // animated - therefore, we need to add
                            // the following hint.
//                            // XXXMEH
//                            OutputDebugString("setting audioDeviceReflushHint track property to 1.\n");
                            pValues->SetPropertyULONG32("audioDeviceReflushHint", 1);
                        }
                        // If mediaRepeat or any <param> elements
                        // are specified, then we need to add them to request parameters
                        IHXValues* pReqVal = NULL;
                        IHXCommonClassFactory* pFactory = m_pParent->getFactory();
                        if (pFactory)
                        {
                            if (pElement->m_MediaRepeat.GetLength() > 0 ||
                                hasParamChildren(pElement, TRUE))
                            {
                                pFactory->CreateInstance(CLSID_IHXValues, (void**) &pReqVal);
                                if (pReqVal)
                                {
                                    if (pElement->m_MediaRepeat.GetLength() > 0)
                                    {
                                        addStringProperty(pReqVal, m_pContext,
                                                          "mediaRepeat",
                                                          pElement->m_MediaRepeat);
                                    }
                                    if (hasParamChildren(pElement, TRUE))
                                    {
                                        addParamProperties(pElement,
                                                           pReqVal,
                                                           m_pContext,
                                                           TRUE);
                                    }
                                }
                            }
                        }
                  // /XXXEH- revisit this when we can handle groups of
                  // lock-sync'd-to-each-other tracks that are
                  // sync-independent of parallel tracks:
                  BOOL bSyncbaseIsSyncIndependent = FALSE;
                  if (pSyncNode  &&  pSyncNode->m_pElement  &&
                        SmilSyncBehaviorIndependent ==
                        pSyncNode->m_pElement->m_syncBehavior)
                  {
                      bSyncbaseIsSyncIndependent = TRUE;
                  }
                  if (SmilSyncBehaviorIndependent !=
                        pElement->m_syncBehavior  &&
                        !bSyncbaseIsSyncIndependent)
                  {
                      // Now add the track - if there are any request
                      // parameters, then use AddTrack2
                      if (pReqVal)
                      {
                        // QI for IHXGroup2
                        IHXGroup2* pGroup2 = NULL;
                        pGroup->QueryInterface(IID_IHXGroup2,
                              (void**) &pGroup2);
                        if (pGroup2)
                        {
                            rc = pGroup2->AddTrack2(pValues, pReqVal);
                        }
                        HX_RELEASE(pGroup2);
                      }
                      else
                      {
                        rc = pGroup->AddTrack(pValues);
                      }
                      m_ulTrackIndex++;
                  }
                  // /track's syncBehavior="independent", so create a
                  // new child player for it:
                  else if (m_pParent)
                  {
                      IHXClientEngine* pClientEngine =
                            m_pParent->getClientEngine();
                      if (pClientEngine)
                      {
                        IHXPlayer* pTopLevelPlayer =
                              m_pParent->getPlayer();
                        IHXPlayer* pChildPlayer = NULL;
                        if (HXR_OK == pClientEngine->CreatePlayer(
                              pChildPlayer)  &&  pChildPlayer)
                        {
                            IHXPlayerNavigator* pPlayerNavRoot=NULL;
                            IHXPlayerNavigator* pPlayerNavKid=NULL;
                            HX_RESULT pnrslt =
                                  pTopLevelPlayer->QueryInterface(
                                  IID_IHXPlayerNavigator,
                                  (void**)&pPlayerNavRoot);
                            if (HXR_OK == pnrslt)
                            {
                              HX_RESULT pnradd = pPlayerNavRoot->
                                    AddChildPlayer(pChildPlayer);
                              UINT16 ulNumKids = pPlayerNavRoot->
                                    GetNumChildPlayer();
                              pnrslt =
                                    pChildPlayer->QueryInterface(
                                    IID_IHXPlayerNavigator,
                                    (void**)&pPlayerNavKid);
                              if (HXR_OK == pnrslt)
                              {
                                  HX_RESULT pnrspp =pPlayerNavKid->
                                        SetParentPlayer(
                                        pTopLevelPlayer);
                              }
#if defined(BE_SITE_SUPPLIER_20010205)
                              m_XXXEH_HACK_regionId =
                                    pElement->m_region;
                              m_pIndependentPlayer = pChildPlayer;

                              const char* pPlayTo = (const char*)
                                    pElement->m_region;
                              UINT32 ulLexicalNum = 0;
                              pValues->GetPropertyULONG32(
                                    "lexicalNum", ulLexicalNum);

                              setPlayToAssoc(0, 9999+ulLexicalNum,
                                    pElement->m_pNode->m_id,
                                    pElement->m_pNode->m_repeatid,
                                    (const char*)
                                    pElement->m_region,
                                    (const char*)
                                    pElement->m_region, 
                                    NULL /*pBeginTransition*/,
                                    NULL /*pEndTransition*/,
                                    ulLexicalNum);

                              // /We implement IHXSiteSupplier, so
                              // we'll say we're the context and
                              // then we'll get called with
                              // SitesNeeded by this player, and we
                              // can hook it up then:
                              pChildPlayer->SetClientContext(
                                    (IHXSiteSupplier*)this);
#else
                              pChildPlayer->SetClientContext(
                                    m_pContext);
#endif /* BE_SITE_SUPPLIER_20010205 */

                              HX_RESULT openrslt = HXR_FAIL;
                              // /Need IHXPlayer2 for OpenRequest():
                              IHXPlayer2* pPlayer2_Child = NULL;

                              // /XXXEH- hack for now; set begin,
                              // end, clip-begin, and clip-end as
                              // URL-encoded parameters:
                              char chDelimiter = '?';
                              CHXString extraURLparams = "";

                              if ((UINT32)-1 !=
                                    pElement->m_ulDelay  &&
                                    0 != pElement->m_ulDelay)
                              {
                                  extraURLparams.Format(
                                    "%cdelay=%.3f", chDelimiter,
                                    (float)pElement->m_ulDelay /
                                    1000.0 );
                                  urlString += extraURLparams;
                                  extraURLparams = "";
                                  chDelimiter = '&';
                              }
                              if (-1 != fDur)
                              {
                                  extraURLparams.Format(
                                    "%cduration=%.3f", 
                                    chDelimiter,
                                    (float)fDur / 1000.0 );
                                  urlString += extraURLparams;
                                  extraURLparams = "";
                                  chDelimiter = '&';
                              }
                              if ((UINT32)-1 !=
                                    pElement->m_ulClipBegin)
                              {
                                  extraURLparams.Format(
                                    "%cstart=%.3f", chDelimiter,
                                    (float)pElement->m_ulClipBegin/
                                    1000.0 );
                                  urlString += extraURLparams;
                                  extraURLparams = "";
                                  chDelimiter = '&';
                              }
                              if ((UINT32)-1 !=
                                    pElement->m_ulClipEnd)
                              {
                                  extraURLparams.Format(
                                    "%cend=%.3f", chDelimiter,
                                    (float)pElement->m_ulClipEnd/
                                    1000.0 );
                                  urlString += extraURLparams;
                                  extraURLparams = "";
                                  chDelimiter = '&';
                              }

                              if (HXR_OK == 
                                    pChildPlayer->QueryInterface(
                                    IID_IHXPlayer2, 
                                    (void**)&pPlayer2_Child))
                              {
                                  IHXRequest* pRequest = NULL;
                                  IHXCommonClassFactory* pFactory=
                                        m_pParent->getFactory();
                                  if (pFactory)
                                  {
                                    IHXRequest* pRequest = NULL;
                                    pFactory->CreateInstance(
                                          CLSID_IHXRequest,
                                          (void**) &pRequest);
                                    if (pRequest)
                                    {
                                        pRequest->
                                          SetRequestHeaders(
                                          pValues);
                                        pRequest->SetURL((const
                                          char*)urlString);
                                        openrslt =
                                              pPlayer2_Child->
                                              OpenRequest(
                                              pRequest);
                                    }
                                  }
                              }
                              else // /No IHXPlayer2, so just call
                                  // OpenURL() & see what happens:
                              {
                                  openrslt = pChildPlayer->
                                        OpenURL((const char*)
                                        urlString);
                              }
                              if (HXR_OK == openrslt)
                              {
                                  pChildPlayer->Begin();
                              }
                            }
                        }
                      }
                  }
                        // Release the request parameters
                        HX_RELEASE(pReqVal);
                }
            }
          }

          pValues->Release();
      }
    }

cleanup:
    return rc;
}

HX_RESULT
CSmilDocumentRenderer::handleSourceUpdate(CSmilSourceUpdate* pElement)
{
    HX_RESULT rc = HXR_OK;

    const char* pszID = (const char*)pElement->m_srcID;

    // determine whether this source has been initialized yet...
    SMILPlayToAssoc* pPlayToAssoc = 0;
    if(m_pPlayToAssocList)
    {
      CHXSimpleList::Iterator i;
      for(i=m_pPlayToAssocList->Begin();i!=m_pPlayToAssocList->End();++i)
      {
          SMILPlayToAssoc* pThisAssoc = (SMILPlayToAssoc*)(*i);
          if(pThisAssoc->m_id == pszID  &&
                // /Added this while re-fixing PR 69639; be sure to
                // ignore already-stopped track & keep looking in
                // case element restart made new track w/same id:
                !pThisAssoc->m_bTrackStopped)
          {
            pPlayToAssoc = pThisAssoc;
            break;
          }
      }
    }
    if(pPlayToAssoc &&
       pPlayToAssoc->m_sourceMap.GetCount() > 0)
    {
        rc = doSourceUpdate(pPlayToAssoc, pElement);
    }
    else
    {
      // stick it into the deferred map,
      // it will be handled in RendererInitialized()
      if(!m_pDeferredSourceMap)
      {
          m_pDeferredSourceMap = new CHXMapStringToOb;
      }
      
      SMILDeferredSourceInfo* pInfo = new SMILDeferredSourceInfo;
      // /XXXEH- if duration is pure of delay, here, tell pInfo so (need pInfo to have new flag var):
      pInfo->m_ulDuration = pElement->m_ulUpdatedDuration;
      pInfo->m_ulDelay = 0;

      // /Fixes mem leak when pszID is already in the map:
      SMILDeferredSourceInfo* pTmpInfo;
      if (m_pDeferredSourceMap->Lookup(pszID, (void*&)pTmpInfo))
      {
          HX_DELETE(pTmpInfo);
      }
      (*m_pDeferredSourceMap)[pszID] = pInfo;
    }

    return rc;
}

// /Immediately removes a track that's associated with the source:
// Returns HXR_OK only if the track was found and RemoveTrack(n)
// succeeded:
HX_RESULT CSmilDocumentRenderer::handleTrackRemoval(const char* pszID, INT32 nGroupNum)
{
    HX_RESULT retVal = HXR_FAIL;

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)
    // First we have to check if this is an animation
    if (pszID && m_pAnimationMap)
    {
        void* pVoid = NULL;
        m_pAnimationMap->Lookup(pszID, pVoid);
        if (pVoid)
        {
            // Yes, this is an animation, so clear the return value
            retVal = HXR_OK;
            // Now call removeAnimation with this element
            CSmilAnimateElement* pAnim = (CSmilAnimateElement*) pVoid;
            removeAnimation(pAnim);
        }
    }
#endif /* #if defined(HELIX_FEATURE_SMIL2_ANIMATION) */

    // If we are still currently in a failed state, then 
    // it wasn't an animation.
    if (FAILED(retVal))
    {
        IHXGroup* pGroup = 0;
        if(m_pGroupMap && 
           m_pGroupMap->Lookup(nGroupNum, (void*&)pGroup))
        {
            // determine whether this source has been started yet:
            SMILPlayToAssoc* pPlayToAssoc = NULL;
            if(m_pPlayToAssocList)
            {
                CHXSimpleList::Iterator i;
                for(i=m_pPlayToAssocList->Begin();
                        i!=m_pPlayToAssocList->End();++i)
                {
                    SMILPlayToAssoc* pThisAssoc = (SMILPlayToAssoc*)(*i);
                    if (pThisAssoc->m_id == pszID  &&
                      // /Added this while re-fixing PR 69639; be sure to
                      // ignore already-stopped track & keep looking in
                      // case element restart made new track w/same id:
                      !pThisAssoc->m_bTrackStopped)
                    {
                        pPlayToAssoc = pThisAssoc;
                        break;
                    }
                }
            }
            if (pPlayToAssoc)
            {

#if defined(_DEBUG)  &&  defined(XXXEH_DEBUGOUT_TRACKSTOPPAUSERESUME)
{
FILE* f1 = ::fopen("c:\\smil2AddDuration.txt", "a+");
::fprintf(f1, "CSmilDocumentRenderer::handleTrackRemoval(ID=%s, group=%ld)\n",
      (const char*)pszID, nGroupNum);
::fclose(f1);
}
#endif

            // /Fixes PR 81253: old value in m_pDeferredSourceMap
            // has old (first-play) duration so updateStreamTiming() was
            // getting called with that (now-wrong) value when
            // rendererInitialized() got called if this track restarted,
            // resulting in wrong presentation duration.  So, remove old
            // reference to that element from the map here:
            if (m_pDeferredSourceMap)
            {
                SMILDeferredSourceInfo* pDeferredInfo = NULL;
                const char* pDeferredID = (const char*)pPlayToAssoc->m_id;
                if (m_pDeferredSourceMap->Lookup(pDeferredID,
                      (void*&)pDeferredInfo))
                {
                  CSmilElement* pThisElement = 
                        m_pSmilParser->findElement(pDeferredID);

                  HX_ASSERT(pThisElement);

                  HX_DELETE(pDeferredInfo);
                  BOOL bRemovedKeyOK =
                        m_pDeferredSourceMap->RemoveKey(pDeferredID);
                  HX_ASSERT(bRemovedKeyOK);

                }
            }


                pPlayToAssoc->m_bRemovePending = TRUE;
            if (m_bInTrackDurationSetCall)
            {
                retVal = HXR_OK;
                // /Helps fix PR 66391: call RemoveTrack() later, after
                // TrackDurationSet() has finished, otherwise core crashes
                // when it continues to use the removed-and-deleted
                // pStream in SourceInfo::SetupRenderer():
                if (!m_pDeferrededRemoveTrackMap)
                {
                  m_pDeferrededRemoveTrackMap = new CHXMapStringToOb();
                }
                if (m_pDeferrededRemoveTrackMap)
                {
                  // /Don't add it if it's already in the map:
                  if (!(*m_pDeferrededRemoveTrackMap)[pszID])
                  {
                      // /Add remove-track ID to the map for deferring
                      // calling RemoveTrack() until the core is ready:
                      CHXString* pCPNStrID = new CHXString(pszID);
                      if (pCPNStrID)
                      {
                        (*m_pDeferrededRemoveTrackMap)[pszID] =
                              (void*)pCPNStrID;
                      }
                  }
                }
                else
                {
                  retVal = HXR_OUTOFMEMORY;
                }
            }
            else
            {
                retVal = pGroup->RemoveTrack(pPlayToAssoc->m_uTrackIndex);
            }
          }
        }
    }


    return retVal;
}

// /Immediately pauses a track that's associated with the source:
// Returns HXR_OK only if the track was found and PauseTrack(n)
// succeeded:
// /pTimeVal gets created and filled with data that makes it a watcher
// for the endEvent of the "pauser" so that the element getting paused
// here can resume at that time, if and when it happens:
HX_RESULT
CSmilDocumentRenderer::handleTrackPausing(SMILNode* pNode,
      LONG32 lTimeOfPause,
      SMILPriorityClassPauseDisplay pauseDisplay,
      const char* pIdOfPauser)
{
    HX_RESULT rc = HXR_FAILED;

    IHXGroup* pGroup = 0;
    if(m_pGroupMap && 
       m_pGroupMap->Lookup((INT32)pNode->m_nGroup, (void*&)pGroup))
    {
        // /QI for IHXGroup2
        IHXGroup2* pGroup2 = NULL;
        pGroup->QueryInterface(IID_IHXGroup2, (void**) &pGroup2);
        if (pGroup2)
        {
          // /Determine whether this source has been started yet:
          SMILPlayToAssoc* pPlayToAssoc = NULL;
          if(m_pPlayToAssocList)
          {
            CHXSimpleList::Iterator i;
            for(i=m_pPlayToAssocList->Begin();
                  i!=m_pPlayToAssocList->End();++i)
            {
                SMILPlayToAssoc* pThisAssoc = (SMILPlayToAssoc*)(*i);
                if(pThisAssoc->m_id == pNode->m_id  &&
                      // /Helps re-fix PR 69639; be sure to ignore
                      // already-stopped track & keep looking in case
                      // element restart made new track w/same id:
                      !pThisAssoc->m_bTrackStopped)
                {
                  pPlayToAssoc = pThisAssoc;
                  break;
                }
            }
          }
          if (pPlayToAssoc)
          {
            IHXTrack* pHXTrack = NULL;
            rc = pGroup2->GetIHXTrack(pPlayToAssoc->m_uTrackIndex,
                  pHXTrack);
            if (pHXTrack)
            {

#if defined(_DEBUG)  &&  defined(XXXEH_DEBUGOUT_TRACKSTOPPAUSERESUME)
{
FILE* f1 = ::fopen("c:\\smil2AddDuration.txt", "a+");
::fprintf(f1, "CSmilDocumentRenderer::handleTrackPausing(ID=%s,"
        " timeOfPause=%ld, ID of pauser=%s)\n",
      (const char*)pNode->m_id, lTimeOfPause, (const char*)pIdOfPauser);
::fclose(f1);
}
#endif

                rc = pHXTrack->Pause();

                // /Ceate a new SmilTimeValue watching for endEvent
                // on pIdOfPauser
                SmilTimeValue* pTimeVal = new SmilTimeValue(m_pContext,
                      /* Don't care what start line is:*/ 0,
                      pNode->m_pElement);
                if(pTimeVal)
                {
                  CHXString pStr = pIdOfPauser;
                  pStr += ".resumeEvent";
                  HX_RESULT pnr1 = pTimeVal->parseValue(pStr,
                        SMILSyncAttrBegin, (const char*)pNode->m_id);

                  HX_RESULT pnr2= pTimeVal->setPauseTime(lTimeOfPause);
                  HX_ASSERT(HXR_OK == pnr2);

                  // /Note: this function takes care of getting rid of
                  // any existing resumeEvent on this element; since it
                  // can only be in the "resume stack" once:
                  BOOL bOldResumeEventWasRemoved = FALSE;
                  HX_RESULT rslt = m_pSmilParser->addResumeEvent(
                        pTimeVal, bOldResumeEventWasRemoved);
#if defined(_DEBUG)
                  if (bOldResumeEventWasRemoved)
                  {
                      bOldResumeEventWasRemoved = 1;// /DEBUG-ONLY CODE
                  }
#endif
                }
            }
            HX_RELEASE(pHXTrack);
          }
      }
        HX_RELEASE(pGroup2);
    }

    return rc;
}

// /Immediately resumes a track that's associated with the source:
// Returns HXR_OK only if the track was found and PlayTrack(n)
// succeeded:
HX_RESULT
CSmilDocumentRenderer::handleTrackResuming(const char* pID, INT32 nGroupNum)
{
    HX_RESULT rc = HXR_FAILED;

    IHXGroup* pGroup = 0;
    if(m_pGroupMap && 
       m_pGroupMap->Lookup(nGroupNum, (void*&)pGroup))
    {
      // /QI for IHXGroup2
      IHXGroup2* pGroup2 = NULL;
      pGroup->QueryInterface(IID_IHXGroup2, (void**) &pGroup2);
      if (pGroup2)
      {
          // /XXXEH- TODO: OPTIMIZATION: use "getPlayToAssocByMedia(pID)"
          // here instead (?):
          // /Determine whether this source has been started yet:
          SMILPlayToAssoc* pPlayToAssoc = NULL;
          if(m_pPlayToAssocList)
          {
            CHXSimpleList::Iterator i;
            for(i=m_pPlayToAssocList->Begin();
                  i!=m_pPlayToAssocList->End();++i)
            {
                SMILPlayToAssoc* pThisAssoc = (SMILPlayToAssoc*)(*i);
                if (pThisAssoc->m_id == pID  &&
                      // /Added this while re-fixing PR 69639; be sure to
                      // ignore already-stopped track & keep looking in
                      // case element restart made new track w/same id:
                      !pThisAssoc->m_bTrackStopped)
                {
                  pPlayToAssoc = pThisAssoc;
                  break;
                }
            }
          }
          HX_ASSERT(pPlayToAssoc);
          if (pPlayToAssoc)
          {
            IHXTrack* pHXTrack = NULL;
            rc = pGroup2->GetIHXTrack(pPlayToAssoc->m_uTrackIndex,
                  pHXTrack);
            HX_ASSERT(pHXTrack);
            if (pHXTrack)
            {
                if (isMediaPausedAndDisabled(pID))
                {
                  // /Handle un-blocking of mouse events and changing
                  // site appearance back to normal:
                  if (!reenablePausedAndDisabledMedia(pID,
                        (UINT16)nGroupNum))
                  {
                      HX_ASSERT(0);
                  }
                }
                // /XXXEH- TODO- OPTIMIZATION: only need to do this if
                // pauseDisplay == "hide" but we'll need a map for that,
                // as well:
                else
                {
                  CSmilBasicRegion* pRegion = NULL;
                  if(pPlayToAssoc)
                  {
                      // First, assume that pPlayTo is an id and see
                      // if we can find the region by id:
                      pRegion = getRegionByID(pPlayToAssoc->m_playTo);
                      if (!pRegion)
                      {
                        // We didn't find it by id, so try to find it
                        // by regionName:
                        pRegion = getFirstRegionByName(
                              pPlayToAssoc->m_playTo);
                      }
                  }

                  if (pRegion  &&  m_pSiteInfoList)
                  {
                      CHXSimpleList::Iterator i =
                            m_pSiteInfoList->Begin();
                      for(; i != m_pSiteInfoList->End(); ++i)
                      {
                        SMILSiteInfo* pSiteInfo =(SMILSiteInfo*)(*i);
                        if(pSiteInfo->m_uGroupIndex ==
                              m_uCurrentGroupIndex  &&
                              pSiteInfo->m_MediaID ==
                              pPlayToAssoc->m_id)
                        {
                            IHXSite* pRegionSite = NULL;
                            CSmilBasicRegion* pCurRegion =
                                  getRegionByID(
                                  pSiteInfo->m_regionID);
                            if (pCurRegion  !=  pRegion)
                            {
                              HX_ASSERT(pRegion);
                              continue;
                            }

                            // /Helps fix PR 81510 (& dup. PR 83796):
                            // z-ordering code can put it at top of
                            // z-order of siblings who have already
                            // played to same region or otherwise have
                            // same z-index, by looking at this value:
                            if (m_ulCurrentTime > 0)
                            {
                              pSiteInfo->m_ulResumeTime =
                                    m_ulCurrentTime-1;
                            }

                            if(pRegion->m_eBackgroundColorType ==
                                  CSS2TypeTransparent)
                            {
                              pRegionSite =
                                    pSiteInfo->m_pRegionSite;
                            }
                            // /Show site as it resumes:
                            CSmilShowSiteEvent* pShowEvent = 
                                  new CSmilShowSiteEvent(
                                  pPlayToAssoc->m_uGroupIndex,
                                  // /Add a few millisec so it
                                  // won't re-hide when it starts:
                                  m_ulCurrentTime + 10, 
                                  pSiteInfo->m_pRendererSite,
                                  pRegion->m_pSite,
                                  TRUE, // /ShowSite=TRUE
                                  FALSE,
                                  this,
                                  pPlayToAssoc->m_id,
                                  pRegion->m_region,
                                  pRegion->m_eShowBackground);
                            insertEvent(pShowEvent);
                        }
                      }
                  }
                }

#if defined(_DEBUG)  &&  defined(XXXEH_DEBUGOUT_TRACKSTOPPAUSERESUME)
{
FILE* f1 = ::fopen("c:\\smil2AddDuration.txt", "a+");
::fprintf(f1, "CSmilDocumentRenderer::handleTrackResuming(ID=%s, group=%ld)\n",
      (const char*)pID, nGroupNum);
::fclose(f1);
}
#endif

                rc = pHXTrack->Begin();
            }
            HX_RELEASE(pHXTrack);
          }
      }
      HX_RELEASE(pGroup2);
    }

    return rc;
}

HX_RESULT CSmilDocumentRenderer::handleEndLayout(CSmilEndLayout* pElement)
{
    HX_RESULT retVal = HXR_OK;

    // Set the flag saying we DO have a <layout> element
    m_bLayoutElementPresent = TRUE;
    // Decide if we have an "empty layout" condition, which
    // is where we just have:
    // <layout>
    // </layout>
    // but nothing inside it. So therefore we have empty layout
    // if at this point all of the following are true:
    // a) we have no <root-layout> element
    // b) we have no <region> elements
    // c) we have no <regPoint> elements
    // d) we have no <topLayout> elements
    if ((!m_pRegionMap    ||
         (m_pRegionMap &&  m_pRegionMap->GetCount() == 0)) &&
        (!m_pRootLayout   ||
         (m_pRootLayout && !m_pRootLayout->IsRootLayoutElementPresent())) &&
        (!m_pRegPointMap  ||
         (m_pRegPointMap && m_pRegPointMap->GetCount() == 0)) &&
        (!m_pViewportList ||
         (m_pViewportList && m_pViewportList->GetCount() == 0)))
    {
        m_bEmptyLayout = TRUE;
    }
    // Decide if we need to set up the root-layout at 
    // this point or later. We should only attempt to
    // setup the root layout here if either: a) both
    // root layout width and height are valid; OR
    // b) we have some <region> children of the root layout
    // defined. If we have (a), we know we will succeed,
    // but if (b) we may or may not succeed.
    if (m_pRootLayout &&
        ((m_pRootLayout->IsWidthSet() &&
          m_pRootLayout->IsHeightSet()) ||
         (m_pRootLayout->m_pChildList &&
          m_pRootLayout->m_pChildList->GetCount() > 0)))
    {
        // We need to attempt to set up the root layout
        // without yet assigning defaults
        HX_RESULT rv = setupRootLayout(FALSE);
        if (SUCCEEDED(rv))
        {
            // Set the flag saying we've done the
            // root-layout setup
            m_bIsRootLayoutSetup = TRUE;
        }
        else
        {
            // Ok, we have regions which are children
            // of the root-layout, but we could not
            // groc the size of the root-layout from
            // the regions. So we will try again, this
            // time assigning defaults if necessary.
            // This should succeed.
            rv = setupRootLayout(TRUE);
            if (SUCCEEDED(rv))
            {
                // Set the flag saying we've done the
                // root-layout setup
                m_bIsRootLayoutSetup = TRUE;
            }
        }
    }

#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)
    // Decide if we need to do viewport setup here.
    // We should only attempt to do viewport setup
    // here if, of course, there is at least one
    // viewport defined.
    if (m_pViewportList &&
        m_pViewportList->GetCount() > 0)
    {
        // Setup any viewports
        HX_RESULT rv = setupViewports();
    }
#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */

    return retVal;
}

HX_RESULT
CSmilDocumentRenderer::handleMeta(CSmilMeta* pElement)
{
    HX_RESULT rc = HXR_OK;

    IHXPlayer* pPlayer = m_pParent->getPlayer();
    IHXGroupManager* pMgr = NULL;
    IHXValues* pValues = NULL;

    if(pElement->m_name.GetLength() > 0)
    {
      if(HXR_OK == pPlayer->QueryInterface(IID_IHXGroupManager, (void**)&pMgr))
      {
          pValues = pMgr->GetPresentationProperties();
          if(!pValues)
          {
            pValues = new CHXOrderedValues;
            pValues->AddRef();
            pMgr->SetPresentationProperties(pValues);
          }

          IHXBuffer* pBuf = new CHXBuffer;
          pBuf->AddRef();
          pBuf->Set((BYTE*)(const char*)pElement->m_content,
            pElement->m_content.GetLength()+1);
          pValues->SetPropertyCString((const char*)pElement->m_name,
            pBuf);
          pBuf->Release();
          pValues->Release();
          pMgr->Release();
      }
    }

    return rc;
}

HX_RESULT
CSmilDocumentRenderer::handleMetadata(CSmilMetadata* pElement)
{
    HX_RESULT rc = HXR_OK;

    if (m_bMetadataPassedOffAlready) // /We send all metadata in one big chunk.
    {
      return rc;
    }

    m_bMetadataPassedOffAlready = TRUE;

    IHXPlayer* pPlayer = m_pParent->getPlayer();
    IHXGroupManager* pMgr = NULL;
    IHXValues* pValues = NULL;

    if(m_metadata.GetLength() > 0)
    {
      if(HXR_OK == pPlayer->QueryInterface(IID_IHXGroupManager, (void**)&pMgr))
      {
          pValues = pMgr->GetPresentationProperties();
          if(!pValues)
          {
            pValues = new CHXOrderedValues;
            pValues->AddRef();
            pMgr->SetPresentationProperties(pValues);
          }

          IHXBuffer* pBuf = new CHXBuffer;
          pBuf->AddRef();
          pBuf->Set((BYTE*)(const char*)m_metadata,
            m_metadata.GetLength()+1);
          pValues->SetPropertyCString("metadata", pBuf);
          pBuf->Release();
          pValues->Release();
          pMgr->Release();
      }
    }
#if defined (XXX_DUMP_METADATA_TO_FILE)
{FILE* f = ::fopen("c:\\temp\\metadata.txt", "w");
    if (f)
    {
      ::fprintf(f,"METADATA:{{{\n%s\n}}}\n", (const char*)m_metadata);
      ::fclose(f);
    }
}
#endif

    return rc;
}

HX_RESULT
CSmilDocumentRenderer::handleRendererPreFetch(CSmilRendererPreFetch *pRend)
{
    HX_RESULT rc = HXR_OK;
    const char* pMimeType = (const char*)pRend->m_mimeType;

    IHXRendererUpgrade* pUpgrade = 0;

    if(m_pContext)
    {
      IHXSystemRequired* pISystemRequired = NULL;
      m_pContext->QueryInterface(IID_IHXSystemRequired, 
          (void**)&pISystemRequired);

      CHXBuffer* pBuffer = new CHXBuffer;
      pBuffer->AddRef();
      pBuffer->Set((BYTE*)pMimeType, strlen(pMimeType)+1);
      if (pISystemRequired)
      {
          IHXUpgradeCollection* pUpgradeCollection = NULL;
          IHXPlayer* pPlayer = m_pParent->getPlayer();          
          if(pPlayer)
            pPlayer->QueryInterface(IID_IHXUpgradeCollection, (void**)&pUpgradeCollection);

          if(pUpgradeCollection)
          {
            pUpgradeCollection->Add(eUT_Required, pBuffer, 0, 0);
            // HasFeatures() calls removes all existing features from pUpgradeCollection.
            pISystemRequired->HasFeatures(pUpgradeCollection);
          }
          HX_RELEASE(pUpgradeCollection);
          HX_RELEASE(pISystemRequired);
      }

      HX_RELEASE(pBuffer);
    }

    return rc;
}

HX_RESULT
CSmilDocumentRenderer::insertEvent(CSmilLayoutEvent* pEvent)
{
    const char* pszMediaID  = "NULL";
    const char* pszRegionID = "NULL";
    if (pEvent &&
        (pEvent->m_type == CSmilLayoutEvent::eShowSite ||
         pEvent->m_type == CSmilLayoutEvent::eHideSite))
    {
        CSmilShowSiteEvent* pShow = (CSmilShowSiteEvent*) pEvent;
        pszMediaID  = pShow->getMediaID();
        pszRegionID = pShow->getRegionID();
    }
    MLOG_EVENT(m_pErrorMessages,
               "insertEvent() (%s,%lu,%u,%lu,%lu,%s,%s) tick=%lu\n",
               pEvent->getEventTypeName(),
               pEvent->m_ulEventTime,
               pEvent->m_uGroupIndex,
               pEvent->m_bOnlyHideSite,
               pEvent->m_bIgnorEvent,
               pszMediaID,
               pszRegionID,
               HX_GET_BETTERTICKCOUNT());
    if(!m_pEventList)
    {
      m_pEventList = new CHXSimpleList;
    }

    LISTPOSITION lPos = m_pEventList->GetHeadPosition();
    LISTPOSITION lPrev = lPos;

    BOOL bInserted = FALSE;
    BOOL bFoundOurShow = FALSE;
    while(lPos)
    {
      CSmilLayoutEvent* pThisEvent = 
          (CSmilLayoutEvent*)m_pEventList->GetNext(lPos);

      if((pThisEvent->m_type == CSmilLayoutEvent::eShowSite || 
          pThisEvent->m_type == CSmilLayoutEvent::eHideSite) &&
          pThisEvent->m_ulEventTime == pEvent->m_ulEventTime &&
          pThisEvent->getRegionSite() == pEvent->getRegionSite())
      {
          if(pEvent->m_type == CSmilLayoutEvent::eShowSite)
          {
            if(!lPos)
            {
                m_pEventList->AddTail(pEvent);
                bInserted = TRUE;
                break;
            }

            // find position of last 'hide' event at this time
            // move past all non show events at this time...
            while(lPos &&
                pThisEvent->m_ulEventTime == pEvent->m_ulEventTime &&
                pThisEvent->getRegionSite() == pEvent->getRegionSite() &&
                pThisEvent->m_type != CSmilLayoutEvent::eShowSite)
            {
                lPrev = lPos;
                pThisEvent = (CSmilLayoutEvent*)m_pEventList->GetNext(lPos);
            }
          }

          // move past any Transition events at this time...
          while (lPos &&
            pThisEvent->m_ulEventTime == pEvent->m_ulEventTime &&
            (pThisEvent->m_type == CSmilLayoutEvent::eBeginTransition ||
            pThisEvent->m_type == CSmilLayoutEvent::eEndTransition) )
          {
            lPrev = lPos;
            pThisEvent = (CSmilLayoutEvent*)m_pEventList->GetNext(lPos);
          }
          
          m_pEventList->InsertBefore(lPrev, pEvent);
          bInserted = TRUE;
          break;
      }
      if ((pEvent->m_type == CSmilLayoutEvent::eBeginTransition ||
          pEvent->m_type == CSmilLayoutEvent::eEndTransition) &&
          pThisEvent->m_ulEventTime == pEvent->m_ulEventTime)
      {
          // we want transitions to be put before this event..
          m_pEventList->InsertBefore(lPrev, pEvent);
          bInserted = TRUE;
          break;
      }
      else if(pThisEvent->m_ulEventTime > pEvent->m_ulEventTime)
      {
          if (pEvent->m_type == CSmilLayoutEvent::eHideSite)
          {
            // if we are a hide event, check to see if there is 
            // a hide event in the future for our same region, and
            // a different renderer site, if so, modifiy this
            // event so it will only hide the renderer...

            // lPos  -- position of pThisEvent (past were we want to be inserted)
            // lPrev -- start here so we will also consider pThisEvent..
            LISTPOSITION next = lPrev;
            while (next)
            {
                CSmilLayoutEvent* pFutureEvent = (CSmilLayoutEvent*)m_pEventList->GetAt(next);
                
                if (pFutureEvent->getRegionSite() == pEvent->getRegionSite() &&
                  pFutureEvent->getRendererSite() != pEvent->getRendererSite())
                {
                  if (pFutureEvent->m_type == CSmilLayoutEvent::eHideSite)
                  {
                      pEvent->m_bOnlyHideSite = TRUE;
                      break;
                  }
                  // as soon as we find a show event for this same region
                  // and a different site, we can stop.
                  else if (pFutureEvent->m_type == CSmilLayoutEvent::eShowSite)
                  {
                      break;
                  }
                }
                m_pEventList->GetNext(next);
            }
          }
          
          m_pEventList->InsertBefore(lPrev, pEvent);
          bInserted = TRUE;
          break;
      }
      else if (pEvent->m_type == CSmilLayoutEvent::eHideSite &&
          pThisEvent->m_type == CSmilLayoutEvent::eShowSite &&
          pThisEvent->getRegionSite() == pEvent->getRegionSite() &&
          pThisEvent->getRendererSite() == pEvent->getRendererSite())
      {
          // note that we have passed the show event for our site
          bFoundOurShow = TRUE;
      }
      else if (pEvent->m_type == CSmilLayoutEvent::eHideSite &&
          pThisEvent->m_type == CSmilLayoutEvent::eHideSite &&
          pThisEvent->getRegionSite() == pEvent->getRegionSite() &&
          pThisEvent->getRendererSite() != pEvent->getRendererSite()
          && bFoundOurShow)
      {
          // if we are inserting a hide event, after the current hide event,
          // and the current hide event goes to the same region as the current 
          // hide event, && the renderer sites are different, then we need
          // to modify this event so that it does not hide the region's site...
          pThisEvent->m_bOnlyHideSite = TRUE;
      }

      lPrev = lPos;
    }
    if(!bInserted)
    {
      // not inserted, stick it on the end of the list
      m_pEventList->AddTail(pEvent);
    }

    // set list position member
    m_ulEventListPosition = m_pEventList->GetHeadPosition();

    MLOG_EVENT(m_pErrorMessages, "\tdumping event queue after insertion\n");
//    DumpEventQueue();

    return HXR_OK;
}

CSmilShowSiteEvent* CSmilDocumentRenderer::getShowHideEvent(const char* pszMediaID,
                                                            const char* pszRegionID,
                                                            BOOL        bShowEvent)
{
    CSmilShowSiteEvent* pRet = NULL;

    if (m_pEventList && pszMediaID && pszRegionID)
    {
        LISTPOSITION pos = m_pEventList->GetHeadPosition();
        while (pos)
        {
            CSmilLayoutEvent* pEvent =
                (CSmilLayoutEvent*) m_pEventList->GetNext(pos);
            if (pEvent &&
                (pEvent->m_type == CSmilLayoutEvent::eShowSite ||
                 pEvent->m_type == CSmilLayoutEvent::eHideSite))
            {
                CSmilShowSiteEvent* pShowEvent = (CSmilShowSiteEvent*) pEvent;
                if (pShowEvent->getShowSite() == bShowEvent &&
                    !strcmp(pszMediaID, pShowEvent->getMediaID()) &&
                    !strcmp(pszRegionID, pShowEvent->getRegionID()))
                {
                    pRet = pShowEvent;
                    break;
                }
            }
        }
    }

    return pRet;
}

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)

CSmilAnimateEvent* CSmilDocumentRenderer::getAnimateEvent(CSmilAnimateElement* pAnim)
{
    CSmilAnimateEvent* pRet = NULL;

    if (m_pEventList && pAnim)
    {
        LISTPOSITION pos = m_pEventList->GetHeadPosition();
        while (pos)
        {
            CSmilLayoutEvent* pEvent =
                (CSmilLayoutEvent*) m_pEventList->GetNext(pos);
            if (pEvent &&
                pEvent->m_type == CSmilLayoutEvent::eAnimate)
            {
                CSmilAnimateEvent* pAnimEvent = (CSmilAnimateEvent*) pEvent;
                if (pAnimEvent->isSameElement(pAnim))
                {
                    pRet = pAnimEvent;
                    break;
                }
            }
        }
    }

    return pRet;
}

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

void CSmilDocumentRenderer::removeEvent(CSmilLayoutEvent* pEvent)
{
    if (pEvent && m_pEventList)
    {
        LISTPOSITION pos = m_pEventList->GetHeadPosition();
        while (pos)
        {
            CSmilLayoutEvent* pListEvent =
                (CSmilLayoutEvent*) m_pEventList->GetAt(pos);
            if (pListEvent == pEvent)
            {
                m_pEventList->RemoveAt(pos);
                break;
            }
            m_pEventList->GetNext(pos);
        }
    }
}

HX_RESULT
CSmilDocumentRenderer::insertSiteInfo(void* pVoidInfo)
{
    if(!m_pSiteInfoList)
    {
      m_pSiteInfoList = new CHXSimpleList;
    }

    SMILSiteInfo* pInfo = (SMILSiteInfo*)pVoidInfo;

    LISTPOSITION lPos = m_pSiteInfoList->GetHeadPosition();
    LISTPOSITION lPrev = lPos;

    BOOL bInserted = FALSE;
    while(lPos)
    {
      SMILSiteInfo* pThisInfo = 
          (SMILSiteInfo*)m_pSiteInfoList->GetNext(lPos);

      if(pThisInfo->m_ulDelay > pInfo->m_ulDelay)
      {
          m_pSiteInfoList->InsertBefore(lPrev, pInfo);
          bInserted = TRUE;
          break;
      }
      lPrev = lPos;
    }
    if(!bInserted)
    {
      // not inserted, stick it on the end of the list
      m_pSiteInfoList->AddTail(pInfo);
    }

    return HXR_OK;
}

void CSmilDocumentRenderer::removeSiteInfo(SMILSiteInfo* pSiteInfo)
{
    if (m_pSiteInfoList && pSiteInfo)
    {
        LISTPOSITION pos = m_pSiteInfoList->GetHeadPosition();
        while (pos)
        {
            SMILSiteInfo* pInfo =
                (SMILSiteInfo*) m_pSiteInfoList->GetAt(pos);
            if (pInfo == pSiteInfo)
            {
                m_pSiteInfoList->RemoveAt(pos);
                break;
            }
            m_pSiteInfoList->GetNext(pos);
        }
    }
}

HX_RESULT
CSmilDocumentRenderer::onTimeSync(UINT32 ulTimeValue)
{
    MLOG_TIMESYNC(m_pErrorMessages, "onTimeSync(%lu) tick=%lu\n", ulTimeValue,
                  HX_GET_BETTERTICKCOUNT());
    HX_RESULT rc = HXR_OK;
    m_ulCurrentTime = ulTimeValue;

    if(!m_bFirstTimeSync)
    {
      // draw background and regions
      m_bFirstTimeSync = TRUE;

      // now I should force a background redraw...
      if(m_pRootLayout->m_pSite)
      {
            forceFullRedraw(m_pRootLayout->m_pSite);

          if(m_pRegionMap)
          {
            CHXMapStringToOb::Iterator i = m_pRegionMap->Begin();
            for(; i != m_pRegionMap->End(); ++i)
            {
                CSmilBasicRegion* pRegion = (CSmilBasicRegion*)(*i);
                if (pRegion)
                {
                        forceFullRedraw(pRegion->m_pSite);
                }
            }
          }
      }
    }

    rc = flushAllEvents( ulTimeValue, TRUE );

    // /Helps fix PR 66391: it should now be OK to call RemoveTrack() on each
    // track that was added and subsequently found to begin too late to play
    // once its parent's duration became resolved:
    if (m_pDeferrededRemoveTrackMap)
    {
      CHXMapStringToOb::Iterator i = m_pDeferrededRemoveTrackMap->Begin();
      for(; i != m_pDeferrededRemoveTrackMap->End(); ++i)
      {
          CHXString* pID = (CHXString*)(*i);
            // Get the playTo for this media and then the group and track#:
            SMILPlayToAssoc* pPlayToAsso = pID?
                // /Changing to (*pID) instead of (pID) properly fixes
                // PR 66391 which wasn't working for implicit IDs.  It's a
                // bit scary that the prior code *worked* for explicit IDs
                // which were stored in the smallString of the CHXString:
                getPlayToAssocByMedia((const char*)(*pID)) : NULL;
          HX_ASSERT(pPlayToAsso);
            if (pPlayToAsso)
            {
                UINT16 usGroup = pPlayToAsso->m_uGroupIndex;
            IHXGroup* pGroup = 0;
            if (m_pGroupMap  &&  m_pGroupMap->Lookup(usGroup, (void*&)pGroup))
            {
                HX_RESULT pnrRemoveTrackRetVal =
                      pGroup->RemoveTrack(pPlayToAsso->m_uTrackIndex);
                HX_ASSERT(HXR_OK == pnrRemoveTrackRetVal);
            }
          }
          HX_DELETE(pID); // /Whole list gets deleted, below.
      }
      // /Delete the whole list:
      HX_DELETE(m_pDeferrededRemoveTrackMap);
    }

    if (m_pActiveTransitions)
    {
      // cycle through all the active transitions...
      LISTPOSITION lpos = m_pActiveTransitions->GetHeadPosition();
      while (lpos)
      {
          SMILTransitionState* pState = 
            (SMILTransitionState*)m_pActiveTransitions->GetAt(lpos);
          if (ulTimeValue < pState->m_ulEndTime)
          {
            doTransition(lpos, ulTimeValue);
            m_pActiveTransitions->GetNext(lpos);
          }
          else
          {
            doTransition(lpos, pState->m_ulEndTime);
            lpos = m_pActiveTransitions->RemoveAt(lpos);
            HX_RELEASE(pState->m_pSiteTransition);
            HX_DELETE(pState);
          }
      }
    }

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)

    // We want to cap the time at the duration of
    // the current group - we don't want to send
    // a timesync that is greater than the duration.
    UINT32 ulAnimTime = ulTimeValue;
    if (ulAnimTime > m_ulCurGroupDuration &&
        m_ulCurGroupDuration > 0)
    {
        ulAnimTime = m_ulCurGroupDuration;
    }

    // Do all the active animations
    if (m_pActiveAnimations && m_pActiveAnimations->GetCount() > 0)
    {
        if (atLeastOneActiveAnimation(ulAnimTime))
        {
            if (!isSiteCompositionModeON())
            {
                turnSiteCompositionModeON();
            }
        }
        // m_pAnimSiteRedrawMap is a list of sites
        // which need to ForceRedraw() as a result on an
        // animation on this time sync.
        if (!m_pAnimSiteRedrawMap)
        {
            m_pAnimSiteRedrawMap = new CHXMapPtrToPtr();
        }
        if (m_pAnimSiteRedrawMap)
        {
            m_pAnimSiteRedrawMap->RemoveAll();
        }
        // m_pAnimRegionRecomputeMap is a list of sites
        // which need to ForceRedraw() as a result on an
        // animation on this time sync.
        if (!m_pAnimRegionRecomputeMap)
        {
            m_pAnimRegionRecomputeMap = new CHXMapPtrToPtr();
        }
        if (m_pAnimRegionRecomputeMap)
        {
            m_pAnimRegionRecomputeMap->RemoveAll();
        }
        // m_pAnimTopLayoutMap is a collection of topLayout's
        // which have been animated programmatically and
        // need resizing.
        if (m_pAnimTopLayoutMap)
        {
            m_pAnimTopLayoutMap->RemoveAll();
        }
        // Clear the animate root layout flag
        m_bAnimateRootLayout = FALSE;
        // If there is a soundlevel animation going on, then
        // we need to try to grab the mutex
        if (m_pSoundLevelMutex) m_pSoundLevelMutex->Lock();
        // Send this time to all active animations
        LISTPOSITION pos = m_pActiveAnimations->GetHeadPosition();
        while (pos)
        {
            CSmilAnimateInfo* pInfo = (CSmilAnimateInfo*) m_pActiveAnimations->GetAt(pos);
            if (pInfo && pInfo->m_pSandwich &&
                pInfo->m_pSandwich->GetAttributeName() != kAttrNameSoundLevel)
            {
                // Execute the animation at this time
                doAnimation(pInfo, ulAnimTime);
                // Are there any layers left in the sandwich?
                if (pInfo->m_pSandwich->GetNumLayers())
                {
                    // There are still some layers left, so
                    // leave the sandwich alone and just advance
                    // to the next animation
                    m_pActiveAnimations->GetNext(pos);
                }
                else
                {
                    // There are no more layers left in the
                    // sandwich, so this animation is done. So,
                    // we need to restore whatever original values
                    // were present
                    setValue(pInfo, pInfo->m_pUnder, pInfo->m_pDepend, TRUE);
                    // Now we need to delete this info object
                    HX_DELETE(pInfo);
                    // And remove it from the list
                    // We have to do the following hack since
                    // CHXSimpleList does not advance the position to 
                    // NULL if you RemoveAt() the last element in the list.
                    BOOL bLast = (m_pActiveAnimations->GetAt(pos) ==
                                  m_pActiveAnimations->GetTail());
                    pos        = m_pActiveAnimations->RemoveAt(pos);
                    if (bLast && pos)
                    {
                        m_pActiveAnimations->GetNext(pos);
                    }
                }
            }
            else
            {
                m_pActiveAnimations->GetNext(pos);
            }
        }
        // If there is a soundlevel animation going on, then
        // we can now release the mutex
        if (m_pSoundLevelMutex) m_pSoundLevelMutex->Unlock();
        // Recompute the root-layout if necessary
        if (m_bAnimateRootLayout && m_pRootLayout && m_pRootLayout->m_pRoot)
        {
            HXxSize cNewRootSize = {0, 0};
            cNewRootSize.cx = (INT32) floor(m_pRootLayout->m_pRoot->m_dWidth  + 0.5);
            cNewRootSize.cy = (INT32) floor(m_pRootLayout->m_pRoot->m_dHeight + 0.5);
            SiteSizeChanged(m_pRootLayout->m_pSite, &cNewRootSize);
        }
#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)
        // Animate any topLayout's
        if (m_pAnimTopLayoutMap &&
            m_pAnimTopLayoutMap->GetCount() > 0)
        {
            POSITION mpos = m_pAnimTopLayoutMap->GetStartPosition();
            while (mpos)
            {
                void* pKey = NULL;
                void* pVal = NULL;
                m_pAnimTopLayoutMap->GetNextAssoc(mpos, pKey, pVal);
                if (pKey)
                {
                    CSmilBasicViewport* pVP = (CSmilBasicViewport*) pKey;
                    if (pVP && pVP->m_pPort)
                    {
                        HXxSize cNewRootSize = {0, 0};
                        cNewRootSize.cx = (INT32) floor(pVP->m_pPort->m_dWidth  + 0.5);
                        cNewRootSize.cy = (INT32) floor(pVP->m_pPort->m_dHeight + 0.5);
                        SiteSizeChanged(pVP->m_pSite, &cNewRootSize);
                    }
                }
            }
            // Clear the map
            m_pAnimTopLayoutMap->RemoveAll();
        }
#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */
        // We've collected all the regions for which
        // we need to call recomputeBoxLayout()
        if (m_pAnimRegionRecomputeMap)
        {
            POSITION mpos = m_pAnimRegionRecomputeMap->GetStartPosition();
            while (mpos)
            {
                void* pKey = NULL;
                void* pVal = NULL;
                m_pAnimRegionRecomputeMap->GetNextAssoc(mpos, pKey, pVal);
                if (pKey)
                {
                    CSmilBasicRegion* pRegion = (CSmilBasicRegion*) pKey;
                    recomputeBoxLayout(pRegion, TRUE);
                }
            }
            // Clear the map
            m_pAnimRegionRecomputeMap->RemoveAll();
        }
        // We've collected all the sites which need
        // a ForceRedraw(), so do that now
        if (m_pAnimSiteRedrawMap)
        {
            POSITION mpos = m_pAnimSiteRedrawMap->GetStartPosition();
            while (mpos)
            {
                void* pKey = NULL;
                void* pVal = NULL;
                m_pAnimSiteRedrawMap->GetNextAssoc(mpos, pKey, pVal);
                if (pKey)
                {
                    IHXSite* pSite = (IHXSite*) pKey;
                    forceFullRedraw(pSite);
                }
            }
            // Clear the map
            m_pAnimSiteRedrawMap->RemoveAll();
        }
        unlockSiteComposition();
        bltSiteComposition();
        lockSiteComposition();
    }

    if (!atLeastOneActiveAnimation(ulAnimTime))
    {
        if (isSiteCompositionModeON())
        {
            turnSiteCompositionModeOFF();
//            forceFullRecursiveRedraw(m_pRootLayout);
        }
    }

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

    BOOL bSomeElementScheduleWasChanged = FALSE;
    CHXSimpleList pauseDisplayHideElementList;
    CHXSimpleList pauseDisplayDisableElementList;

    if (m_pHandlePendingSchedulingCallback &&
      m_pHandlePendingSchedulingCallback->m_bIsCallbackPending)
    {
      m_pHandlePendingSchedulingCallback->m_bIsCallbackPending = FALSE;
      m_pScheduler->Remove(m_pHandlePendingSchedulingCallback->m_PendingHandle);
      m_pHandlePendingSchedulingCallback->m_PendingHandle = 0;
    }

    HX_RESULT tmprslt =
          m_pSmilParser->handlePendingScheduling(m_ulCurrentTime,
          m_uCurrentGroupIndex,
          bSomeElementScheduleWasChanged,
          &pauseDisplayHideElementList,
          &pauseDisplayDisableElementList);
    if (SUCCEEDED(tmprslt)  &&  bSomeElementScheduleWasChanged)
    {
      CHXSimpleList* pWhichList = NULL;
      BOOL bDoHide = TRUE;
      if (!pauseDisplayHideElementList.IsEmpty())
      {
          pWhichList = &pauseDisplayHideElementList;
          bDoHide = TRUE;
      }
      else if (!pauseDisplayDisableElementList.IsEmpty())
      {
          pWhichList = &pauseDisplayDisableElementList;
          bDoHide = FALSE;
      }
      if (pWhichList)
      {
          LISTPOSITION listPos =
                pWhichList->GetHeadPosition();
          while (listPos)
          {
            LISTPOSITION lPosOfCurTmpVal = listPos;
            // /Gets val at listPos and then moves listPos to next node:
            CHXString* pCStrId = (CHXString*)pWhichList->GetNext(listPos);
            if (pCStrId)
            {
                SMILPlayToAssoc* pPlayToAssoc = getPlayToAssocByMedia(
                      (const char*)(*pCStrId));
                CSmilBasicRegion* pRegion = NULL;
                if(pPlayToAssoc)
                {
                  // First, assume that pPlayTo is an id and see
                  // if we can find the region by id:
                  pRegion = getRegionByID(pPlayToAssoc->m_playTo);
                  if (!pRegion)
                  {
                      // We didn't find it by id, so try to find it
                      // by regionName:
                      pRegion = getFirstRegionByName(
                            pPlayToAssoc->m_playTo);
                  }
                }
                HX_ASSERT(pRegion  &&  m_pSiteInfoList);
                if (pRegion)
                {
                  if(m_pSiteInfoList)
                  {
                      CHXSimpleList::Iterator i =
                            m_pSiteInfoList->Begin();
                      for(; i != m_pSiteInfoList->End(); ++i)
                      {
                        SMILSiteInfo* pSiteInfo =(SMILSiteInfo*)(*i);
                        if(pSiteInfo->m_uGroupIndex ==
                              m_uCurrentGroupIndex  &&
                              pSiteInfo->m_MediaID ==
                              pPlayToAssoc->m_id)
                        {
                            IHXSite* pRegionSite = NULL;
                            CSmilBasicRegion* pCurRegion =
                                  getRegionByID(
                                  pSiteInfo->m_regionID);
                            if (pCurRegion  !=  pRegion)
                            {
                              HX_ASSERT(pRegion);
                              continue;
                            }
                            if(pRegion->m_eBackgroundColorType ==
                                  CSS2TypeTransparent)
                            {
                              pRegionSite =
                                    pSiteInfo->m_pRegionSite;
                            }
                            if (bDoHide)
                            {
                              // /Hide site while it's paused:
                              CSmilShowSiteEvent* pHideEvent = 
                                    new CSmilShowSiteEvent(
                                    pPlayToAssoc->m_uGroupIndex,
                                    // /Add a few millisec so it
                                    // won't "unhide" when other
                                    // track starts (fixes
                                    // PR 50708):
                                    m_ulCurrentTime + 10, 
                                    pSiteInfo->m_pRendererSite,
                                    pRegion->m_pSite,
                                    FALSE, // /ShowSite=FALSE
                                    FALSE,
                                    this,
                                    pPlayToAssoc->m_id,
                                    pRegion->m_region,
                                    pRegion->m_eShowBackground);
                              insertEvent(pHideEvent);
                              // /Note: don't break here as there
                              // might be multiple sites to which
                              // this media is currently playing:
                            }
                            else // /pauseDisplay == "disable":
                            {
#if defined(XXXEH_NEED_TO_ADD_51_PRCNT_OPAQUE_SITE_ON_TOP_TO_DISABLE)
                              // /Create a child of the region site
                              IHXSite* pTempSite = NULL;
                              HX_RESULT retVal = pSiteInfo->
                                    m_pRendererSite->CreateChild(
                                    pTempSite);
                              if (SUCCEEDED(retVal))
                              {
                                  // /Add the site to the manager:
                                  retVal = m_pSiteMgr->AddSite(
                                        pTempSite);
                                  HXxSize cSize;
                                  pSiteInfo->m_pRendererSite->GetSize(cSize);
                                  HXxPoint cPos;
                                  pSiteInfo->m_pRendererSite->GetPosition(cPos);
                                  pTempSite->SetSize(cSize);
                                  pTempSite->SetPosition(cPos);
                                  if (SUCCEEDED(retVal))
                                  {
                                    showSite(pTempSite, TRUE);
                                    // /Do we need to do this too?!:
                                    HXxRect cRect = {0, 0, cSize.cx, cSize.cy};
                                    pTempSite->DamageRect(cRect);
                                    pTempSite->ForceRedraw();
                                  }
                              }
#else /* Otherwise just disable the site so it won't handle mouse events: */
      /*Also, QI the renderer site for IHXVideoControl (YUV renderers only)
        and adjust the brightness down by 50% */
                              // /Put media ID into the map:
                              if(!m_pPausedAndDisabledIDMap)
                              {
                                  m_pPausedAndDisabledIDMap =
                                        new CHXMapStringToOb;
                              }
                              
                              CHXString* pCStr = new CHXString;
                              const char* pID = (const char*)
                                    (*pCStrId);
                              if (pCStr)
                              {
                                  *pCStr = pID;
                                  // /Prevents mem leak when pID is
                                  // already in the map:
                                  CHXString* pTmpCStr;
                                  if (m_pPausedAndDisabledIDMap->
                                        Lookup(pID,
                                        (void*&)pTmpCStr))
                                  {
                                    HX_DELETE(pTmpCStr);
                                  }
                                  (*m_pPausedAndDisabledIDMap)[pID] =
                                        pCStr;
                              }
                              // /Now, adjust the brightness of the
                              // site if we can:
                              IHXVideoControl* pVidCntrls = NULL;
                              HX_RESULT pnrVC =
                                    pSiteInfo->m_pRendererSite->
                                    QueryInterface(IID_IHXVideoControl,
                                    (void**)&pVidCntrls);
                              if (SUCCEEDED(pnrVC))
                              {
                                  // /Save this and reset it in
                                  // during handleTrackResuming():
                                  float fBrightness =
                                    pVidCntrls->GetBrightness();

                                  // /Put media ID into the map:
                                  if(!m_pPausedAndDisabledBrightnessMap)
                                  {
                                    m_pPausedAndDisabledBrightnessMap =
                                          new CHXMapStringToOb;
                                  }
                                  
                                  float* pfPrePauseBrightness =
                                        new float;
                                  if (pfPrePauseBrightness)
                                  {
                                    *pfPrePauseBrightness =
                                          fBrightness;
                                    // /Prevents mem leak when pID
                                    // is already in the map:
                                    float* pfTmp;
                                    if (m_pPausedAndDisabledBrightnessMap->
                                          Lookup(pID,
                                          (void*&)pfTmp))
                                    {
                                        HX_DELETE(pfTmp);
                                    }
                                    (*m_pPausedAndDisabledBrightnessMap)[pID] =
                                          pfPrePauseBrightness;
                                  }

                                  float fPausedBrightness =
                                        (fBrightness < -25? -90:
                                        fBrightness - 75);
                                  HX_RESULT rsltSetBrtness =
                                        pVidCntrls->SetBrightness(
                                        fPausedBrightness);
                                  pSiteInfo->m_pRendererSite->
                                        ForceRedraw();
                                  HX_RELEASE(pVidCntrls);
                              }
#endif
                            }
                        }
                      }
                  }
                }
            }
            // /Remove it from the list:
            HX_DELETE(pCStrId);
            pWhichList->RemoveAt(lPosOfCurTmpVal);
          }

      }
      handleElements();
    }
    if (SUCCEEDED(rc))
    {
      rc = tmprslt;
    }

    // /Don't do this at time zero otherwise current player doesn't have
    // time to get up and running:
    if (m_ulCurrentTime > 100)
    {
      // /findNextPendingOnLoadURL finds next "automatic" hyperlink, i.e.,
      // hyperlink whose a/anchor/area tags has 'actuate="onLoad"' in it:
      CHXMapStringToOb* pMapOfAnchorsToLaunchAtCurTime =
            m_pSmilParser->findNextPendingOnLoadURL(m_ulCurrentTime);
      if (pMapOfAnchorsToLaunchAtCurTime)
      {
          // /Now, go through all onLoad anchors found and launch them all:
          CHXMapStringToOb::Iterator i =
                pMapOfAnchorsToLaunchAtCurTime->Begin();
          for(; i != pMapOfAnchorsToLaunchAtCurTime->End(); ++i)
          {
            CSmilAAnchorElement* pAnchor = (CSmilAAnchorElement*)(*i);
            if (!pAnchor)
            {
                HX_ASSERT(pAnchor);
                continue;
            }

            // /NOTE: be sure to do the following LAST in this function in
            // case the launching of an URL replaces this smlrendr instance.
            // /Launch the URL;
            HX_RESULT rcHLT = handleHyperlinkTraversal(pAnchor);
          }
          HX_DELETE(pMapOfAnchorsToLaunchAtCurTime);
      }
    }

    return rc;
}

void
CSmilDocumentRenderer::RemoveEvents(UINT32 ulGroupIndex, IHXSite* pSite)
{
    // handle all events up to time ulFlushToTime + ulGranularity
    HX_RESULT rc = HXR_OK;

    if(m_pEventList)
    {
      LISTPOSITION lPos = m_pEventList->GetHeadPosition();
      while(lPos && m_pEventList->GetCount())
      {
          // handle all events at or before ulTimeValue
          CSmilLayoutEvent* pEvent = (CSmilLayoutEvent*)m_pEventList->GetAt(lPos);
          if(pEvent->m_uGroupIndex == ulGroupIndex &&
             pEvent->getRendererSite() == pSite)
          {
            HX_DELETE(pEvent);
            lPos = m_pEventList->RemoveAt(lPos);
          }
          else
          {
            m_pEventList->GetNext(lPos);
          }
      }

      // set list position member
      m_ulEventListPosition = m_pEventList->GetHeadPosition();
    }

    return;
}

HX_RESULT CSmilDocumentRenderer::flushAllEvents( UINT32 ulFlushToTime, BOOL bBreak)
{
    // handle all events up to time ulFlushToTime + ulGranularity
    HX_RESULT rc = HXR_OK;

    if(m_pEventList && m_pEventList->GetCount() > 0)
    {
      //LISTPOSITION lPos = m_pEventList->GetHeadPosition();
      while(m_ulEventListPosition)
      {
          // handle events which have eventTime<=ulFlushToTime+ulGranularity
          // also only handle events that are in the current group, in case
          // events for the next group have been added to the event list.
          CSmilLayoutEvent* pEvent
            = (CSmilLayoutEvent*) m_pEventList->GetAt(m_ulEventListPosition);
   
#ifdef _MACINTOSH
          // XXXBobClark:
          // On the Mac side, our site's ShowSite implementation
          // draws synchronously: right then. On Windows, it draws
          // asynchronously by setting up a callback. The upshot is
          // that on Windows you can get away with a ShowSite(TRUE)
          // followed immediately by a ShowSite(FALSE), because the
          // actual screen won't get updated while the site is
          // briefly visible. But on the Mac it will show a visible
          // flicker. So the interim workaround here is to check the
          // events we're flushing; if we're showing a site that will
          // be hidden by the time the flush is done (or vice versa),
          // we ignore that event.
          // XXXMEH - Note that this code was written when there was
          // only show and hide events. Now that there are other events,
          // we need to make sure we only operate on show and hide events
          LISTPOSITION tempPos = m_ulEventListPosition;
          BOOL bCurrentEventCancelledOutByFutureEvent = FALSE;
          if (pEvent->IsShowOrHideEvent())
          {
            CSmilShowSiteEvent* pCurrentEvent = (CSmilShowSiteEvent*)pEvent;
            m_pEventList->GetNext(tempPos); // to start with the next position...
            while (tempPos)
            {
                CSmilLayoutEvent* pFutureLayoutEvent = (CSmilLayoutEvent*) m_pEventList->GetAt(tempPos);
                if (!pFutureLayoutEvent || !pFutureLayoutEvent->IsShowOrHideEvent())
                {
                  break;
                }
                CSmilShowSiteEvent* pFutureEvent = (CSmilShowSiteEvent*)m_pEventList->GetAt(tempPos);
                if ( !pFutureEvent
                   || pFutureEvent->m_ulEventTime > ulFlushToTime)
                {
                  break;
                }
                
                if ( pFutureEvent && pCurrentEvent
                      && pCurrentEvent->getRendererSite() == pFutureEvent->getRendererSite()
                      && pCurrentEvent->getRegionSite() == pFutureEvent->getRegionSite()
                      && pCurrentEvent->getShowSite() != pFutureEvent->getShowSite() )
                {
                  bCurrentEventCancelledOutByFutureEvent = TRUE;
                  break;
                }
                
                m_pEventList->GetNext(tempPos);
            }
          }
#endif
            // no need of granularity since the core will
            // ensure we call OnTimeSync at the end of its duration
            if (pEvent &&
                (m_uCurrentGroupIndex == -1 || pEvent->m_uGroupIndex == m_uCurrentGroupIndex) &&
                pEvent->m_ulEventTime <= ulFlushToTime) //XXXMERGE
            {
#ifdef _MACINTOSH
                if (!bCurrentEventCancelledOutByFutureEvent)
                {
#endif
                   if (!pEvent->getHandledFlag())
                   {
                       rc = pEvent->handleEvent(ulFlushToTime);
                       pEvent->setHandledFlag(TRUE);
                   }
#ifdef _MACINTOSH
                }
#endif
            }
            else if( bBreak )
            {
                break;
            }

            BOOL bRemoveEvent = FALSE;
#if defined(HELIX_FEATURE_SMIL2_ANIMATION)
            // If this is an animate event and it begins
            // an animation on some SMIL event, then we
            // should remove this CSmilLayoutEvent from
            // the queue after executing it.
            if (pEvent->m_type == CSmilLayoutEvent::eAnimate)
            {
                CSmilAnimateEvent* pAnimEvent = (CSmilAnimateEvent*) pEvent;
                if (pAnimEvent->hasEventBasedBegin())
                {
                    bRemoveEvent = TRUE;
                }
            }
#endif /* #if defined(HELIX_FEATURE_SMIL2_ANIMATION) */

            if (bRemoveEvent)
            {
                // Remove the event from the queue
                m_ulEventListPosition = m_pEventList->RemoveAt(m_ulEventListPosition);
                // Delete the event
                HX_DELETE(pEvent);
            }
            else
            {
                m_pEventList->GetNext(m_ulEventListPosition);
            }
      }
    }

    return rc;
}


BOOL
CSmilDocumentRenderer::IsFullScreen()
{
    BOOL bRet = FALSE;

    if (m_pRootLayout->m_pSite)
    {
      IHXSiteFullScreen* pFull = NULL;
      m_pRootLayout->m_pSite->QueryInterface(IID_IHXSiteFullScreen, (void**) &pFull);
      if (pFull)
      {
          bRet = pFull->IsFullScreen();
      }
      HX_RELEASE(pFull);
    }

    return bRet;
}

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)

void CSmilDocumentRenderer::startSoundLevelAnimation(CSmilAnimateInfo* pInfo)
{
    MLOG_ANIM(m_pErrorMessages,"startSoundLevelAnimation(0x%08x) m_ulCurrentTime=%lu tick=%lu\n",
              pInfo, m_ulCurrentTime, HX_GET_BETTERTICKCOUNT());
    if (pInfo && m_pPlayToAssocList)
    {
        // Get the sound level to set
        UINT16 usSoundLevel = 100;
        if (pInfo->m_pSandwich)
        {
            CAttr cRet   = pInfo->m_pSandwich->GetValue(m_ulCurrentTime,
                                                        pInfo->m_pUnder, pInfo->m_pDepend);
            INT32 lLevel = (INT32) (cRet.GetValueDouble() + 0.5);
            if (lLevel < 0) lLevel = 0;
            usSoundLevel = (UINT16) lLevel;
        }
        // Find the right IHXTrack
        SMILPlayToAssoc* pPlayTo = NULL;
        LISTPOSITION     pos     = m_pPlayToAssocList->GetHeadPosition();
        while (pos)
        {
            SMILPlayToAssoc* pListPlayTo = (SMILPlayToAssoc*) m_pPlayToAssocList->GetNext(pos);
            if (pListPlayTo &&
                pListPlayTo->m_playTo == pInfo->m_pSandwich->GetTargetElementID())
            {
                IHXTrack* pTrack = NULL;
                HX_RESULT  rv     = getTrack(pListPlayTo->m_uGroupIndex,
                                             pListPlayTo->m_uTrackIndex,
                                             pTrack);
                if (SUCCEEDED(rv))
                {
                    // Start the sound level animation
                    MLOG_ANIM(m_pErrorMessages,"I call BeginSoundLevelAnimation(%u) at m_ulCurrentTime=%lu\ttick=%lu\n",
                              usSoundLevel, m_ulCurrentTime, HX_GET_BETTERTICKCOUNT());
                    pTrack->BeginSoundLevelAnimation(usSoundLevel);
                }
                HX_RELEASE(pTrack);
            }
        }
    }
    MLOG_ANIM(m_pErrorMessages,"exit startSoundLevelAnimation(0x%08x) m_ulCurrentTime=%lu tick=%lu\n",
              pInfo, m_ulCurrentTime, HX_GET_BETTERTICKCOUNT());
}

void CSmilDocumentRenderer::finishSoundLevelAnimation(CSmilAnimateInfo* pInfo,
                                                      BOOL              bUseCurrentLevel)
{
    MLOG_ANIM(m_pErrorMessages,"finishSoundLevelAnimation(0x%08x,%lu) at m_ulCurrentTime=%lu\ttick=%lu\n",
              pInfo, bUseCurrentLevel, m_ulCurrentTime, HX_GET_BETTERTICKCOUNT());
    if (pInfo && pInfo->m_pUnder && m_pPlayToAssocList)
    {
        // Get the sound level from the underlying value
        INT32 lLevel = (INT32) (pInfo->m_pUnder->GetValueDouble() + 0.5);
        // Clip at 0 if necessary
        if (lLevel < 0)
        {
            lLevel = 0;
        }
        UINT16 usSoundLevel = (UINT16) lLevel;
        // Call on all IHXTrack's playing to this region
        SMILPlayToAssoc* pPlayTo = NULL;
        LISTPOSITION     pos     = m_pPlayToAssocList->GetHeadPosition();
        while (pos)
        {
            SMILPlayToAssoc* pListPlayTo = (SMILPlayToAssoc*) m_pPlayToAssocList->GetNext(pos);
            if (pListPlayTo &&
                pListPlayTo->m_playTo == pInfo->m_pSandwich->GetTargetElementID())
            {
                IHXTrack* pTrack = NULL;
                HX_RESULT  rv     = getTrack(pListPlayTo->m_uGroupIndex,
                                             pListPlayTo->m_uTrackIndex,
                                             pTrack);
                if (SUCCEEDED(rv))
                {
                    if (bUseCurrentLevel)
                    {
                        // If this flag is set, we should end the 
                        // soundLevel animation with whatever the
                        // current value of the soundLevel is, instead
                        // of reverting back to the underlying value
                        // of the animation. This will be used when we
                        // are ending one animation with the intention
                        // of immediately beginning another.
                        usSoundLevel = pTrack->GetSoundLevel();
                    }
                    MLOG_ANIM(m_pErrorMessages,"I call EndSoundLevelAnimation(%u) at m_ulCurrentTime=%lu\ttick=%lu\n",
                              usSoundLevel, m_ulCurrentTime, HX_GET_BETTERTICKCOUNT());
                    // Start the sound level animation
                    pTrack->EndSoundLevelAnimation(usSoundLevel);
                }
                HX_RELEASE(pTrack);
            }
        }
    }
    MLOG_ANIM(m_pErrorMessages,"exit finishSoundLevelAnimation(0x%08x,%lu) at m_ulCurrentTime=%lu\ttick=%lu\n",
              pInfo, bUseCurrentLevel, m_ulCurrentTime, HX_GET_BETTERTICKCOUNT());
}

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

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

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)

    if (pszElementID && m_pAnimationMap)
    {
        POSITION pos = m_pAnimationMap->GetStartPosition();
        while (pos)
        {
            const char* pszKey = NULL;
            void*       pVoid  = NULL;
            m_pAnimationMap->GetNextAssoc(pos, pszKey, pVoid);
            if (pszKey && pVoid)
            {
                CSmilAnimateElement* pAnim = (CSmilAnimateElement*) pVoid;
                if (pAnim &&
                    pAnim->m_pTargetElementID &&
                    *pAnim->m_pTargetElementID == pszElementID &&
                    ((UINT32) pAnim->m_ucAttributeName) == ulAttrName)
                {
                    bRet = TRUE;
                    break;
                }
            }
        }
    }
    // Did we find a currently scheduled animation?
    if (!bRet && m_pSmilParser)
    {
        // We didn't find a currently scheduled
        // animation, but we need to check if the
        // parser knows about any animations on this
        // element/attribute, since animations which
        // start on events won't show up in the document
        // until the time they actually start. This is
        // especially important for soundLevel animations,
        // since we need to know about any possible animations
        // on the soundLevel before we add the track.
        bRet = m_pSmilParser->isAttributeAnimated(pszElementID, ulAttrName);
    }

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

    return bRet;
}

BOOL CSmilDocumentRenderer::isRegionBackgroundColorOverridden(CSmilBasicRegion* pRegion)
{
    BOOL bRet = FALSE;

    if (pRegion && m_pPlayToAssocList)
    {
        LISTPOSITION pos = m_pPlayToAssocList->GetHeadPosition();
        while (pos)
        {
            SMILPlayToAssoc* pAssoc =
                (SMILPlayToAssoc*) m_pPlayToAssocList->GetNext(pos);
            if (pAssoc &&
                pAssoc->m_playTo == pRegion->m_region)
            {
                CSmilSource* pSrc = getSource(pAssoc->m_id);
                if (pSrc &&
                    pSrc->m_bBackgroundColorSpecified)
                {
                    bRet = TRUE;
                    break;
                }
            }
        }
    }

    return bRet;
}

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)

CSmilAnimateElement* CSmilDocumentRenderer::getFirstAnimationElement(const char* pszElementID,
                                                                     UINT32      ulAttrName)
{
    CSmilAnimateElement* pRet = NULL;

    if (pszElementID && m_pAnimationMap)
    {
        // Initialize the iterator
        m_AnimationIterator = m_pAnimationMap->GetStartPosition();
        // Get the first element in the map
        pRet = getNextAnimationElement(pszElementID, ulAttrName);
    }

    return pRet;
}

CSmilAnimateElement* CSmilDocumentRenderer::getNextAnimationElement(const char* pszElementID,
                                                                    UINT32      ulAttrName)
{
    CSmilAnimateElement* pRet = NULL;

    if (pszElementID && m_pAnimationMap)
    {
        while (m_AnimationIterator)
        {
            const char* pszKey = NULL;
            void*       pVoid  = NULL;
            m_pAnimationMap->GetNextAssoc(m_AnimationIterator, pszKey, pVoid);
            if (pszKey && pVoid)
            {
                CSmilAnimateElement* pAnim = (CSmilAnimateElement*) pVoid;
                if (pAnim &&
                    pAnim->m_pTargetElementID &&
                    *pAnim->m_pTargetElementID == pszElementID &&
                    ((UINT32) pAnim->m_ucAttributeName) == ulAttrName)
                {
                    pRet = pAnim;
                    break;
                }
            }
        }
    }

    return pRet;
}

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

BOOL CSmilDocumentRenderer::getGroupDuration(UINT16      usGroupIndex,
                                             REF(UINT32) rulDuration)
{
    BOOL bRet = FALSE;

    if (m_pGroupInfoMap)
    {
        void* pVoid = NULL;
        if(m_pGroupInfoMap->Lookup((INT32) usGroupIndex, pVoid))
        {
            SMILGroupInfo* pGroupInfo = (SMILGroupInfo*) pVoid;
            if (pGroupInfo &&
                pGroupInfo->m_bDurationSet)
            {
                rulDuration = pGroupInfo->m_ulDuration;
                bRet        = TRUE;
            }
        }
    }

    return bRet;
}

HX_RESULT CSmilDocumentRenderer::getTrack(UINT16 uGroupIndex, UINT16 uTrackIndex, REF(IHXTrack*) rpTrack)
{
    HX_RESULT retVal = HXR_FAIL;

    SMILPlayToAssoc* pPT = getPlayToAssoc(uGroupIndex, uTrackIndex);
    if (pPT && m_pGroupMap)
    {
        // Get the current group
        void* pVoid = NULL;
        if(m_pGroupMap->Lookup(pPT->m_uGroupIndex, pVoid) && pVoid)
        {
            // QI for IHXGroup2
            IHXGroup*  pGroup  = (IHXGroup*) pVoid;
            IHXGroup2* pGroup2 = NULL;
            pGroup->QueryInterface(IID_IHXGroup2, (void**) &pGroup2);
            if (pGroup2)
            {
                // Get the right IHXTrack
                IHXTrack* pTrack = NULL;
                pGroup2->GetIHXTrack(pPT->m_uTrackIndex, pTrack);
                if (pTrack)
                {
                    retVal = HXR_OK;
                    HX_RELEASE(rpTrack);
                    rpTrack = pTrack;
                    rpTrack->AddRef();
                }
                HX_RELEASE(pTrack);
            }
            HX_RELEASE(pGroup2);
        }
    }

    return retVal;
}

void CSmilDocumentRenderer::forceFullRedraw(IHXSite* pSite)
{
    if (pSite)
    {
        HXxSize cSize;
        pSite->GetSize(cSize);
        HXxRect cRect = {0, 0, cSize.cx, cSize.cy};
        pSite->DamageRect(cRect);
        pSite->ForceRedraw();
    }
}

void CSmilDocumentRenderer::forceFullRecursiveRedraw(CSmilBasicBox* pBox)
{
    if (pBox)
    {
        // Force a redraw of this box's site
        forceFullRedraw(pBox->m_pSite);
        // Force an update on any child renderer sites
        if (pBox->m_pChildRendererSiteList)
        {
            LISTPOSITION pos = pBox->m_pChildRendererSiteList->GetHeadPosition();
            while (pos)
            {
                IHXSite* pChildRendSite =
                    (IHXSite*) pBox->m_pChildRendererSiteList->GetNext(pos);
                forceFullRedraw(pChildRendSite);
            }
        }
        // Now recursively force updates on any children
        if (pBox->m_pChildList)
        {
            LISTPOSITION pos = pBox->m_pChildList->GetHeadPosition();
            while (pos)
            {
                CSmilBasicBox* pChildBox =
                    (CSmilBasicBox*) pBox->m_pChildList->GetNext(pos);
                forceFullRecursiveRedraw(pChildBox);
            }
        }
    }
}

void CSmilDocumentRenderer::destroyRendererSites(CSmilBasicBox* pBox)
{
#if 0
    if (pBox)
    {
        // Take care of all the renderers which are
        // children of this box's site
        if (pBox->m_pChildRendererSiteList)
        {
            // Since we know no renderers are playing to a
            // root-level site box, then we know that this box
            // has to be a region, so we can cast to a CSmilBasicRegion
            CSmilBasicRegion* pRegion = (CSmilBasicRegion*) pBox;
            // Loop through the child renderer sites
            LISTPOSITION pos = pBox->m_pChildRendererSiteList->GetHeadPosition();
            while (pos)
            {
                IHXSite* pRendSite =
                    (IHXSite*) pBox->m_pChildRendererSiteList->GetNext(pos);
                if (pRendSite)
                {
                    // Remove the site from the site manager
                    if (m_pSiteMgr)
                    {
                        m_pSiteMgr->RemoveSite(pRendSite);
                    }
                    // Destroy the parent-child relationship between
                    // region site and renderer site
                    if (pRegion->m_pSite)
                    {
                        pRegion->m_pSite->DestroyChild(pRendSite);
                    }
                    // Detach the connection between the
                    // site and the site watcher. Note that this
                    // only causes the site to remove its ref on
                    // the site watcher - it doesn't delete it.
                    pRendSite->DetachWatcher();
                    // Get the site watcher from the site watcher map
                    if (m_pSiteWatcherMap)
                    {
                        void* pVoid = NULL;
                        if (m_pSiteWatcherMap->Lookup((void*) pRendSite, pVoid) && pVoid)
                        {
                            // Get the CSmilSiteWatcher pointer
                            CSmilSiteWatcher* pWatcher = (CSmilSiteWatcher*) pVoid;
                            // Remove the site watcher from the site watcher map
                            m_pSiteWatcherMap->RemoveKey((void*) pRendSite);
                            // Release our ref on the site watcher
                            HX_RELEASE(pWatcher);
                        }
                    }
                    // Remove this renderer from the m_pSiteInfoByRendererMap
                    if (m_pSiteInfoByRendererMap)
                    {
                        m_pSiteInfoByRendererMap->RemoveKey((void*) pRendSite);
                    }
                    // Release the site info's ref 
                    // on the renderer site
                    HX_RELEASE(pRendSite);
                }
            }
        }
    }
#else
    HX_ASSERT(m_pSiteInfoList); // /Fix for PR 116482 in player core should
                                // ensure that we don't get here w/NULL m_p...
    // Here we want to destroy all the renderer sites
    // which were playing to a region in this top-level
    // box. The top level box could be the root-layout or
    // it could be a viewport.
    if (pBox && m_pSiteInfoByRendererMap  &&
            // /This check fixes PR 101285 crash.  If m_pSiteInfoList has been
            // deleted, then m_pSiteInfoByRendererMap points at deleted items:
            m_pSiteInfoList)
    {
        // Create a list which keeps track of which
        // keys we have operated on
        CHXSimpleList cKeyList;
        // Loop through all of the renderers
        POSITION pos = m_pSiteInfoByRendererMap->GetStartPosition();
        while (pos)
        {
            void* pKey = NULL;
            void* pVal = NULL;
            m_pSiteInfoByRendererMap->GetNextAssoc(pos, pKey, pVal);
            if (pVal)
            {
                // Get the site info pointer
                SMILSiteInfo* pSiteInfo = (SMILSiteInfo*) pVal;
                if (pSiteInfo->m_pRendererSite)
                {
                    // Get the playback region
                    CSmilBasicRegion* pRegion = getRegionByID(pSiteInfo->m_regionID);
                    if (pRegion)
                    {
                        // Now get the top-level box which this
                        // region is under
                        CSmilBasicBox* pTop = getTopLevelBox(pRegion);
                        // Only do this operation for those renderers
                        // playing to regions which are in *this* top-level box.
                        if (pTop == pBox)
                        {
                      // XXX HP this is to fix PR63462.
                      // pSiteInfo->m_pRendererSite is added by addRendererSiteChild()
                      // and we need to call its corresponding removeRendererSiteChild()
                      // before we destroying the site.
                      pRegion->removeRendererSiteChild(pSiteInfo->m_pRendererSite);

                            // Remove the site from the site manager
                            m_pSiteMgr->RemoveSite(pSiteInfo->m_pRendererSite);
                            // Destroy the parent-child relationship between
                            // region site and renderer site
                            if (pRegion->m_pSite)
                            {
                                pRegion->m_pSite->DestroyChild(pSiteInfo->m_pRendererSite);
                            }
                            // Detach the connection between the
                            // site and the site watcher. Note that this
                            // only causes the site to remove its ref on
                            // the site watcher - it doesn't delete it.
                            pSiteInfo->m_pRendererSite->DetachWatcher();
                            // Get the site watcher from the site watcher map
                            if (m_pSiteWatcherMap)
                            {
                                void* pVoid = NULL;
                                if (m_pSiteWatcherMap->Lookup((void*) pSiteInfo->m_pRendererSite, pVoid) &&
                                    pVoid)
                                {
                                    // Get the CSmilSiteWatcher pointer
                                    CSmilSiteWatcher* pWatcher = (CSmilSiteWatcher*) pVoid;
                                    // Remove the site watcher from the site watcher map
                                    m_pSiteWatcherMap->RemoveKey((void*) pSiteInfo->m_pRendererSite);
                                    // Release our ref on the site watcher
                                    HX_RELEASE(pWatcher);
                                }
                            }
                            // Remove the site watcher from the map
                            removeRendererSiteWatcherFromMap((const char*) pSiteInfo->m_MediaID);
                            // Remove the site from the map
                            removeRendererSiteFromMap((const char*) pSiteInfo->m_MediaID);
                            // Release the site info's ref 
                            // on the renderer site
                            HX_RELEASE(pSiteInfo->m_pRendererSite);
                            // Add this key to the list
                            cKeyList.AddTail((void*) pKey);
                        }
                    }
                }
            }
        }
        // Now loop through the keys we operated on
        // and remove them from the map
        LISTPOSITION lpos = cKeyList.GetHeadPosition();
        while (lpos)
        {
            void* pKey = cKeyList.GetNext(lpos);
            if (pKey)
            {
                m_pSiteInfoByRendererMap->RemoveKey(pKey);
            }
        }
      
        // Delete these only if close has already been called.
        // In that case, this is our last line of defense
        // against a leak.
        if (m_bCloseCalled)
        {
            // Now if we have deleted all the renderers in the map,
            // then delete the whole map
            if (m_pSiteInfoByRendererMap &&
                m_pSiteInfoByRendererMap->GetCount() == 0)
            {
                HX_DELETE(m_pSiteInfoByRendererMap);
            }
            // If we have deleted all the watchers in the
            // site watcher map, then delete the map itself.
            if (m_pSiteWatcherMap &&
                m_pSiteWatcherMap->GetCount() == 0)
            {
                HX_DELETE(m_pSiteWatcherMap);
            }
        }
    }
#endif
}

HX_RESULT CSmilDocumentRenderer::setupRootLayout(BOOL bAssignDefaults)
{
    HX_RESULT retVal = HXR_OK;

    if (m_pRootLayout)
    {
        // Are we supposed to assign defaults?
        if (bAssignDefaults)
        {
          // If we haven't resolved the root-layout width,
          // then assign a default root layout width
          if (!m_pRootLayout->m_bWidthResolved)
          {
              m_pRootLayout->m_Rect.left             = 0;
              m_pRootLayout->m_Rect.right            = DEFAULT_ROOT_LAYOUT_WIDTH;
              m_pRootLayout->m_bWidthResolved        = TRUE;
                m_pRootLayout->m_bDefaultWidthAssigned = TRUE;
                if (!m_pRootLayout->m_bOriginalWidthSet)
                {
                    m_pRootLayout->m_OriginalSize.cx   = DEFAULT_ROOT_LAYOUT_WIDTH;
                    m_pRootLayout->m_bOriginalWidthSet = TRUE;
                }
            }
          // If we haven't resolved the root-layout height,
          // then assign a default root layout height
          if (!m_pRootLayout->m_bHeightResolved)
          {
              m_pRootLayout->m_Rect.top               = 0;
              m_pRootLayout->m_Rect.bottom            = DEFAULT_ROOT_LAYOUT_HEIGHT;
              m_pRootLayout->m_bHeightResolved        = TRUE;
                m_pRootLayout->m_bDefaultHeightAssigned = TRUE;
                if (!m_pRootLayout->m_bOriginalHeightSet)
                {
                    m_pRootLayout->m_OriginalSize.cy    = DEFAULT_ROOT_LAYOUT_HEIGHT;
                    m_pRootLayout->m_bOriginalHeightSet = TRUE;
                }
          }
        }
      // Compute left and right of the region rects
      HX_RESULT rvW = computeBoxDimension(m_pRootLayout, BoxDimensionWidth);
      // Compute top and bottom of the region rects
      HX_RESULT rvH = computeBoxDimension(m_pRootLayout, BoxDimensionHeight);
        if (SUCCEEDED(rvW) && SUCCEEDED(rvH))
        {
            // Get the root layout id
            const char* pszRootID = "root-layout";
            if (m_pRootLayout->m_pRoot &&
                m_pRootLayout->m_pRoot->m_pNode)
            {
                pszRootID = m_pRootLayout->m_pRoot->m_pNode->m_id;
            }
          // Create a site user for the root layout
          HX_RELEASE(m_pRootLayout->m_pSiteUser);
          m_pRootLayout->m_pSiteUser = new CSmilSiteUser(this,
                                             m_pRootLayout->GetBackgroundColor(),
                                             m_pContext,
                                             TRUE,
                                                           pszRootID);
          if (m_pRootLayout->m_pSiteUser)
          {
            // AddRef the site user
            m_pRootLayout->m_pSiteUser->AddRef();
            // Set several properties in the site user
                //
                // First, get an IHXValues interface
                IHXValues* pSiteUserValues = NULL;
                m_pRootLayout->m_pSiteUser->QueryInterface(IID_IHXValues,
                                                           (void**) &pSiteUserValues);
                if (pSiteUserValues)
                {
                    // Set the "name" CString property
                    addStringProperty(pSiteUserValues, m_pContext,
                                      "name", "TopLevelSite");
                    // Set the "contextWindow" CString property
                    const char* pszCWVal = NULL;
                    if (m_pRootLayout->m_pRoot)
                    {
                        if (m_pRootLayout->m_pRoot->m_eContextWindow == ContextWindowAuto)
                        {
                            pszCWVal = "auto";
                        }
                        else
                        {
                            pszCWVal = "openAtStart";
                        }
                    }
                    addStringProperty(pSiteUserValues, m_pContext,
                                      "contextWindow", pszCWVal);
                }
                HX_RELEASE(pSiteUserValues);
            // QI for IUnknown
            IUnknown* pThisUnk = NULL;
            m_pRootLayout->m_pSiteUser->QueryInterface(IID_IUnknown, (void**) &pThisUnk);
            if (pThisUnk)
            {
                // This is a call back to CSmilRenderer, which in turn
                // calls IHXLayoutSiteGroupManager::AddLayoutSiteGroup()
                // with CSmilDocumentRenderer as the callback interface.
                // This call results in our AttachSite method being called,
                // which sets the top level site member (m_pRootLayout->m_pSite).
                m_pParent->HandleAddLayoutSiteGroup(pThisUnk);
                m_bSiteLayoutComplete = TRUE;
            }
            HX_RELEASE(pThisUnk);

            // Create sites for each of the regions
            // under the root-layout
            retVal = createRegionSites(m_pRootLayout->m_pChildList);
            if (SUCCEEDED(retVal))
            {
                // We will initially set the z-orders
                resolveZOrder(m_pRootLayout, m_ulCurrentTime);
                // force initial redraw
                forceFullRedraw(m_pRootLayout->m_pSite);
            }
          }
          else
          {
            retVal = HXR_OUTOFMEMORY;
          }
      }
        else
        {
            retVal = HXR_FAIL;
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)

HX_RESULT CSmilDocumentRenderer::setupViewports()
{
    HX_RESULT retVal = HXR_OK;

    // Do we have any viewports?
    if (m_pViewportList && m_pViewPortManager)
    {
        // Loop through each viewport, setting it up
        LISTPOSITION pos = m_pViewportList->GetHeadPosition();
        while (pos && SUCCEEDED(retVal))
        {
            // Get the viewport
            CSmilBasicViewport* pVP =
                (CSmilBasicViewport*) m_pViewportList->GetNext(pos);
            if (pVP && pVP->m_pPort)
            {
                // Compute left and right of the region rects
                retVal = computeBoxDimension(pVP, BoxDimensionWidth);
                if (SUCCEEDED(retVal))
                {
                    // Compute top and bottom of the region rects
                    retVal = computeBoxDimension(pVP, BoxDimensionHeight);
                    if (SUCCEEDED(retVal))
                    {
                        // Create a site user for this viewport
                        HX_RELEASE(pVP->m_pSiteUser);
                        pVP->m_pSiteUser = new CSmilSiteUser(this,
                                                             pVP->m_pPort->m_ulBackgroundColor,
                                                             m_pContext,
                                                             TRUE,
                                                             pVP->m_pPort->m_pNode->m_id);
                        if (pVP->m_pSiteUser)
                        {
                            // AddRef the site user
                            pVP->m_pSiteUser->AddRef();
                            // Create an IHXValues
                            IHXCommonClassFactory* pFactory = NULL;
                            retVal = m_pContext->QueryInterface(IID_IHXCommonClassFactory,
                                                                (void**) &pFactory);
                            if (SUCCEEDED(retVal))
                            {
                                IHXValues* pValues = NULL;
                                retVal = pFactory->CreateInstance(CLSID_IHXValues,
                                                                  (void**) &pValues);
                                if (SUCCEEDED(retVal))
                                {
                                    // Set the "playto" property to be the
                                    // id of this viewport
                                    setProperty(pValues, "playto", (const char*) pVP->m_id);
                                    // Get the IHXSiteUser interface from CSmilSiteUser
                                    IHXSiteUser* pSiteUser = NULL;
                                    retVal = pVP->m_pSiteUser->QueryInterface(IID_IHXSiteUser,
                                                                              (void**) &pSiteUser);
                                    if (SUCCEEDED(retVal))
                                    {
                                        // Create the viewport. This is an asynchronous
                                        // call - it will call us back with AttachSite.
                                        retVal = m_pViewPortManager->OpenViewPort(pValues, pSiteUser);
                                    }
                                    HX_RELEASE(pSiteUser);
                                }
                                HX_RELEASE(pValues);
                            }
                            HX_RELEASE(pFactory);
                        }
                        else
                        {
                            retVal = HXR_OUTOFMEMORY;
                        }
                    }
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
    }

    return retVal;
}

HX_RESULT CSmilDocumentRenderer::finishOneViewportSetup(CSmilBasicViewport* pPort)
{
    HX_RESULT retVal = HXR_OK;

    if (pPort && pPort->m_pPort)
    {
        // Create all the sites which are children of this site
        retVal = createRegionSites(pPort->m_pChildList);
        if (SUCCEEDED(retVal))
        {
            // We will initially set the z-orders
            resolveZOrder(pPort, m_ulCurrentTime);
            // If the open property of the viewport is
            // onStart, then we will go ahead and show the
            // window. If it is whenActive, then we will
            // hide the window now.
            BOOL bShow = TRUE;
            if (pPort->m_pPort->m_eOpen == ViewportOpenWhenActive)
            {
                bShow = FALSE;
            }
            showHideViewport((const char*) pPort->m_id, bShow);
            // force initial redraw
            forceFullRedraw(pPort->m_pSite);
            // Set the setup flag
            pPort->m_bViewportIsSetup = TRUE;
            // Now we need to check if all the viewport sites
            // have been setup. If they have, then we will call
            // setupViewportsDone().
            BOOL bAllAreSetup = TRUE;
            if(m_pViewportList)
            {
                LISTPOSITION pos = m_pViewportList->GetHeadPosition();
                while (pos)
                {
                    CSmilBasicViewport* pListPort =
                        (CSmilBasicViewport*) m_pViewportList->GetNext(pos);
                    if (pListPort && pListPort->m_bViewportIsSetup == FALSE)
                    {
                        bAllAreSetup = FALSE;
                        break;
                    }
                }
            }
            if (bAllAreSetup)
            {
                // All the viewports are now setup
                setupViewportsDone(retVal);
            }
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT CSmilDocumentRenderer::setupViewportsDone(HX_RESULT status)
{
    HX_RESULT retVal = HXR_OK;

    return retVal;
}

void CSmilDocumentRenderer::closeViewports()
{
    if (m_pViewportList && m_pViewPortManager)
    {
        LISTPOSITION pos = m_pViewportList->GetHeadPosition();
        while (pos)
        {
            CSmilBasicViewport* pVP =
                (CSmilBasicViewport*) m_pViewportList->GetNext(pos);
            if (pVP && pVP->m_bOpen)
            {
#ifdef XXXMEH_DO_VIEWPORT_TLC
                m_bSMILViewportClose = TRUE;
#endif
                // Close the viewport
                m_pViewPortManager->CloseViewPort((const char*) pVP->m_id);
#ifdef XXXMEH_DO_VIEWPORT_TLC
                m_bSMILViewportClose = FALSE;
#endif
            }
        }
    }
}

void CSmilDocumentRenderer::showHideViewport(const char* pszID, BOOL bShow)
{
    if (m_pViewPortManager && pszID)
    {
        IHXViewPort* pPort = NULL;
        m_pViewPortManager->GetViewPort(pszID, pPort);
        if (pPort)
        {
            if (bShow)
            {
                pPort->Show();
            }
            else
            {
                pPort->Hide();
            }
        }
        HX_RELEASE(pPort);
    }
}

void CSmilDocumentRenderer::showHideViewport(CSmilBasicViewport* pPort,
                                             BOOL                bShow)
{
    if (pPort          &&
        pPort->m_pPort &&
        pPort->m_pPort->m_pNode)
    {
        showHideViewport((const char*) pPort->m_id, bShow);
    }
}

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

void CSmilDocumentRenderer::destroyRegions(CHXSimpleList* pChildList)
{
    if (pChildList)
    {
        // Loop through the children regions
        LISTPOSITION pos = pChildList->GetHeadPosition();
        while (pos)
        {
            // Since these are children, they have to be
            // regions, as top-level boxes (root-layout
            // and viewport) cannot be children.
            CSmilBasicRegion* pRegion = (CSmilBasicRegion*) pChildList->GetNext(pos);
            if (pRegion)
            {
                // First we must destroy the children of this
                // region, since they hold links to their ancestors
                destroyRegions(pRegion->m_pChildList);
                // Now we can destroy this region.
                if (m_pRegionMap)
                {
                    // First, remove the key from the region map
                    m_pRegionMap->RemoveKey((const char*) pRegion->m_region);
                }
                // Now we can delete the region
                HX_DELETE(pRegion);
            }
        }
    }
}

void CSmilDocumentRenderer::destroyAllRegions()
{
    if (m_pRegionMap)
    {
      // Run through the map
      POSITION pos = m_pRegionMap->GetStartPosition();
      while (pos)
      {
          const char* pszKey = NULL;
          void*       pVal   = NULL;
          m_pRegionMap->GetNextAssoc(pos, pszKey, pVal);
          if (pVal)
          {
            // Get the region pointer
            CSmilBasicRegion* pRegion = (CSmilBasicRegion*) pVal;
            // Delete this region
            HX_DELETE(pRegion);
          }
      }

        // Remove all the entries
        m_pRegionMap->RemoveAll();
        // Delete the map
        HX_DELETE(m_pRegionMap);
    }
}

void CSmilDocumentRenderer::setElementRemoveTime(const char* pszID,
                                                 UINT32      ulRemoveTime)
{
    if (pszID && m_pSmilParser)
    {
        CSmilElement* pElement = m_pSmilParser->findElement(pszID);
        if (pElement)
        {
            pElement->m_ulRemoveTime = ulRemoveTime;
        }
    }
}

#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) && defined(XXXMEH_DO_VIEWPORT_TLC)

void CSmilDocumentRenderer::clearViewportMap()
{
    if (m_pViewPortSiteMap)
    {
        POSITION pos = m_pViewPortSiteMap->GetStartPosition();
        while (pos)
        {
            const char* pszKey = NULL;
            void*       pObj   = NULL;
            m_pViewPortSiteMap->GetNextAssoc(pos, pszKey, pObj);
            if (pObj)
            {
                IHXSite* pSite = (IHXSite*) pObj;
                HX_RELEASE(pSite);
            }
        }
        m_pViewPortSiteMap->RemoveAll();
    }
}

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

void CSmilDocumentRenderer::showAllSites(CSmilBasicBox* pBox, BOOL bShow)
{
    if (pBox && pBox->m_pSite)
    {
        ShowBackground eShow = ShowBackgroundAlways;
        if (pBox->m_pParent)
        {
            CSmilBasicRegion* pRegion = (CSmilBasicRegion*) pBox;
            eShow = pRegion->m_eShowBackground;
        }
        if (eShow == ShowBackgroundAlways)
        {
            BOOL bVisible = isSiteVisible(pBox->m_pSite);
            showSite(pBox->m_pSite, bShow);
        }
        // Process the children
        if (pBox->m_pChildList)
        {
            LISTPOSITION pos = pBox->m_pChildList->GetHeadPosition();
            while (pos)
            {
                CSmilBasicBox* pChildBox =
                    (CSmilBasicBox*) pBox->m_pChildList->GetNext(pos);
                showAllSites(pChildBox, bShow);
            }
        }
    }
}

void CSmilDocumentRenderer::showSite(IHXSite* pSite, BOOL bShow)
{
    if(pSite)
    {
        IHXSite2* pSite2 = NULL;
        pSite->QueryInterface(IID_IHXSite2, (void**) &pSite2);
        if (pSite2)
        {
            pSite2->ShowSite(bShow);
        }
        HX_RELEASE(pSite2);
    }
}

BOOL CSmilDocumentRenderer::isSiteVisible(IHXSite* pSite)
{
    BOOL bRet = FALSE;

    if (pSite)
    {
        IHXSite2* pSite2 = NULL;
        pSite->QueryInterface(IID_IHXSite2, (void**) &pSite2);
        if (pSite2)
        {
            bRet = pSite2->IsSiteVisible();
        }
        HX_RELEASE(pSite2);
    }

    return bRet;
}

void CSmilDocumentRenderer::setSiteZIndex(IHXSite* pSite, INT32 lZIndex)
{
    if (pSite)
    {
        IHXSite2* pSite2 = NULL;
        pSite->QueryInterface(IID_IHXSite2, (void**) &pSite2);
        if (pSite2)
        {
            INT32     lCurZOrder = 0;
            HX_RESULT rv         = pSite2->GetZOrder(lCurZOrder);
            if (FAILED(rv) ||
                (SUCCEEDED(rv) && lZIndex != lCurZOrder))
            {
                pSite2->SetZOrder(lZIndex);
            }
        }
        HX_RELEASE(pSite2);
    }
}

INT32 CSmilDocumentRenderer::getSiteZIndex(IHXSite* pSite)
{
    INT32 lRet = 0;

    if (pSite)
    {
        IHXSite2* pSite2 = NULL;
        pSite->QueryInterface(IID_IHXSite2, (void**) &pSite2);
        if (pSite2)
        {
            pSite2->GetZOrder(lRet);
        }
        HX_RELEASE(pSite2);
    }

    return lRet;
}

UINT32 CSmilDocumentRenderer::getNumberOfChildSites(IHXSite* pSite)
{
    UINT32 ulRet = 0;

    if (pSite)
    {
        IHXSite2* pSite2 = NULL;
        pSite->QueryInterface(IID_IHXSite2, (void**) &pSite2);
        if (pSite2)
        {
            ulRet = pSite2->GetNumberOfChildSites();
        }
        HX_RELEASE(pSite2);
    }

    return ulRet;
}

HX_RESULT
CSmilDocumentRenderer::onPreSeek(UINT32 ulOldTime, UINT32 ulNewTime)
{
    HX_RESULT rc = HXR_OK;

//{FILE* f1 = ::fopen("c:\\temp\\out.txt", "a+"); ::fprintf(f1, "onPreSeek\n");::fclose(f1);}

    if(m_pSiteInfoList  &&  m_pSiteInfoList->GetCount() > 0)
    {
      // hide all regions/sites in current group
      CHXSimpleList::Iterator i = m_pSiteInfoList->Begin();
      for(; i != m_pSiteInfoList->End(); ++i)
      {
          SMILSiteInfo* pSiteInfo = (SMILSiteInfo*)(*i);
          if(pSiteInfo->m_uGroupIndex == m_uCurrentGroupIndex)
          {
            // /Reset resumeTime of site infos if seek is back before they
            // occurred so wrong one isn't on top (after fix for PR 81510
            // (& dup. PR 83796)):
            if (pSiteInfo->m_ulResumeTime > ulNewTime)
            {
                pSiteInfo->m_ulResumeTime = 0;
            }

            IHXSite* pRegionSite = NULL;
            CSmilBasicRegion* pRegion = getRegionByID(pSiteInfo->m_regionID);
            if (!pRegion)
            {
                HX_ASSERT(pRegion);
                continue;
            }
            if(pRegion->m_eBackgroundColorType == CSS2TypeTransparent)
            {
                pRegionSite = pSiteInfo->m_pRegionSite;
            }
            showSite(pSiteInfo->m_pRendererSite, FALSE);
            showSite(pRegionSite, FALSE);
          }
      }

      // remove all the transitions...
      removeActiveTransitions();

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)
        // remove all the animations
        removeActiveAnimations();
#endif /* #if defined(HELIX_FEATURE_SMIL2_ANIMATION) */

        // Clear the "handled" flag on all events
        clearAllEventHandledFlags();

      // /Notify rmasmil so its data structures can prep for a seek, e.g.,
      // re-loading all old actuate="onLoad" links that were removed when
      // used already, prior to this (backwards) seek; fixes PR 67170:
      if (m_pSmilParser)
      {
          m_pSmilParser->prepForSeek(ulOldTime, ulNewTime);
      }

      // show/hide sites up to ulNewTime

      // events at the same time for the same region
      // are ordererd 'hide' first, then 'show' so
      // a hide doesn't override a show.
      m_ulEventListPosition = m_pEventList->GetHeadPosition();
      flushAllEvents( ulNewTime, TRUE );
    }
    return rc;
}

HX_RESULT
CSmilDocumentRenderer::endStream()
{
    m_bSMILPresentationHasEnded = TRUE;

    flushAllEvents();    // flush all remaining events

    if(m_pGroupMap)
    {
      CHXMapLongToObj::Iterator i;
      for(i=m_pGroupMap->Begin();i!=m_pGroupMap->End();++i)
      {
          IHXGroup* pGroup = (IHXGroup*)(*i);
          pGroup->Release();
      }
      HX_DELETE(m_pGroupMap);
    }

    HX_RELEASE(m_pPersistentLayoutStream);

    closeOldRenderers(TRUE);

    HX_RELEASE(m_pPersistentProperties);
    HX_RELEASE(m_pPersistentParentRenderer);

    if (isSiteCompositionModeON())
    {
        turnSiteCompositionModeOFF();
    }

    return HXR_OK;
}

CSmilBasicRegion* CSmilDocumentRenderer::setupDefaultLayout()
{
    CSmilBasicRegion* pRet = NULL;

    // First we need to check if we have already created
    // a default region.
    // XXXMEH - later, we might want to something more sophisticated
    // here and make sure that the default region name we create
    // is unique. For now, we make it a weird string.
    const char* pszDefRegID = "jgje4u5kd845prhd94";
    // Do we already have a default region?
    CSmilBasicRegion* pRegion = getRegionByID(pszDefRegID);
    if (!pRegion)
    {
        // We don't already have a default region - create one.
        pRegion = new CSmilBasicRegion(NULL);
        if (pRegion)
        {
            // Assign the default id to this region
            pRegion->m_region = pszDefRegID;
            // Set the root-layout as the parent
            pRegion->m_pParent = m_pRootLayout;
            // Add this region as a child of the root-layout
            HX_RESULT retVal = m_pRootLayout->addChild(pRegion);
            if (SUCCEEDED(retVal))
            {
                // Add the default region to the region map
                m_pRegionMap->SetAt(pszDefRegID, (void*) pRegion);
                // Do we need to set up the root-layout?
                if (!m_bIsRootLayoutSetup)
                {
                    // We need to set up the root-layout. This can
                    // happen if either there WAS no root-layout element
                    // or if the root-layout element was missing either
                    // a "width" or "height" property
                    retVal = setupRootLayout(TRUE);
                    if (SUCCEEDED(retVal))
                    {
                        // Set the flag
                        m_bIsRootLayoutSetup = TRUE;
                        // Assign the out parameter
                        pRet = pRegion;
                    }
                }
                else
                {
                    // <root-layout> is already set up, but we need to resolve
                    // the width and height of this region.
                    retVal = computeBoxDimension(pRegion, BoxDimensionWidth);
                    if (SUCCEEDED(retVal))
                    {
                        retVal = computeBoxDimension(pRegion, BoxDimensionHeight);
                        if (SUCCEEDED(retVal))
                        {
                            // Now we need to do the regular setup of this region
                            retVal = createRegionSite(pRegion);
                            if (SUCCEEDED(retVal))
                            {
                                // Assign the out parameter
                                pRet = pRegion;
                            }
                        }
                    }
                }
            }
        }
    }
    else
    {
        // We have a default region, so we assume that
        // we don't have to do any additional setup.
        pRet = pRegion;
    }

    return pRet;
}

CSmilBasicRegion* CSmilDocumentRenderer::getRegionByID(const char* pID)
{
    CSmilBasicRegion* pRegion = 0;
    if(m_pRegionMap)
    {
      m_pRegionMap->Lookup(pID, (void*&)pRegion);
    }
    return pRegion;
}

UINT32 CSmilDocumentRenderer::getNumRegionsByName(const char* pszName)
{
    UINT32 ulRet = 0;

    CSmilBasicRegion* pRegion = getFirstRegionByName(pszName);
    while (pRegion)
    {
        ulRet++;
        pRegion = getNextRegionByName(pszName);
    }

    return ulRet;
}

CSmilBasicRegion* CSmilDocumentRenderer::getFirstRegionByName(const char* pszName)
{
    CSmilBasicRegion* pRet = NULL;

    if (m_pRegionMap)
    {
        // Initialize the iterator
        m_pRegionMapIterator = m_pRegionMap->GetStartPosition();
        // Get the region
        pRet = getRegionByName(pszName);
    }

    return pRet;
}

CSmilBasicRegion* CSmilDocumentRenderer::getNextRegionByName(const char* pszName)
{
    return getRegionByName(pszName);
}

BOOL CSmilDocumentRenderer::isAncestorRegion(CSmilBasicRegion* pPossibleParent,
                                             CSmilBasicRegion* pPossibleChild)
{
    BOOL bRet = FALSE;

    if (pPossibleParent)
    {
        while (pPossibleChild)
        {
            if (pPossibleParent == pPossibleChild)
            {
                bRet = TRUE;
                break;
            }
            // If our parent has a parent, then the
            // CSmilBasicBox is still a region.
            if (pPossibleChild->m_pParent &&
                pPossibleChild->m_pParent->m_pParent)
            {
                pPossibleChild = (CSmilBasicRegion*) pPossibleChild->m_pParent;
            }
            else
            {
                break;
            }
        }
    }

    return bRet;
}

HX_RESULT CSmilDocumentRenderer::getCommonAncestorRegion(CSmilBasicRegion*      pRegA,
                                                         CSmilBasicRegion*      pRegB,
                                                         REF(CSmilBasicRegion*) rpCommon)
{
    HX_RESULT retVal = HXR_OK;

    if (pRegA && pRegB)
    {
        // First check if B is a child of A,
        // then check if A is a child of B,
        // then check for a common ancestor
        if (isAncestorRegion(pRegA, pRegB))
        {
            rpCommon = pRegA;
        }
        else if (isAncestorRegion(pRegB, pRegA))
        {
            rpCommon = pRegB;
        }
        else
        {
            retVal = HXR_FAIL;
        }
    }

    return retVal;
}

#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)

CSmilBasicViewport* CSmilDocumentRenderer::getViewport(const char* pszID)
{
    CSmilBasicViewport* pRet = NULL;

    if(m_pViewportList)
    {
        LISTPOSITION pos = m_pViewportList->GetHeadPosition();
        while (pos)
        {
            CSmilBasicViewport* pPort =
                (CSmilBasicViewport*) m_pViewportList->GetNext(pos);
            if (pPort && pPort->m_id == pszID)
            {
                pRet = pPort;
                break;
            }
        }
    }

    return pRet;
}

CSmilBasicViewport* CSmilDocumentRenderer::getViewportBySiteUser(CSmilSiteUser* pSiteUser)
{
    CSmilBasicViewport* pRet = NULL;

    if(m_pViewportList)
    {
        LISTPOSITION pos = m_pViewportList->GetHeadPosition();
        while (pos)
        {
            CSmilBasicViewport* pPort =
                (CSmilBasicViewport*) m_pViewportList->GetNext(pos);
            if (pPort &&
                pPort->m_pSiteUser == pSiteUser)
            {
                pRet = pPort;
                break;
            }
        }
    }

    return pRet;
}

CSmilBasicViewport* CSmilDocumentRenderer::getViewportBySite(IHXSite* pSite)
{
    CSmilBasicViewport* pRet = NULL;

    if(m_pViewportList)
    {
        LISTPOSITION pos = m_pViewportList->GetHeadPosition();
        while (pos)
        {
            CSmilBasicViewport* pPort =
                (CSmilBasicViewport*) m_pViewportList->GetNext(pos);
            if (pPort &&
                pPort->m_pSite == pSite)
            {
                pRet = pPort;
                break;
            }
        }
    }

    return pRet;
}

CSmilBasicViewport* CSmilDocumentRenderer::getViewportByDescendant(CSmilBasicRegion* pRegion)
{
    CSmilBasicViewport* pRet = NULL;

    if (pRegion && !pRegion->m_bUnderRootLayout)
    {
        CSmilBasicBox* pTop = getTopLevelBox(pRegion);
        if (pTop)
        {
            pRet = (CSmilBasicViewport*) pTop;
        }
    }

    return pRet;
}

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

CSmilBasicBox* CSmilDocumentRenderer::getTopLevelBox(CSmilBasicRegion* pRegion)
{
    CSmilBasicBox* pRet = NULL;

    if (pRegion && pRegion->m_pParent)
    {
        pRet = pRegion->m_pParent;
        while (pRet->m_pParent)
        {
            pRet = pRet->m_pParent;
        }
    }

    return pRet;
}

CSmilRegPoint* CSmilDocumentRenderer::getRegPoint(const char* pszID)
{
    CSmilRegPoint* pRet = NULL;

    if(m_pRegPointMap)
    {
        void* pVoid = NULL;
        if (m_pRegPointMap->Lookup(pszID, pVoid))
        {
            pRet = (CSmilRegPoint*) pVoid;
        }
    }

    return pRet;
}

CSmilSource* CSmilDocumentRenderer::getSource(const char* pszID)
{
    CSmilSource* pRet = NULL;

    if (m_pSmilParser)
    {
        CSmilElement* pElement = m_pSmilParser->findElement(pszID);
        if (pElement && pElement->m_pNode &&
            (pElement->m_pNode->m_tag == SMILRef        ||
             pElement->m_pNode->m_tag == SMILText       ||
             pElement->m_pNode->m_tag == SMILImg        ||
             pElement->m_pNode->m_tag == SMILAudio      ||
             pElement->m_pNode->m_tag == SMILVideo      ||
             pElement->m_pNode->m_tag == SMILAnimation  ||
             pElement->m_pNode->m_tag == SMILTextstream ||
             pElement->m_pNode->m_tag == SMILBrush      ||
             pElement->m_pNode->m_tag == SMILPrefetch))
        {
            pRet = (CSmilSource*) pElement;
        }
    }

    return pRet;
}

void CSmilDocumentRenderer::setTopLevelSiteSize()
{
    if (m_pRootLayout->IsWidthSet() &&
      m_pRootLayout->IsHeightSet())
    {
        // Set the members
        m_topSiteSize.cx         = (INT32) m_pRootLayout->GetWidth();
        m_topSiteSize.cy         = (INT32) m_pRootLayout->GetHeight();
        m_topSiteOriginalSize.cx = m_topSiteSize.cx;
        m_topSiteOriginalSize.cy = m_topSiteSize.cy;
        // Set the site's size
        if(m_topSiteSize.cx > 0 && m_topSiteSize.cy > 0 &&
           m_pRootLayout && m_pRootLayout->m_pSite)
        {
            // Get the site's current size
            HXxSize cSize = {0, 0};
            m_pRootLayout->m_pSite->GetSize(cSize);
            // If the size we want to set and the current
            // size are different, then call SetSize()
            if (cSize.cx != m_topSiteSize.cx ||
                cSize.cy != m_topSiteSize.cy)
            {
                m_pRootLayout->m_pSite->SetSize(m_topSiteSize);
            }
        }
    }
}

HX_RESULT CSmilDocumentRenderer::setRegionParentChild(CSmilBasicRegion* pRegion)
{
    HX_RESULT retVal = HXR_OK;

    if (pRegion &&
        pRegion->m_pSmilRegion &&
        pRegion->m_pSmilRegion->m_pNode &&
        pRegion->m_pSmilRegion->m_pNode->m_pParent)
    {
        SMILNodeTag eParentTag = pRegion->m_pSmilRegion->m_pNode->m_pParent->m_tag;
        CHXString   cParentID  = pRegion->m_pSmilRegion->m_pNode->m_pParent->m_id;
        if (eParentTag == SMILBasicLayout)
        {
            // Does the root layout exist?
            if (!m_pRootLayout)
            {
                // Create a root layout
                m_pRootLayout = new CSmilBasicRootLayout();
            }
            if (m_pRootLayout)
            {
                // Set our own parent member
                pRegion->m_pParent = m_pRootLayout;
                // Add a child to the root-layout
                retVal = m_pRootLayout->addChild(pRegion);
            }
            else
            {
                retVal = HXR_OUTOFMEMORY;
            }
        }
        else if (eParentTag == SMILRegion)
        {
            CSmilBasicRegion* pParent = getRegionByID(cParentID);
            if (pParent)
            {
                // Set our own parent member
                pRegion->m_pParent = pParent;
                // Add ourselves as a child of our parent
                retVal = pParent->addChild(pRegion);
                if (SUCCEEDED(retVal))
                {
                    // Copy our parent's flag which says
                    // whether or not we are descended from
                    // the root-layout
                    pRegion->m_bUnderRootLayout = pParent->m_bUnderRootLayout;
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)
        else if (eParentTag == SMILViewport)
        {
            CSmilBasicViewport* pPort = getViewport(cParentID);
            if (pPort)
            {
                // Set our own parent member
                pRegion->m_pParent = pPort;
                // Add ourselves as a child of our parent
                pPort->addChild(pRegion);
                // Clear the flag that says we are a descendant
                // of the root layout
                pRegion->m_bUnderRootLayout = FALSE;
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */
    }

    return retVal;
}

HX_RESULT CSmilDocumentRenderer::createRegionSites(CHXSimpleList* pChildList)
{
    HX_RESULT retVal = HXR_OK;

    // Don't do anything if we don't have any children
    if (pChildList)
    {
        // Loop through the children
        LISTPOSITION pos = pChildList->GetHeadPosition();
        while (pos && SUCCEEDED(retVal))
        {
            // Get the child region
            CSmilBasicRegion* pRegion = (CSmilBasicRegion*) pChildList->GetNext(pos);
            if (pRegion)
            {
                // Setup the site for this region
                retVal = createRegionSite(pRegion);
                if (SUCCEEDED(retVal))
                {
                    // We have successfully processed this region,
                    // so now process its children
                    retVal = createRegionSites(pRegion->m_pChildList);
                }
            }
        }
    }

    return retVal;
}

HX_RESULT CSmilDocumentRenderer::createRegionSite(CSmilBasicRegion* pRegion)
{
    HX_RESULT retVal = HXR_OK;

    if (pRegion &&
        pRegion->m_pParent &&
        pRegion->m_pParent->m_pSite)
    {
        // Make sure we don't already have a site
        HX_RELEASE(pRegion->m_pSite);
        // Create the site from its parent site - this
        // could either be a top-level site (root-layout
        // or viewport) or another region's site
        retVal = pRegion->m_pParent->m_pSite->CreateChild(pRegion->m_pSite);
        if (SUCCEEDED(retVal))
        {
            // Set the position of the site
            HXxPoint cPos = {pRegion->m_Rect.left, pRegion->m_Rect.top};
#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)
#if defined(XXXMEH_DO_VIEWPORT_TLC) && defined(_WINDOWS)
            if (pRegion->m_pParent &&
                !pRegion->m_pParent->m_pParent &&
                !pRegion->m_bUnderRootLayout)
            {
                cPos.x += GetSystemMetrics(SM_CXFIXEDFRAME);
                cPos.y += GetSystemMetrics(SM_CYFIXEDFRAME) + GetSystemMetrics(SM_CYCAPTION);
            }
#endif
#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */
            MLOG_LAYOUT(m_pErrorMessages,
                        "createRegionSite() region=%s SetSize(%ld,%ld)\n",
                        (const char*) pRegion->m_region, cPos.x, cPos.y);
            retVal = pRegion->m_pSite->SetPosition(cPos);
            if (SUCCEEDED(retVal))
            {
                // Set the size of the site
                HXxSize cSize = {HXxRECT_WIDTH(pRegion->m_Rect),
                                 HXxRECT_HEIGHT(pRegion->m_Rect)};
                MLOG_LAYOUT(m_pErrorMessages,
                            "createRegionSite() region=%s SetSize(%ld,%ld)\n",
                            (const char*) pRegion->m_region, cSize.cx, cSize.cy);
                retVal = pRegion->m_pSite->SetSize(cSize);
                if (SUCCEEDED(retVal))
                {
                    // Since region's have a backgroundColor, then
                    // we have to create a site user which will 
                    // draw this background color.
                    HX_RELEASE(pRegion->m_pSiteUser);
                    pRegion->m_pSiteUser = new CSmilSiteUser((CSmilSiteUserResponse*) this,
                                                             pRegion->m_ulBackgroundColor,
                                                             m_pContext, FALSE, pRegion->m_region);
                    if (pRegion->m_pSiteUser)
                    {
                        // AddRef the site user
                        pRegion->m_pSiteUser->AddRef();
                        // Attach this site user to the site
                        retVal = pRegion->m_pSite->AttachUser(pRegion->m_pSiteUser);
                        if (SUCCEEDED(retVal))
                        {
                            // If this region has a showBackground="always", then
                            // make it visible. Otherwise, hide it - the show event
                            // for the media playing in the region will make it
                            // visible later
                            if (pRegion->m_eShowBackground == ShowBackgroundAlways)
                            {
                                showSite(pRegion->m_pSite, TRUE);
                            }
                            else if (pRegion->m_eShowBackground == ShowBackgroundWhenActive)
                            {
                                showSite(pRegion->m_pSite, FALSE);
                            }
                        }
                    }
                    else
                    {
                        retVal = HXR_OUTOFMEMORY;
                    }
                }
            }
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

void CSmilDocumentRenderer::destroyRegionSites(CHXSimpleList* pChildList)
{
    // Do we have any children?
    if (pChildList)
    {
        // Loop through the children
        LISTPOSITION pos = pChildList->GetHeadPosition();
        while (pos)
        {
            // Get the region
            CSmilBasicRegion* pRegion = (CSmilBasicRegion*) pChildList->GetNext(pos);
            if (pRegion)
            {
                // We have to process our children first
                destroyRegionSites(pRegion->m_pChildList);
                // Do we have a site to destroy?
                if (pRegion->m_pParent &&
                    pRegion->m_pParent->m_pSite &&
                    pRegion->m_pSite)
                {
                    // Detach the site user - our ref on it will
                    // get released when the CSmilBasicRegion is
                    // destructed
                    pRegion->m_pSite->DetachUser();
                    // Destroy the parent-child relationship
                    pRegion->m_pParent->m_pSite->DestroyChild(pRegion->m_pSite);
                }
            }
        }
    }
}

CSmilBasicRegion* CSmilDocumentRenderer::getRegionByName(const char* pszName)
{
    CSmilBasicRegion* pRet = NULL;

    if (pszName && m_pRegionMap)
    {
        while (m_pRegionMapIterator)
        {
            const char* pszKey = NULL;
            void*       pVoid  = NULL;
            m_pRegionMap->GetNextAssoc(m_pRegionMapIterator, pszKey, pVoid);
            if (pVoid)
            {
                CSmilBasicRegion* pRegion = (CSmilBasicRegion*) pVoid;
                if (pRegion->m_pSmilRegion &&
                    pRegion->m_pSmilRegion->m_RegionName.GetLength() > 0 &&
                    strlen(pszName)                                  > 0 &&
                    pRegion->m_pSmilRegion->m_RegionName == pszName)
                {
                    pRet = pRegion;
                    break;
                }
            }
        }
    }

    return pRet;
}

STDMETHODIMP CSmilDocumentRenderer::SitePositionChanged(IHXSite* pSite, HXxPoint* pPoint)
{
    HX_RESULT retVal = HXR_OK;

    return retVal;
}

STDMETHODIMP CSmilDocumentRenderer::SiteSizeChanged(IHXSite* pSite, HXxSize* pSize)
{
    HX_RESULT retVal = HXR_OK;

    if (pSite && pSize)
    {
        if (m_pRootLayout &&
            m_pRootLayout->m_pSite &&
            pSite == m_pRootLayout->m_pSite)
        {
            // check for resize of top level site, we must have
            // a "site" and not already be resizing!
            if (!m_pRootLayout->m_bSiteChangingSize)
            {
                if (pSize->cx != HXxRECT_WIDTH(m_pRootLayout->m_Rect) ||
                    pSize->cy != HXxRECT_HEIGHT(m_pRootLayout->m_Rect))
                {
                    MLOG_LAYOUT(m_pErrorMessages,
                                "SiteSizeChanged(%ld,%ld) root-layout m_ulCurrentTime=%lu t=%lu\n",
                                pSize->cx, pSize->cy, m_ulCurrentTime, HX_GET_BETTERTICKCOUNT());
                    m_pRootLayout->m_bSiteChangingSize = TRUE;
                    resizeTopLevelBox(m_pRootLayout, m_pRootLayout->m_OriginalSize, *pSize);
                    forceFullRedraw(m_pRootLayout->m_pSite);
                    m_pRootLayout->m_bSiteChangingSize = FALSE;
                }
            }
        }
#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)
        else
        {
            CSmilBasicViewport* pPort = getViewportBySite(pSite);
            if (pPort)
            {
                // check for resize of top level site, we must have
                // a "site" and not already be resizing!
                if (!pPort->m_bSiteChangingSize)
                {
                    if (pSize->cx != HXxRECT_WIDTH(pPort->m_Rect) ||
                        pSize->cy != HXxRECT_HEIGHT(pPort->m_Rect))
                    {
                        pPort->m_bSiteChangingSize = TRUE;
                        resizeTopLevelBox(pPort, pPort->m_OriginalSize, *pSize);
                        forceFullRedraw(pPort->m_pSite);
                        pPort->m_bSiteChangingSize = FALSE;
                    }
                }
            }
        }
#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

STDMETHODIMP CSmilDocumentRenderer::BeginDone(UINT16 uGroupIndex, UINT16 uTrackIndex)
{
    HX_RESULT retVal = HXR_OK;

    return retVal;
}

STDMETHODIMP CSmilDocumentRenderer::SeekDone(UINT16 uGroupIndex, UINT16 uTrackIndex,
                                             UINT32 ulSeekTime)
{
    HX_RESULT retVal = HXR_OK;

    return retVal;
}

STDMETHODIMP CSmilDocumentRenderer::PauseDone(UINT16 uGroupIndex, UINT16 uTrackIndex)
{
    HX_RESULT retVal = HXR_OK;

    return retVal;
}

STDMETHODIMP CSmilDocumentRenderer::StopDone(UINT16 uGroupIndex, UINT16 uTrackIndex)
{
    HX_RESULT retVal = HXR_OK;

    return retVal;
}

STDMETHODIMP CSmilDocumentRenderer::OnSoundLevelAnimation(UINT16 uGroupIndex,
                                                          UINT16 uTrackIndex,
                                                          UINT32 ulSoundLevelAnimationTime)
{
    // XXXMEH
//    char szDbgStr[256];
//    sprintf(szDbgStr, "enter OnSoundLevelAnimation(%u,%u,%lu) m_ulCurrentTime=%lu tick=%lu threadID=%lu\n",
//            uGroupIndex, uTrackIndex, ulSoundLevelAnimationTime,
//            m_ulCurrentTime, HX_GET_BETTERTICKCOUNT(), GetCurrentThreadId());
//    OutputDebugString(szDbgStr);
    HX_RESULT retVal = HXR_OK;

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)
    // Get the playto assocation
    SMILPlayToAssoc* pPlayTo = getPlayToAssoc(uGroupIndex, uTrackIndex);
    if (pPlayTo)
    {
        // If there is another animation getting added right
        // now, then we need to hold up until it is added
        if (m_pSoundLevelMutex) m_pSoundLevelMutex->Lock();
        // Find the animation element which is animating
        // the soundLevel of this region
        // XXXMEH - TODO - replace this with a more efficient method
        // of finding the correct animation sandwich
        CSmilAnimateInfo* pInfo = NULL;
        if (m_pActiveAnimations)
        {
            LISTPOSITION pos = m_pActiveAnimations->GetHeadPosition();
            while (pos)
            {
                CSmilAnimateInfo* pListInfo =
                    (CSmilAnimateInfo*) m_pActiveAnimations->GetNext(pos);
                if (pListInfo &&
                    pListInfo->m_pSandwich)
                {
                    CHXString cRegionID(pListInfo->m_pSandwich->GetTargetElementID());
                    if (cRegionID == pPlayTo->m_playTo &&
                        pListInfo->m_pSandwich->GetAttributeName() == kAttrNameSoundLevel)
                    {
                        pInfo = pListInfo;
                        break;
                    }
                }
            }
        }
        // Now compute the value of the animation at
        // the time we were given
        if (pInfo && pInfo->m_pSandwich && pInfo->m_pUnder)
        {
            // We want to cap the time at the duration of
            // the current group - we don't want to send
            // a timesync that is greater than the duration.
            UINT32 ulAnimTime = ulSoundLevelAnimationTime;
            if (ulAnimTime > m_ulCurGroupDuration &&
                m_ulCurGroupDuration > 0)
            {
                ulAnimTime = m_ulCurGroupDuration;
            }
            // Compute the value of the animation at the time
            CAttr cRet = pInfo->m_pSandwich->GetValue(ulAnimTime,
                                                      pInfo->m_pUnder,
                                                      pInfo->m_pDepend);
            // Get the sound level and clamp it at 0 on the bottom end.
            double dVal = cRet.GetValueDouble();
            if (dVal < 0.0) dVal = 0.0;
            // XXXMEH - what should we clamp it to at the top end?
            // I'll choose 10x as the limit for now, which would be 1000%.
            if (dVal > MAX_ALLOWED_SOUNDLEVEL) dVal = MAX_ALLOWED_SOUNDLEVEL;
            // Get this value as a UINT16
            UINT16 usLevel = (UINT16) (dVal + 0.5);
            // Set the soundLevel for the track
            IHXTrack* pTrack = NULL;
            HX_RESULT  rv     = getTrack(uGroupIndex, uTrackIndex, pTrack);
            if (SUCCEEDED(rv))
            {
                MLOG_ANIM(m_pErrorMessages,"OnSoundLevelAnimation(%u,%u,%lu) m_ulCurrentTime=%lu "
                          "tick=%lu I call SetSoundLevel(%u)\n",
                          uGroupIndex, uTrackIndex, ulSoundLevelAnimationTime,
                          m_ulCurrentTime, HX_GET_BETTERTICKCOUNT(), usLevel);
                pTrack->SetSoundLevel(usLevel);
            }
            HX_RELEASE(pTrack);
            // Adjust the sandwich
            pInfo->m_pSandwich->AdjustLayers(ulAnimTime);
            // Are there no more layers left in this animation?
            if (pInfo->m_pSandwich->GetNumLayers() == 0)
            {
                // This animation is done, so finish call
                // IHXTrack::EndSoundLevelAnimation
                finishSoundLevelAnimation(pInfo);
                // Remove the animation from the active 
                // animation list
                removeAnimation(pInfo);
                // Now we can delete the info object
                HX_DELETE(pInfo);
            }
        }
        // Now we can release the mutex, since we
        // are done messing with the animations.
        if (m_pSoundLevelMutex) m_pSoundLevelMutex->Unlock();
    }
    else
    {
        retVal = HXR_FAIL;
    }
#endif /* #if defined(HELIX_FEATURE_SMIL2_ANIMATION) */

    // XXXMEH
//    sprintf(szDbgStr, "exit  OnSoundLevelAnimation(%u,%u,%lu) m_ulCurrentTime=%lu tick=%lu threadID=%lu\n",
//            uGroupIndex, uTrackIndex, ulSoundLevelAnimationTime,
//            m_ulCurrentTime, HX_GET_BETTERTICKCOUNT(), GetCurrentThreadId());
//    OutputDebugString(szDbgStr);

    return retVal;
}

#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)

STDMETHODIMP CSmilDocumentRenderer::ViewPortOpened(const char* pszViewPort)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pszViewPort)
    {
        CSmilBasicViewport* pPort = getViewport(pszViewPort);
        if (pPort)
        {
            // Set the open flag
            pPort->m_bOpen = TRUE;
            // Clear the return value
            retVal = HXR_OK;

          // /Raise the "open" event:
          HX_RESULT rc = m_pSmilParser->tryToResolveBeginEndEvents(
                "topLayoutOpenEvent", pszViewPort, m_ulCurrentTime);
          if SUCCEEDED(rc)
          {
            handleElements();
          }
        }
    }

    return retVal;
}

STDMETHODIMP CSmilDocumentRenderer::ViewPortClosed(const char* pszViewPort)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pszViewPort)
    {
        CSmilBasicViewport* pPort = getViewport(pszViewPort);
        if (pPort)
        {
            // Clear the open flag
            pPort->m_bOpen = FALSE;
            // Clear the return value
            retVal = HXR_OK;

          // /Raise the "close" event:
          HX_RESULT rc = m_pSmilParser->tryToResolveBeginEndEvents(
                "topLayoutCloseEvent", pszViewPort, m_ulCurrentTime);
          if SUCCEEDED(rc)
          {
            handleElements();
          }
        }
    }

    return retVal;
}

STDMETHODIMP CSmilDocumentRenderer::ViewPortShown(const char* pszViewPort)
{
    HX_RESULT retVal = HXR_OK;

    return retVal;
}

STDMETHODIMP CSmilDocumentRenderer::ViewPortHidden(const char* pszViewPort)
{
    HX_RESULT retVal = HXR_OK;

    return retVal;
}

STDMETHODIMP CSmilDocumentRenderer::ViewPortFocusSet(const char* pszViewPort)
{
    HX_RESULT retVal = HXR_OK;

    return retVal;
}

STDMETHODIMP CSmilDocumentRenderer::ViewPortZOrderSet(const char* pszViewPort, UINT32 ulZOrder)
{
    HX_RESULT retVal = HXR_OK;

    return retVal;
}

#ifdef XXXMEH_DO_VIEWPORT_TLC

STDMETHODIMP CSmilDocumentRenderer::OnViewPortOpen(IHXValues* pValues, IHXSiteUser* pSiteUser)
{
    HX_RESULT retVal = HXR_OK;

    if (pValues && m_pContext)
    {
        // Get the common class factory interface
        IHXCommonClassFactory* pFactory = NULL;
        retVal = m_pContext->QueryInterface(IID_IHXCommonClassFactory,
                                            (void**) &pFactory);
        if (SUCCEEDED(retVal))
        {
            // Create an IHXSiteWindowed
            IHXSiteWindowed* pSiteWindowed = NULL;
            retVal = pFactory->CreateInstance(CLSID_IHXSiteWindowed,
                                              (void**) &pSiteWindowed);
            if (SUCCEEDED(retVal))
            {
                // Call Create() on the site
                UINT32 ulStyle = 0;
#if defined(_WINDOWS)
                ulStyle = WS_CAPTION | WS_SIZEBOX | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
#endif
                retVal = pSiteWindowed->Create(NULL, ulStyle);
                if (SUCCEEDED(retVal))
                {
                    // Get the IHXSite interface
                    IHXSite* pSite = NULL;
                    retVal = pSiteWindowed->QueryInterface(IID_IHXSite,
                                                           (void**) &pSite);
                    if (SUCCEEDED(retVal))
                    {
                        // Get the "playto" property
                        IHXBuffer* pPlayToStr = NULL;
                        retVal = pValues->GetPropertyCString("playto", pPlayToStr);
                        if (SUCCEEDED(retVal))
                        {
                            // Get the string
                            const char* pszPlayTo = (const char*) pPlayToStr->GetBuffer();
                            // Get the IHXValues interface from the site
                            IHXValues* pSiteValues = NULL;
                            pSite->QueryInterface(IID_IHXValues, (void**) &pSiteValues);
                            if (pSiteValues)
                            {
                                // Set the "name" property in the site properties
                                pSiteValues->SetPropertyCString("name", pPlayToStr);
                                // Get the viewport
                                CSmilBasicViewport* pPort = getViewport(pszPlayTo);
                                if (pPort && pPort->m_pSiteUser)
                                {
                                    // QI for IHXSiteUser
                                    IHXSiteUser* pSiteUser = NULL;
                                    retVal = pPort->m_pSiteUser->QueryInterface(IID_IHXSiteUser,
                                                                                (void**) &pSiteUser);
                                    if (SUCCEEDED(retVal))
                                    {
                                        // Add the viewport site to the map
                                        if (!m_pViewPortSiteMap)
                                        {
                                            m_pViewPortSiteMap = new CHXMapStringToOb();
                                        }
                                        if (m_pViewPortSiteMap)
                                        {
                                            // AddRef the site before going in the map
                                            pSite->AddRef();
                                            // Put it into the map
                                            m_pViewPortSiteMap->SetAt(pszPlayTo, pSite);
                                        }
                                        // Attach the viewport's site user to the site
                                        retVal = pSite->AttachUser(pSiteUser);
                                    }
                                    HX_RELEASE(pSiteUser);
                                }
                                else
                                {
                                    retVal = HXR_FAIL;
                                }
                            }
                            HX_RELEASE(pSiteValues);
                        }
                        HX_RELEASE(pPlayToStr);
                    }
                    HX_RELEASE(pSite);
                }
            }
            HX_RELEASE(pSiteWindowed);
        }
        HX_RELEASE(pFactory);
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

STDMETHODIMP CSmilDocumentRenderer::OnViewPortClose(const char* pszViewPort)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pszViewPort && m_pViewPortSiteMap)
    {
        void* pObj = NULL;
        if (m_pViewPortSiteMap->Lookup(pszViewPort, pObj))
        {
            // Get the site
            IHXSite* pSite = (IHXSite*) pObj;
            // Remove it from the map
            m_pViewPortSiteMap->RemoveKey(pszViewPort);
            // Detach the user
            pSite->DetachUser();
            // Since we called IHXSiteWindowed::Create(), then
            // we need to call IHXSiteWindowed::Destroy() as well.
            IHXSiteWindowed* pSiteWindowed = NULL;
            pSite->QueryInterface(IID_IHXSiteWindowed, (void**) &pSiteWindowed);
            if (pSiteWindowed)
            {
                pSiteWindowed->Destroy();
            }
            HX_RELEASE(pSiteWindowed);
            // Release the map's ref on the site
            HX_RELEASE(pSite);
            // Clear the return value
            retVal = HXR_OK;
        }
    }

    return retVal;
}

STDMETHODIMP CSmilDocumentRenderer::OnViewPortShow(const char* pszViewPort)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pszViewPort && m_pViewPortSiteMap)
    {
        void* pObj = NULL;
        if (m_pViewPortSiteMap->Lookup(pszViewPort, pObj) && pObj)
        {
            // Get the site
            IHXSite* pSite = (IHXSite*) pObj;
            // Do a show on the site
            showSite(pSite, TRUE);
            // Clear the return value
            retVal = HXR_OK;
        }
    }

    return retVal;
}

STDMETHODIMP CSmilDocumentRenderer::OnViewPortHide(const char* pszViewPort)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pszViewPort && m_pViewPortSiteMap)
    {
        void* pObj = NULL;
        if (m_pViewPortSiteMap->Lookup(pszViewPort, pObj) && pObj)
        {
            // Get the site
            IHXSite* pSite = (IHXSite*) pObj;
            // Do a hide on the site
            showSite(pSite, FALSE);
            // Clear the return value
            retVal = HXR_OK;
        }
    }

    return retVal;
}

STDMETHODIMP CSmilDocumentRenderer::OnViewPortFocus(const char* pszViewPort)
{
    HX_RESULT retVal = HXR_OK;

    return retVal;
}

STDMETHODIMP CSmilDocumentRenderer::OnViewPortZOrder(const char* pszViewPort, UINT32 ulZOrder)
{
    HX_RESULT retVal = HXR_OK;

    return retVal;
}

#endif // #ifdef XXXMEH_DO_VIEWPORT_TLC

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

BOOL
CSmilDocumentRenderer::draw(HXxEvent* pEvent, IHXSite* pSite, 
                      UINT32 ulBgColor)
{
    if(pEvent &&
       pEvent->event == HX_SURFACE_UPDATE && 
       pEvent->result == HXR_OK)
    {
        // Set up color
        UINT32 ulColor = ulBgColor;
        // Set up bitmap info header
        HXBitmapInfoHeader cHeader;
        cHeader.biSize          = 40;
        cHeader.biWidth         = 1;
        cHeader.biHeight        = 1;
        cHeader.biPlanes        = 1;
        cHeader.biBitCount      = 32;
        cHeader.biCompression   = (ulBgColor & 0xFF000000 ? HX_ARGB : HX_RGB);
        cHeader.biSizeImage     = 0;
        cHeader.biXPelsPerMeter = 0;
        cHeader.biYPelsPerMeter = 0;
        cHeader.biClrUsed       = 0;
        cHeader.biClrImportant  = 0;
        cHeader.rcolor          = 0;
        cHeader.gcolor          = 0;
        cHeader.bcolor          = 0;
        // Set up src rect
        HXxRect cSrcRect = {0, 0, 1, 1};
        // Set up dst rect
        HXxSize cSize = {0, 0};
        pSite->GetSize(cSize);
        HXxRect cDstRect = {0, 0, cSize.cx, cSize.cy};
        // Do the blt
        IHXVideoSurface* pSurf = (IHXVideoSurface*) pEvent->param1;
        if(pSurf)
        {
            pSurf->AddRef();
            pEvent->result = pSurf->Blt((BYTE*) &ulColor,
                                        &cHeader,
                                        cDstRect,
                                        cSrcRect);
            pSurf->Release();
        }
    }
    return TRUE;
}

/*
 * IHXRendererAdviseSink methods
 */

STDMETHODIMP
CSmilDocumentRenderer::TrackDurationSet(UINT32 ulGroupIndex,
                              UINT32 ulTrackIndex,
                              UINT32 ulDuration,
                              UINT32 ulDelay,
                              BOOL bLiveSource)
{
    MLOG_SRC(m_pErrorMessages,"TrackDurationSet(%lu,%lu,%lu,%lu,%lu) m_ulCurrentTime=%lu\n",
             ulGroupIndex,
             ulTrackIndex,
             ulDuration,
             ulDelay,
             bLiveSource,
             m_ulCurrentTime);

    HX_RESULT retVal = HXR_OK;

    // /This is needed for fixing PR 66391, so we don't remove a track while
    // core is still setting it up:
    m_bInTrackDurationSetCall = TRUE;

    SMILPlayToAssoc* pPlayToAssoc = getPlayToAssoc((UINT16)ulGroupIndex,
                                       (UINT16)ulTrackIndex);

#if defined(_DEBUG)  &&  defined(XXXEH_DEBUG_HANDLESOURCE_AND_TRACKDURATIONSET)
if (!pPlayToAssoc)
{
    {FILE* f1 = ::fopen("c:\\smil2AddDuration.txt", "a+"); ::fprintf(f1, "TrackDurationSet(media id=%s, duration=%lu, delay=%lu\n",
      "---####--NULL playToAssoc--####----", ulDuration, ulDelay);::fclose(f1);}
}
#endif


    // /XXXEH- talk to Core dudes about this:
#define TRACKDURSET_INDEFDURATION_FUDGE_FACTOR   1000 /*milliseconds*/
    // /Work-around for Mac PR 81141: core is not giving us the duration we
    // requested (namely WAY_IN_THE_FUTURE) on the Mac; it's returning
    // 1981341952 instead of 1981342000 (i.e., is 48 millisecs too small,
    // probably due to rounding error).  Let's adjust if so:
    if (ulDuration > WAY_IN_THE_FUTURE)
    {
      if (ulDuration - WAY_IN_THE_FUTURE < TRACKDURSET_INDEFDURATION_FUDGE_FACTOR)
      {
          ulDuration = WAY_IN_THE_FUTURE;
      }
    }
    else // /ulDuration < WAY_IN_THE_FUTURE
    {
      if (WAY_IN_THE_FUTURE - ulDuration < TRACKDURSET_INDEFDURATION_FUDGE_FACTOR)
      {
          ulDuration = WAY_IN_THE_FUTURE;
      }
    }

    if(pPlayToAssoc)
    {
      ULONG32 ulDurToUseForGroup = ulDuration;
      CSmilElement* pThisElement = 
            m_pSmilParser->findElement(pPlayToAssoc->m_id);
#if defined(_DEBUG)  &&  defined(XXXEH_DEBUG_HANDLESOURCE_AND_TRACKDURATIONSET)
{FILE* f1 = ::fopen("c:\\smil2AddDuration.txt", "a+"); ::fprintf(f1, "TrackDurationSet(media id=%s, duration=%lu, delay=%lu\n",
      (const char*)pPlayToAssoc->m_id, ulDuration, ulDelay);::fclose(f1);}
#endif
      if (!pPlayToAssoc->m_bDurationResolved)
      {
          BOOL bAlreadyHandledAsDiscreteMedia = FALSE;
          
          if (!pPlayToAssoc->m_bIsPersistentSource)
          {
#if defined(HANDLE_DISCRETE_MEDIA_IN_TrackDurationSet)
          // /First, let's see if this is discrete media that needs to have
          // its duration reset to 0:
          BOOL bIsDiscreteImageMedia = FALSE;
          BOOL bIsDiscreteTextMedia  = FALSE;
          IHXBuffer* pIntrinsicDurType = NULL;
          IHXStreamSource* pStreamSource = NULL;
          UINT32 ulStreamHeaderDuration = 0;
          const char* pszStreamMimeType = NULL;
          HX_RESULT pnrIntrDurTypeRet = HXR_FAIL;
          HX_RESULT pnrStrmHdrDurationRet = HXR_FAIL;

          IHXGroup* pGroup = NULL;

          // /Adjust for min
          if (pThisElement)
          {
            if ((UINT32)-1 != pThisElement->m_ulMinActiveDur)
            {
                if (ulDurToUseForGroup < pThisElement->m_ulMinActiveDur)
                {
                  ulDurToUseForGroup = pThisElement->m_ulMinActiveDur;
                }
            }
            if ((UINT32)-1 != pThisElement->m_ulMaxActiveDur)
            {
                if (ulDurToUseForGroup > pThisElement->m_ulMaxActiveDur)
                {
                  ulDurToUseForGroup = pThisElement->m_ulMaxActiveDur;
                }
            }
          }

          // /If it's not live and it has no authored dur (explicitly-
          // set dur, end, min) then see if it's "discrete media" and,
          // if so, set its duration to 0:
          if(!bLiveSource  &&  pThisElement  &&
                (UINT32)-1 == pThisElement->m_ulAuthoredDur  &&
                // /Fixes PR 54282: if end is a sync-arc, m_ulAuthoredDur
                // is not ever set to (end-begin) so we would erroneously
                // treat this (img or text) as discrete media even though
                // it *does* have an explicit end. (Used to work 5/11!?)
                !pThisElement->m_bHasExplicitEnd  &&
                0 == pThisElement->m_ulMinActiveDur  &&
                m_pGroupMap  &&
                m_pGroupMap->Lookup((UINT16)ulGroupIndex,(void*&)pGroup))
          {
            IHXTrack* pTrack = NULL;
            IHXGroup2* pGroup2 = NULL;
            HX_RESULT pGrp2Rslt = pGroup->QueryInterface(IID_IHXGroup2,
                  (void**)&pGroup2);
            // /This AddRef()s pTrack so be sure to release it when done:
            if (HXR_OK==pGrp2Rslt   &&  HXR_OK==pGroup2->GetIHXTrack(
                  (UINT16)ulTrackIndex, pTrack))
            {
                if (HXR_OK == pTrack->GetSource(pStreamSource)  &&
                      pStreamSource)
                {
                  UINT16 uNumStreams = pStreamSource->GetStreamCount();
                  IHXStream* pStream = NULL;
                  IHXValues* pStreamHeader = NULL;

                  // /Use stream #0:
                  IUnknown* pUnk = NULL;
                  if (uNumStreams >= 1)
                  {
                      pStreamSource->GetStream(0, pUnk);
                      HX_ASSERT(pUnk);
                      if (HXR_OK == pUnk->QueryInterface(IID_IHXStream,
                            (void**)&pStream) )
                      {
                        pszStreamMimeType = pStream->GetStreamType();
                        pStreamHeader = pStream->GetHeader();
                        if (pStreamHeader)
                        {
                            pnrIntrDurTypeRet =
                               pStreamHeader->GetPropertyCString(
                               "intrinsicDurationType",
                               pIntrinsicDurType);
                        }
                      }
                  }
                  // /We don't want to handle discrete media that's one
                  // of multiple streams in a track since it will take
                  // on the dur of the other streams so leave it alone.
                  if (uNumStreams == 1)
                  {
                      if (pStream)
                      {
                        if (pStreamHeader)
                        {
                            pnrStrmHdrDurationRet =
                               pStreamHeader->GetPropertyULONG32(
                               "duration",
                               ulStreamHeaderDuration);
                            HX_ASSERT(HXR_OK ==
                                  pnrStrmHdrDurationRet);

                            if (pszStreamMimeType  &&
                              0==strcmp(pszStreamMimeType,
                              "application/vnd.rn-gifstream2")  ||
                              0==strcmp(pszStreamMimeType,
                              "application/vnd.rn-gifstream3")  ||
                              0==strcmp(pszStreamMimeType,
                              "application/vnd.rn-jpegstream")  ||
                              0==strcmp(pszStreamMimeType,
                              "application/vnd.rn-pngstream")  ||
                              0==strcmp(pszStreamMimeType,
                              "application/vnd.rn-jpegstream") )
                            {
                              bIsDiscreteImageMedia = TRUE;
                            }
                            else if (pszStreamMimeType  &&
                                  0==strcmp(pszStreamMimeType,
                                  "text/plain"))
                            {
                              bIsDiscreteTextMedia = TRUE;
                            }
                        }
                      }
                  }
                  HX_RELEASE(pStreamHeader);
                  HX_RELEASE(pStream);
                  HX_RELEASE(pUnk);
                }
#define XXXEH_HACK_TO_GET_AROUND_CORE_BAD_CURRENTGROUP_IN_GetSourceInfo
#if defined(XXXEH_HACK_TO_GET_AROUND_CORE_BAD_CURRENTGROUP_IN_GetSourceInfo)
// /XXXEH- This happens in SMIL 2.0 Structure Interop test case #1.1:
// this is what's failing in the core that got us here; m_nCurrentGroup
// is sometimes 1 and sometimes (erroneously) 0:
/*HXPlayer::GetSourceInfo(...)
{
...
    if (uGroupIndex != m_nCurrentGroup)
    {
*/
                // /XXXEH- treat as discrete image for interop:
                else if(5000 == ulDuration)
                {
                  HX_ASSERT(0);
                  bIsDiscreteImageMedia = TRUE;
                  ulStreamHeaderDuration = 5000;
                }
#endif
            }
            HX_RELEASE(pTrack);
            HX_RELEASE(pGroup2);

            // /If the stream declared itself as discrete media and there
            // is no dur, end, max, ...etc. attribute on the media
            // element that overrides its intrinsic duration, then we
            // should use 0s for its duration even if the stream header
            // says otherwise.  This allows new file formats to stream to
            // old renderers and vice versa, without breaking old content
            // or having new SMIL content behave differently depending on
            // the version of the discrete media file format:
            BOOL bIsDiscreteMedia = FALSE;
            if (HXR_OK == pnrIntrDurTypeRet  &&  pIntrinsicDurType)
            {
                const char* pIntrDurBuf =
                      (const char*)pIntrinsicDurType->GetBuffer();
                if (pIntrDurBuf  &&  0==strcmp(pIntrDurBuf,
                      "intrinsicDurationDiscrete"))
                {
                  bIsDiscreteMedia = TRUE;
                }
            }
            // /else is undeclared, so assume non-discrete media
            // unless the duration is exactly 5000msec and the stream
            // mime type is that of a discrete media type (gif, jpg,
            // png, plain text):
            else
            {
                const char* pStreamURL = NULL;
                // /We shouldn't be here if this is NULL:
    // /              HX_ASSERT(pStreamSource);
                if (pStreamSource)
                {
                  pStreamURL = pStreamSource->GetURL();
                }

                if (5000+ulDelay == ulStreamHeaderDuration  &&
                      (UINT32)-1 == pThisElement->m_ulAuthoredDur)
                {
                  // /For testing only; does ulDuration include
                  // delay?  If so, we need to adjust for delay...:
                  HX_ASSERT(5000+ulDelay == ulDuration);
                  if (bIsDiscreteImageMedia)
                  {
                      bIsDiscreteMedia = TRUE;
                  }
                  else if (pStreamURL)
                  {
                      const char* pTmp = (const char*)pStreamURL;
                      LONG32 lLen = strlen(pStreamURL);
                      if (lLen > 4)
                      {
                        pTmp = pTmp + (lLen-4);// /4==len(".foo")
                        if (0==strcmp(pTmp, ".gif")  ||
                              0==strcmp(pTmp, ".jpg")  ||
                              0==strcmp(pTmp, ".png") )
                        {
                            bIsDiscreteMedia = TRUE;
                            // /For testing, let's make sure that
                            // we always have an image mime type
                            // when we simultaneously have an
                            // image extension:
                            HX_ASSERT(bIsDiscreteImageMedia);
                        }
                      }
                  }
                }
                else if (60000+ulDelay == ulStreamHeaderDuration  &&
                      (UINT32)-1 == pThisElement->m_ulAuthoredDur)
                {
                  // /For testing only; does ulDuration include
                  // delay?  If so, we need to adjust for delay...:
                  HX_ASSERT(60000+ulDelay == ulDuration);
                  if (bIsDiscreteTextMedia)
                  {
                      bIsDiscreteMedia = TRUE;
                  }
                  // /Might be old RealText-served .txt file with
                  // the RealText stream mime type:
                  else
                  {
                      const char* pTmp = (const char*)pStreamURL;
                      LONG32 lLen = strlen(pTmp);
                      if (lLen > 4)
                      {
                        pTmp = pTmp + (lLen-4);// /4==len(".txt")
                        if (0==strcmp(pTmp, ".txt"))
                        {
                            bIsDiscreteMedia = TRUE;
                        }
                      }
                  }
                }
            }

            if (bIsDiscreteMedia)
            {
                bAlreadyHandledAsDiscreteMedia = TRUE;
                pPlayToAssoc->m_bDurationResolved = TRUE;
                pPlayToAssoc->m_ulDelay = ulDelay;
                // /Sets it so parent will use 0 dur:
                // /Note: this used to subtract the ulDelay but that was
                // totally messed up since *THIS* particular duration
                // variable does not account for the delay (unlike other
                // duration variables used in our system).  Removing that
                // fixes the last part of SMIL 2.0 Interop Timing case
                // 1.15 (at least it lets the second image show up) and
                // fixes a whole slew of SMIL 2.0 Interop Transition
                // cases including 1.1 and 1.2:
                ulDuration = ZERO_DUR; 
                BOOL bDurationIncludesDelay = FALSE;

                BOOL bResetDurationHandled = FALSE;

                // /If this had a repeatDur, repeatCount, or repeat on
                // it, we need to set its seq-for-repeat-wrapper
                // parent's dur to 0 (or 1?); get syncAncestor and then
                // see if its m_bIsSeqWrapperForRepeatElement is TRUE:
                SMILNode* pSyncAncestor = NULL;
                if (pThisElement  &&  pThisElement->m_pNode)
                {
                  pSyncAncestor = m_pSmilParser->getSyncAncestor(
                        pThisElement->m_pNode);
                  if (pSyncAncestor  &&
                        pSyncAncestor->m_bIsSeqWrapperForRepeatElement)
                  {
                      if (pSyncAncestor->m_pElement)
                      {
                        UINT32 ulPriorPureDuration =
                              pThisElement->getPureDuration();
                        if (ZERO_DUR == ulPriorPureDuration)
                        {   // /Don't send same value as prior:
                            ulPriorPureDuration++;
                        }
                        // /XXXEH- use 1 (not 0) to keep core happy:
                        m_pSmilParser->resetTimelineElementDuration(
                              pSyncAncestor->m_id, ZERO_DUR,
                              ulPriorPureDuration);
                        bResetDurationHandled = TRUE;
                        m_pSmilParser->durationResolved(
                              pSyncAncestor->m_id, ZERO_DUR, TRUE);
                      }
                  }
                }

                // /HOWEVER (see note about not adding delay to duration,
                // above), we need to add beginOffset to duration, but
                // ONLY if it's set to a valid value above 0:
                if (pThisElement  &&  pThisElement->m_pNode)
                {
                  if (pThisElement->m_bBeginOffsetSet  &&
                        pThisElement->m_lBeginOffset > 0)
                  {
                      ulDuration += ulDelay;
                      bDurationIncludesDelay = TRUE;
                  }
                }

                // /To fix PR 79748 (broken on 20010307 when ulDelay was
                // no longer subtracted from ulDuration, here), we need
                // to subtract the ulDelay once again (as we did prior to
                // 20010307) to send the pure duration to
                // durationResolved() which in turn calls
                // CSmilTimelineElement::setDuration() which assumes
                // the duration passed to it doesn't include the syncbase
                // offset.  The "broken" file that got fixed on 20010307,
                // called "...discrete_media_with_begin_offset_causes_...",
                // still works with this reversion, so something in the
                // core must have been fixed since.  20010307 bug was an
                // infinite loop in core for which this was a work-around:
                pPlayToAssoc->m_ulDuration = ulDuration -
                      // /Helps fix PR 82119, by only subtracting the
                      // delay if we know it's been added, above:
                      (bDurationIncludesDelay? ulDelay : 0);
                HX_ASSERT(pThisElement->m_ulDuration == pThisElement->getPureDuration());
                m_pSmilParser->durationResolved(pPlayToAssoc->m_id,
                      pPlayToAssoc->m_ulDuration);
                if (!bResetDurationHandled) // /Don't call reset twice:
                {
                  UINT32 ulPriorPureDuration = pThisElement->getPureDuration();
                  if (pPlayToAssoc->m_ulDuration == ulPriorPureDuration)
                  {   // /Don't send same value as prior:
                      ulPriorPureDuration++;
                  }

                  m_pSmilParser->resetTimelineElementDuration(
                        pPlayToAssoc->m_id,
                        pPlayToAssoc->m_ulDuration, ulPriorPureDuration);
                }
                handleElements();
            }
            HX_RELEASE(pIntrinsicDurType);
            HX_RELEASE(pStreamSource);
          }
#endif /* HANDLE_DISCRETE_MEDIA_IN_TrackDurationSet */
          }

          if (!bAlreadyHandledAsDiscreteMedia)
          {
            SMILNode* pSyncBaseNode = NULL;
            ULONG32 ulDelayBeyondSyncBase = 0;
            BOOL bAdjustedDurForPartialPlayFactor = FALSE;
            // /Had to add this now that external media markers are
            // causing playToAssoc's id to be something like
            // "xmmf_0x0..."
            if (pThisElement)
            {
                // /Part of fix for PR 57230; SMIL element needs to keep
                // track of whether or not it's live so its 0 dur won't
                // be used to compute its end time:
                if (bLiveSource)
                {
                  HX_ASSERT(pPlayToAssoc->m_bLiveSource);
                  pThisElement->m_bCurrentSourceIsLive = TRUE;
                }

                // /Fixes PR 54251:
                // /If partialPlayFactor is < 1.0 and this is not a repeat
                // element (i.e., initial partial play factor is < 1.0),
                // and if min is not constraining the lower end,
                // then multiply the ulDuration by this factor:
                if (pThisElement->m_pNode->m_fPartialPlayFactor < 1.0  &&
                      (UINT32)-1 == pThisElement->m_ulDuration  &&                      
                      ulDurToUseForGroup >
                      pThisElement->m_ulMinActiveDur)
                {
                  double fDur = double(float(ulDuration - ulDelay));
                  fDur *= pThisElement->m_pNode->m_fPartialPlayFactor;
                  ULONG32 ulDurTmp = ULONG32(float(fDur)) + ulDelay;
                  if (ulDurTmp < ulDurToUseForGroup)
                  {
                      ulDurToUseForGroup = ulDurTmp;
                      if (pThisElement->m_ulMinActiveDur >
                            ulDurToUseForGroup)
                      {
                        ulDurToUseForGroup =
                              pThisElement->m_ulMinActiveDur;
                      }
                      bAdjustedDurForPartialPlayFactor = TRUE;
                  }
                }

                pSyncBaseNode = m_pSmilParser->getSyncAncestor(
                      pThisElement->m_pNode);
                if (!pSyncBaseNode  ||  !pSyncBaseNode->m_pElement)
                {
                  HX_ASSERT(pSyncBaseNode  &&  pSyncBaseNode->m_pElement);
                  pSyncBaseNode = pThisElement->m_pNode->m_pParent;
                }

                HX_ASSERT(ulDelay <= pThisElement->m_ulDelay);

                ulDelayBeyondSyncBase = pThisElement->m_ulDelay -
                      pSyncBaseNode->m_pElement->m_ulDelay;
                // /Hold on, though: in a seq, the true "sync base" is
                // actually the prior sibling's end, not the begin of the
                // time container, i.e, sync ancestor, (unless there is
                // no prior sibling) so we don't want to use the whole
                // delay from the parent in that case (just the begin
                // offset part of this delay):
                if (SMILSeq == pSyncBaseNode->m_tag)
                {
                  ulDelayBeyondSyncBase = 0;
                  if (pThisElement->m_bBeginOffsetSet)
                  {
                      ulDelayBeyondSyncBase =
                            (pThisElement->m_lBeginOffset > 0 ?
                            (UINT32)pThisElement->m_lBeginOffset : 0);
                  }
                }
            }

            pPlayToAssoc->m_bDurationResolved = TRUE;
            pPlayToAssoc->m_ulDelay = ulDelay;
                // If the value of the element duration has
                // changed since we called AddTrack (AND, for
                // now, we have an explicit end), then don't use
                // the duration that came in through TrackDurationSet().
                // Rather, use the newer value of m_ulDuration that
                // is already in the element.
                if (pThisElement->m_ulDuration != pThisElement->m_ulDurationInAddTrack &&
                    pThisElement->m_bHasExplicitEnd)
                {
                    // Use the element duration instead
                    pPlayToAssoc->m_ulDuration = pThisElement->m_ulDuration;
                // /NOTE: PR 63622 content's Superman logo plays from 5s to 62s as it
                // should when it performs the above even though
                // pThisElement->getPureDuration() != pPlayToAssoc->m_ulDuration.
                // Could be a hide-site thing, i.e., if you assign above to
                // pure dur instead, hide (and not media end) could be all
                // that's happening early.
                }
                else
                {
                    // Do what we've always done
                    pPlayToAssoc->m_ulDuration =
                        ulDurToUseForGroup > ulDelay ? ulDurToUseForGroup - ulDelay : 0;

                HX_ASSERT(!pThisElement->m_bDurationIncludesDelayBeyondSyncbase);
                }
            // /But wait!  If track has a delay that is greater than
            // the parent delay, we want to use that difference added
            // to the duration:
            if (ulDelayBeyondSyncBase > 0)
            {
                // /XXXEH- I need to figure out if there is ever a case
                // where the element is delayed beyond its parent and
                // has a begin offset that is *not* equal to this delay:
                HX_ASSERT(pThisElement->m_bBeginOffsetSet?
                      (INT32)ulDelayBeyondSyncBase ==
                      pThisElement->m_lBeginOffset: 1);

                pThisElement->m_bCurBeginIsOffsetFromSyncBase = TRUE;
                pThisElement->m_ulBeginOffsetFromSyncBase =
                      ulDelayBeyondSyncBase;

                if ((UINT32)-1 != pThisElement->m_ulDuration  &&
                      pThisElement->m_ulDuration > pPlayToAssoc->m_ulDuration)
                {
                  pThisElement->m_bDurationIncludesDelayBeyondSyncbase=TRUE;
                  // /Weird: I'm getting here even though if() checks for this:
                  HX_ASSERT((UINT32)-1 != pThisElement->m_ulDuration);
                  HX_ASSERT(ulDelayBeyondSyncBase ==
                        pThisElement->m_ulDuration -
                        pPlayToAssoc->m_ulDuration  &&  "ehodge");
                }

                // /NOTE: I got rid of logic here that "fixed"
                // SMIL 2.0 Interop Timing #18.7.  The right place for
                // that logic is in the subsequent call to
                // CSmilTimelineElement::setDuration(), where
                // logic has existed for a while to deal with the case
                // where the duration doesn't yet include the delay.  In
                // *this* function we're in now, we treat every begin the
                // same: the duration passed to durationResolved now
                // *never* includes the delay.
            }

            // /If an external marker file is the "track", here, then
            // it's not really a valid track (does not partake in the
            // timeline) so it doesn't have an associated CSmilElement
            // and should not cause the timeline to be extended if its
            // duration is longer than the others in its group:
            if (!pThisElement)
            {
                // /Make it 1 millisecond:
                pPlayToAssoc->m_ulDuration = 1;
            }

            if(bLiveSource &&
                pPlayToAssoc->m_ulDuration == 0)
            {
                // don't resolve duration
            }
            else
            {
                if (bAdjustedDurForPartialPlayFactor)
                {
                  // /XXXEH- This needs to be looked at, since all other
                  // cases (post-PR-79699 fix) use pure, delay-free
                  // durations for resetTimelineElementDuration():
                  UINT32 ulNewDuration = pPlayToAssoc->m_ulDuration +
                        // /Ultimately, handleSourceUpdate() gets
                        // called which expects the delay (beyond
                        // the parent begin, not the total delay) to
                        // be built-in to the duration:
                        ulDelayBeyondSyncBase;
                  HX_ASSERT(ulNewDuration == pThisElement->getPureDuration());

                  UINT32 ulPriorPureDuration = pThisElement->getPureDuration();
                  if (ulNewDuration == ulPriorPureDuration)
                  {   // /Don't send same value as prior:
                      ulPriorPureDuration++;
                  }
                  m_pSmilParser->resetTimelineElementDuration(
                        pPlayToAssoc->m_id, ulNewDuration,
                        ulPriorPureDuration);
                }
                HX_ASSERT(pThisElement->m_ulDuration == pThisElement->getPureDuration());
                m_pSmilParser->durationResolved(pPlayToAssoc->m_id,
                      pPlayToAssoc->m_ulDuration);
            }
            handleElements();
          }
      }

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)
        // Check to see if this element has any children
        // which extend past the end of this track.
        checkAnimChildren(pThisElement, ulDuration);
#endif /* #if defined(HELIX_FEATURE_SMIL2_ANIMATION) */
      
      SMILGroupInfo* pGroupInfo = NULL;
      if(m_pGroupInfoMap->Lookup(ulGroupIndex, (void*&)pGroupInfo) &&
         !pGroupInfo->m_bDurationSet)
      {
          if (ulDurToUseForGroup >
                // /Changing this from pGroupInfo->m_ulDuration to
                // m_ulCurGroupDuration fixes PR 56686:
                m_ulCurGroupDuration) // /was:  pGroupInfo->m_ulDuration)
          {
            // duration is the end of group
            pGroupInfo->m_ulDuration = ulDurToUseForGroup;
          }

          if (pGroupInfo->m_ulDuration < m_ulCurGroupDuration)
          {
            pGroupInfo->m_ulDuration = m_ulCurGroupDuration;
          }

          // keep tracking the duration set calls so that
          // we can notify the parent persistent renderer upon
          // the resolvement of its duration
          pGroupInfo->m_nTrackDurationsSet++;
          if (pGroupInfo->m_nTrackDurationsSet == pGroupInfo->m_nTracks)
          {
            pGroupInfo->m_bDurationSet = TRUE;
            ignoreLastHideEvent(ulGroupIndex, pGroupInfo);

                // XXXMEH - if duration due to animations is longer than
                // the group duration, then we need to up the duration
                if (m_ulAnimDuration > pGroupInfo->m_ulDuration)
                {
                    // First update the group duration
                    pGroupInfo->m_ulDuration = m_ulAnimDuration;
                    // Now update the "persistent layout stream duration"
                    if (m_pPersistentLayoutStream)
                    {
                        IHXValues* pStreamProps = NULL;
                        m_pPersistentLayoutStream->GetProperties(pStreamProps);
                        if (pStreamProps)
                        {
                            pStreamProps->SetPropertyULONG32("duration", m_ulAnimDuration);
                            m_pPersistentLayoutStream->SetProperties(pStreamProps);
                        }
                        HX_RELEASE(pStreamProps);
                    }
                }

            PersistentDurationSet(pGroupInfo->m_ulDuration,
                              m_pSmilParser->m_ulPersistentComponentDelay,
                              bLiveSource);
          }
      }
      retVal = HXR_OK;
      goto cleanup;

    }
    else // /Failed: no pPlayToAssoc
    {
      retVal = HXR_FAILED;
    }

cleanup:
    m_bInTrackDurationSetCall = FALSE;

    return retVal;
}

HX_RESULT CSmilDocumentRenderer::ignoreLastHideEvent(UINT32         ulGroupIndex,
                                                    SMILGroupInfo* pGroupInfo)
{
    HX_RESULT retVal = HXR_OK;

#if 1
    if (pGroupInfo &&
        pGroupInfo->m_bDurationSet &&
        m_pEventList)
    {
        LISTPOSITION pos = m_pEventList->GetHeadPosition();
        while (pos)
        {
            CSmilLayoutEvent* pEvent =
                (CSmilLayoutEvent*) m_pEventList->GetNext(pos);
            if (pEvent &&
                pEvent->m_type == CSmilLayoutEvent::eHideSite)
            {
                CSmilShowSiteEvent* pHideEvent = (CSmilShowSiteEvent*) pEvent;
                // Does this media have fill="remove"?
                FillType eFill = getMediaFillBehavior((const char*) pHideEvent->getMediaID());
                // If our remove event is scheduled for the group
                // duration and we don't have fill="remove" behavior,
                // then set the flag to ignore the hide event.
                if (pEvent->m_uGroupIndex == (UINT16) ulGroupIndex &&
                    pEvent->m_ulEventTime >= pGroupInfo->m_ulDuration &&
                    eFill                 != FillRemove)
                {
                    pEvent->m_bIgnorEvent = TRUE;
                }
            }
        }
    }
#else
    if (pGroupInfo->m_bDurationSet && m_pSiteInfoList)
    {
        // walk through the site info list, finding sites that end
        // at the track duration, and remove their last hide event.
        CHXSimpleList::Iterator i = m_pSiteInfoList->Begin();
        for (;i != m_pSiteInfoList->End(); ++i)
        {
            SMILSiteInfo* pSiteInfo = (SMILSiteInfo*)(*i);
            if (pSiteInfo->m_uGroupIndex == ulGroupIndex &&
                pSiteInfo->m_ulDuration == pGroupInfo->m_ulDuration)
            {
                // this site ends at the end of this group...
                // remove its last hide event...
                LISTPOSITION pos = m_pEventList->GetHeadPosition();
                while (pos)
                {
                    CSmilLayoutEvent* pEvent = (CSmilLayoutEvent*)
                        m_pEventList->GetAt(pos);
                    if (pEvent &&
                        pEvent->m_type == CSmilLayoutEvent::eHideSite)
                    {
                        // Cast to show site event
                        CSmilShowSiteEvent* pHideEvent = (CSmilShowSiteEvent*) pEvent;
                        // Get the fill behavior for the media
                        FillType eFill = getMediaFillBehavior((const char*) pHideEvent->getMediaID());
                        // Decide if we need to supress the hide
                        if (pEvent->m_uGroupIndex == ulGroupIndex &&
                            pEvent->m_ulEventTime == pSiteInfo->m_ulDuration &&
                            eFill                 != FillRemove)
                        {
                            pEvent->m_bIgnorEvent = TRUE;
                            break;
                        }
                    }
                    m_pEventList->GetNext(pos);
                }
            }
        }
    }
#endif

    return retVal;
}

STDMETHODIMP
CSmilDocumentRenderer::RepeatedTrackDurationSet(const char*  pID,
                                    UINT32 ulDuration,                              
                                    BOOL   bIsLive)
{
    MLOG_SRC(m_pErrorMessages,
             "RepeatedTrackDurationSet(%s,%lu,%lu) m_ulCurrentTime=%lu\n",
             pID, ulDuration, bIsLive, m_ulCurrentTime);
    if(!bIsLive)
    {
      m_pSmilParser->durationResolved(pID,
                              ulDuration);

      handleElements();
    }

    return HXR_OK;
}

STDMETHODIMP
CSmilDocumentRenderer::TrackUpdated(UINT32 ulGroupIndex,
                            UINT32 ulTrackIndex,
                            IHXValues* pValues)
{
    HX_RESULT     rc = HXR_OK;
    UINT16  uNewTrackIndex = 0;
    UINT32  ulNewTrackIndex = 0;

    SMILPlayToAssoc* pPlayToAssoc = getPlayToAssoc((UINT16)ulGroupIndex,
                                       (UINT16)ulTrackIndex);

    if (!pPlayToAssoc)
    {
      rc = HXR_UNEXPECTED;
      goto cleanup;
    }

    // /Don't cast a UINT16& to a UINT32& here otherwise Big-endian
    // machines will not receive the right 2 bytes (part of fix for
    // PR 121880; rest of fix is in client/core):
    if (HXR_OK == pValues->GetPropertyULONG32("TrackIndex", ulNewTrackIndex))
    {
        pPlayToAssoc->m_uTrackIndex = (UINT16)ulNewTrackIndex;
    }

cleanup:

    return rc;
}
  
STDMETHODIMP CSmilDocumentRenderer::AddShowEvents(const char* pRegionName,
                                                  IHXSite*   pSite)
{
    HX_RESULT rc = HXR_OK;

    SMILPlayToAssoc* pPlayToAssoc = getPlayToAssoc(pRegionName);
    if(pPlayToAssoc)
    {
      showSite(pSite, FALSE);
      
      UINT32 ulShowTime = pPlayToAssoc->m_ulDelay;

      UINT32 ulRemoveTime = pPlayToAssoc->m_ulDuration + 
          pPlayToAssoc->m_ulDelay;
      if (ulShowTime != ulRemoveTime)
      {
            // show site after m_ulDelay
            CSmilShowSiteEvent* pShowEvent =
                new CSmilShowSiteEvent(pPlayToAssoc->m_uGroupIndex,
                                       ulShowTime,
                                       pSite,
                                       NULL,
                                       TRUE);
          insertEvent(pShowEvent);
          
#if defined(HELIX_FEATURE_SMIL2_TRANSITIONS)
          if (pPlayToAssoc->m_beginTransition.GetLength() > 0)
          {
            CSmilTransitionEvent* pTransEvent =
                new CSmilTransitionEvent(pPlayToAssoc->m_ulDelay,
                pPlayToAssoc, pSite, TRUE, this);
            insertEvent(pTransEvent);
          }
#endif /* #if defined(HELIX_FEATURE_SMIL2_TRANSITIONS) */

          // hide site after m_ulDuration if it isn't live
          if(pPlayToAssoc->m_bRemoveSite &&
             !pPlayToAssoc->m_bLiveSource)
          {
                CSmilShowSiteEvent* pHideEvent =
                    new CSmilShowSiteEvent(pPlayToAssoc->m_uGroupIndex,
                                           ulRemoveTime,
                                           pSite,
                                           NULL,
                                           FALSE);
            insertEvent(pHideEvent);
                // Set the event time into the element
                setElementRemoveTime(pPlayToAssoc->m_id, ulRemoveTime);
          }

#if defined(HELIX_FEATURE_SMIL2_TRANSITIONS)
          // schedule an end transition if there is one.
          if (pPlayToAssoc->m_endTransition.GetLength() > 0 &&
            !pPlayToAssoc->m_bLiveSource)
          {
            CSmilTransitionInfo* pTInfo = getTransition(
                pPlayToAssoc->m_endTransition);
            if (pTInfo)
            {
                CSmilTransitionEvent* pTransEvent =
                  new CSmilTransitionEvent(
                  pPlayToAssoc->m_ulDelay + pPlayToAssoc->m_ulDuration - pTInfo->m_pTrans->m_ulDuration,
                  pPlayToAssoc, pSite, FALSE, this);
                insertEvent(pTransEvent);
            }
          }
#endif /* #if defined(HELIX_FEATURE_SMIL2_TRANSITIONS) */
      }
    }

    return rc;
}

STDMETHODIMP CSmilDocumentRenderer::SiteUserAttachSite(CSmilSiteUser* pUser, IHXSite* pSite)
{
    HX_RESULT retVal = HXR_OK;

    if (pUser && pSite)
    {
        if (m_pRootLayout &&
            m_pRootLayout->m_pSiteUser == pUser)
        {
            MLOG_LAYOUT(m_pErrorMessages,
                        "root-layout AttachSite() m_ulCurrentTime=%lu tick=%lu\n",
                        m_ulCurrentTime, HX_GET_BETTERTICKCOUNT());
            // Save the top-level site
            HX_RELEASE(m_pRootLayout->m_pSite);
            m_pRootLayout->m_pSite = pSite;
            m_pRootLayout->m_pSite->AddRef();
            // Create a passive site watcher object for
            // the top-level site
            HX_DELETE(m_pRootLayout->m_pPassiveSiteWatcher);
            m_pRootLayout->m_pPassiveSiteWatcher =
                new CSmilPassiveSiteWatcher((CSmilPassiveSiteWatcherResponse*) this,
                                            m_pRootLayout->m_pSite);
            if (m_pRootLayout->m_pPassiveSiteWatcher)
            {
                m_pRootLayout->m_pPassiveSiteWatcher->AddRef();
            }
            // Set the size of the top-level site
            setTopLevelSiteSize();
            // Clear the flag saying we've detached the root-layout site
            m_bRootLayoutSiteDetached = FALSE;
        }
#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)
        else
        {
            // Check and see if this is a viewport
            CSmilBasicViewport* pPort = getViewportBySiteUser(pUser);
            if (pPort)
            {
                // Save the site
                HX_RELEASE(pPort->m_pSite);
                pPort->m_pSite = pSite;
                pPort->m_pSite->AddRef();
                // Set the position
                HXxPoint cPos = {0, 0};
                pPort->m_pSite->SetPosition(cPos);
                // Set the size
                HXxSize cSize = {HXxRECT_WIDTH(pPort->m_Rect),
                                 HXxRECT_HEIGHT(pPort->m_Rect)};
#if defined(XXXMEH_DO_VIEWPORT_TLC) && defined(_WINDOWS)
                cSize.cx += GetSystemMetrics(SM_CXFIXEDFRAME) * 2;
                cSize.cy += GetSystemMetrics(SM_CYFIXEDFRAME) * 2 + 
                            GetSystemMetrics(SM_CYCAPTION);
#endif
                pPort->m_pSite->SetSize(cSize);
                // Create a passive site watcher object for
                // the top-level site
                HX_DELETE(pPort->m_pPassiveSiteWatcher);
                pPort->m_pPassiveSiteWatcher =
                    new CSmilPassiveSiteWatcher((CSmilPassiveSiteWatcherResponse*) this,
                                                pPort->m_pSite);
                if (pPort->m_pPassiveSiteWatcher)
                {
                    pPort->m_pPassiveSiteWatcher->AddRef();
                }
                // Now we need to finish the setup for this viewport
                retVal = finishOneViewportSetup(pPort);
            }
        }
#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

STDMETHODIMP CSmilDocumentRenderer::SiteUserDetachSite(CSmilSiteUser* pUser)
{
    HX_RESULT retVal = HXR_OK;

    BOOL bNeedToRelease = FALSE;
    if (pUser)
    {
        // Determine if this is a root
        if (m_pRootLayout &&
            m_pRootLayout->m_pSiteUser == pUser)
        {
            // Destroy the renderer sites of those renderers playing
            // to regions which are under the root-layout. This also
            // deletes the corresponding site watchers as well.
            destroyRendererSites(m_pRootLayout);
            // Properly destroy the parent-child relationships - 
            // this makes sure that the proper parent calls
            // DestroyChild(), but it does not delete the CSmilBasicRegion
            // itself, since the region map owns that.
            destroyRegionSites(m_pRootLayout->m_pChildList);

          // keep the region info around so that all layout/region/renderer
          // sites can be re-created during nested meta playlist navigation
          if (m_bCloseCalled ||
                (m_pParent && !m_pParent->m_bUseNestedMeta))
          {
            // Now destroy the CSmilBasicRegion's which
            // are under the root-layout
            destroyRegions(m_pRootLayout->m_pChildList);
          }

            // Close the root layout's passive site watcher. This
            // causes it to release its ref on us.
            if (m_pRootLayout->m_pPassiveSiteWatcher)
            {
                m_pRootLayout->m_pPassiveSiteWatcher->Close();
            }
            // Remove the site user as a layout group
            if (m_pRootLayout->m_pSiteUser && m_pParent)
            {
                IUnknown* pThisUnk = NULL;
                m_pRootLayout->m_pSiteUser->QueryInterface(IID_IUnknown, (void**) &pThisUnk);
                if (pThisUnk)
                {
                    m_pParent->HandleRemoveLayoutSiteGroup(pThisUnk);
                }
                HX_RELEASE(pThisUnk);
            }
            // Close the root layout's site user. This causes it
            // to release its ref on us. So before we do that,
            // we AddRef() ourselves and we will release ourselves
            // directly below.
            AddRef();
            bNeedToRelease = TRUE;
            // Close the root-layout's site user
            if (m_pRootLayout->m_pSiteUser)
            {
                m_pRootLayout->m_pSiteUser->Close();
            }
            // Release the top-level site
            HX_RELEASE(m_pRootLayout->m_pSite);
            // Delete the root-layout ONLY if close
            // has been called. If close has been called, then
            // this is the last line of defense.
            if (m_bCloseCalled)
            {
                HX_DELETE(m_pRootLayout);
            }
            // Set the flag saying we've detached the root-layout site
            m_bRootLayoutSiteDetached = TRUE;
#ifdef _MACINTOSH
            if (m_bResetCursor)
            {
                m_bResetCursor = FALSE;
                ::InitCursor();
            }
#endif
        }
#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)
        else
        {
            // Is this a DetachSite() for one of the viewports?
            CSmilBasicViewport* pPort = getViewportBySiteUser(pUser);
            if (pPort)
            {
                // Destroy the renderer sites of those renderers playing
                // to regions which are under this viewport. This also
                // deletes the corresponding site watchers as well.
                destroyRendererSites(pPort);
                // Properly destroy the parent-child relationships - 
                // this makes sure that the proper parent calls
                // DestroyChild(), but it does not delete the CSmilBasicRegion
                // itself, since the region map owns that.
                destroyRegionSites(pPort->m_pChildList);
                // Now destroy the CSmilBasicRegion's which
                // are under this viewport
                destroyRegions(pPort->m_pChildList);
                // Close this viewport's passive site watcher. This
                // causes it to release its ref on us.
                if (pPort->m_pPassiveSiteWatcher)
                {
                    pPort->m_pPassiveSiteWatcher->Close();
                }
                // Close this viewport's site user
                if (pPort->m_pSiteUser)
                {
                    pPort->m_pSiteUser->Close();
                }
                // Release this viewport's site
                HX_RELEASE(pPort->m_pSite);
            }
        }
#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */
    }
    else
    {
        retVal = HXR_FAIL;
    }

    m_bSitesDetached = TRUE;

    if (bNeedToRelease)
    {
        Release();
    }
    
    return retVal;
}

STDMETHODIMP CSmilDocumentRenderer::SiteUserHandleEvent(CSmilSiteUser* pUser,
                                                        HXxEvent*      pEvent)
{
    HX_RESULT retVal = HXR_OK;

    if (pUser && pEvent)
    {
        switch (pEvent->event)
        {
            case HX_SET_FOCUS:
                {
                    pEvent->handled = TRUE;
                }
                break;

            case HX_LOSE_FOCUS:
                {
                    pEvent->handled = TRUE;
                }
                break;

            case HX_CHAR:
                {
                    UINT16 uCharPressed = (UINT16) (UINT32)pEvent->param1;
                    // XXXEH: TODO: Handle this: "The character is a single
                    // character from [[ISO10646]]."
                    retVal = HandleCharEvent(uCharPressed);
                    if (SUCCEEDED(retVal))
                    {
                        // XXXMEH - we will no longer claim that we handle
                        // keystrokes. This will allow renderers such as 
                        // flash to get the keystrokes for forms, etc.
                        pEvent->handled = FALSE;
                    }
                }
                break;

#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)
#ifdef XXXMEH_DO_VIEWPORT_TLC
            case HX_DETACH_WINDOW:
                {
                    // This message could be the result of either: a) the
                    // user closing the viewport window; or b) the SMIL renderer
                    // shutting down. If this is the result of the SMIL renderer
                    // shutting down, then the SMIL renderer has ALREADY called
                    // IHXViewPortManager::CloseViewPort(), so we should not call
                    // it again. However, if this is the result of the user
                    // closing the viewport window, then we need to call 
                    // CloseViewPort.
                    if (!m_bSMILViewportClose)
                    {
                        // Are we a viewport?
                        CSmilBasicViewport* pPort = getViewportBySiteUser(pUser);
                        if (pPort && m_pViewPortManager)
                        {
                            // We are getting a viewport close, so close the viewport
                            retVal = m_pViewPortManager->CloseViewPort(pPort->m_id);
                            if (SUCCEEDED(retVal))
                            {
                                pEvent->handled = TRUE;
                            }
                        }
                    }
                }
                break;
#if defined(_WINDOWS)
            case WM_SETFOCUS:
                {
                    // Are we a viewport?
                    CSmilBasicViewport* pPort = getViewportBySiteUser(pUser);
                    if (pPort && m_pViewPortManager)
                    {
                        // Get the viewport
                        IHXViewPort* pVP = NULL;
                        retVal = m_pViewPortManager->GetViewPort(pPort->m_id, pVP);
                        if (SUCCEEDED(retVal))
                        {
                            retVal = pVP->SetFocus();
                        }
                        HX_RELEASE(pVP);
                    }
                }
                break;
            case WM_SIZING:
                {
                    HXxRect cRect    = {0, 0, 0, 0};
                    LPRECT  pWinRect = (LPRECT) pEvent->param2;
                    if (pWinRect)
                    {
                        cRect.left   = pWinRect->left;
                        cRect.top    = pWinRect->top;
                        cRect.right  = pWinRect->right;
                        cRect.bottom = pWinRect->bottom;
                    }
                    // Are we a viewport?
                    CSmilBasicViewport* pPort = getViewportBySiteUser(pUser);
                    if (pPort && pPort->m_pSite)
                    {
                        HXxSize cOldSize = {0, 0};
                        pPort->m_pSite->GetSize(cOldSize);
                        HXxSize cSize = {HXxRECT_WIDTH(cRect), HXxRECT_HEIGHT(cRect)};
                        if (cSize.cx > 0 && cSize.cy > 0 &&
                            (cSize.cx != cOldSize.cx ||
                             cSize.cy != cOldSize.cy))
                        {
                            pPort->m_pSite->SetSize(cSize);
                        }
                    }
                }
                break;
#endif // #if defined(_WINDOWS)
#endif // #ifdef XXXMEH_DO_VIEWPORT_TLC
#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */
            default:
                break;
        }
        // Set the result
        if (SUCCEEDED(retVal))
        {
            pEvent->result = retVal;
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}


STDMETHODIMP
CSmilDocumentRenderer::RendererInitialized(IHXRenderer* pRenderer, 
    IUnknown* pStream, IHXValues* pInfo)
{
    HX_RESULT rc = HXR_OK;
    BOOL bIsWindowed = FALSE;
    BOOL bHandleElement = FALSE;

    HX_DISPLAY_TYPE ulFlags;
    IHXBuffer* pBuf = 0;
    if(HXR_OK == pRenderer->GetDisplayType(ulFlags, pBuf))
    {
        if (HX_DISPLAY_WINDOW == (HX_DISPLAY_WINDOW & ulFlags))
      {
          bIsWindowed = TRUE;
      }
      HX_RELEASE(pBuf);
    }

    UINT32 ulGroupIndex      = 0;
    UINT32 ulTrackIndex      = 0;
    UINT32 ulDelay           = 0;
    UINT32 ulDuration        = 0;
    UINT32 ulLiveSource      = 0;

    pInfo->GetPropertyULONG32("GroupIndex", ulGroupIndex);
    pInfo->GetPropertyULONG32("TrackIndex", ulTrackIndex);
    pInfo->GetPropertyULONG32("Delay", ulDelay);
    // "duration" is really the end time in the current group
    pInfo->GetPropertyULONG32("Duration", ulDuration);  
    pInfo->GetPropertyULONG32("LiveSource", ulLiveSource);

    MLOG_SRC(m_pErrorMessages,"RendererInitialized() grp=%lu trk=%lu m_ulCurrentTime=%lu t=%lu\n",
             ulGroupIndex, ulTrackIndex, m_ulCurrentTime, HX_GET_BETTERTICKCOUNT());

    IHXBuffer* pBuffer = NULL;
    SMILPlayToAssoc* pPlayToAssoc = NULL;

    if (HXR_OK == pInfo->GetPropertyCString("id", pBuffer))
    {
      pPlayToAssoc = getPlayToAssocByMedia((const char*)pBuffer->GetBuffer());
    }
    HX_RELEASE(pBuffer);

    if (!pPlayToAssoc)
    {
      // XXX HP we shouldn't be here
      HX_ASSERT(FALSE);
      pPlayToAssoc = getPlayToAssoc((UINT16)ulGroupIndex, (UINT16)ulTrackIndex);
    }

    UINT16 uStreamNumber = 0;
    IHXStream* pStr = 0;
    if(HXR_OK == pStream->QueryInterface(IID_IHXStream,
          (void**)&pStr))
    {
      uStreamNumber = pStr->GetStreamNumber();

        // Here we want to get the URL and save it
        // in the playToAssoc, so that we can look
        // it up later if necessary
        //
        // QI for IHXStreamSource
        IHXStreamSource* pStreamSource = NULL;
        pStr->GetSource(pStreamSource);
        if (pStreamSource)
        {
            if (pPlayToAssoc)
            {
                pPlayToAssoc->m_URL = pStreamSource->GetURL();
                // If we are a external media marker source, then
                // we need to update our info struct with this url
                if (pPlayToAssoc->m_bXMMSource && m_pExternalMediaMarkerList)
                {
                    IHXBuffer* pTB = NULL;
                    pInfo->GetPropertyCString("url", pTB);
                    if (pTB)
                    {
                        HX_RELEASE(pTB);
                    }
                    LISTPOSITION pos = m_pExternalMediaMarkerList->GetHeadPosition();
                    while (pos)
                    {
                        CExternalMediaMarkerInfo* pMInfo =
                            (CExternalMediaMarkerInfo*) m_pExternalMediaMarkerList->GetNext(pos);
                        if (pMInfo &&
                            pMInfo->m_usGroupIndex == (UINT16) ulGroupIndex &&
                            pMInfo->m_usTrackIndex == (UINT16) ulTrackIndex)
                        {
                            HX_VECTOR_DELETE(pMInfo->m_pszRendererURL);
                            pMInfo->m_pszRendererURL =
                            new char [pPlayToAssoc->m_URL.GetLength() + 1];
                            if (pMInfo->m_pszRendererURL)
                            {
                                strcpy(pMInfo->m_pszRendererURL, pPlayToAssoc->m_URL); /* Flawfinder: ignore */
                                break;
                            }
                        }
                    }
                }
            }
        }
        HX_RELEASE(pStreamSource);

      HX_RELEASE(pStr);
    }

    // /Get SMIL layout stream:
    // XXX HP the following if statement will be obsolete after using 
    //            nested meta logic since m_pPersistentLayoutStream should
    //            be set in ::InitPersistent()
    if (!m_pParent->m_bUseNestedMeta && !m_pPersistentLayoutStream)
    {
      if (HXR_OK != pStream->QueryInterface(IID_IHXLayoutStream, 
            (void**)&m_pPersistentLayoutStream))
      {
          HX_ASSERT(m_pPersistentLayoutStream);
      }
    }

    CSmilBasicRegion* pRegion = NULL;
    if(bIsWindowed)
    {
        // This is the box at which we need to
        // re-compute the z-order stack
        CSmilBasicBox* pZOrderBox = NULL;
      IHXLayoutStream* pLayoutStream = NULL;
      if(HXR_OK == pStream->QueryInterface(IID_IHXLayoutStream,
                (void**)&pLayoutStream))
      {
          BOOL bNoRegion = TRUE;

          if(pPlayToAssoc)
          {
            CHXSimpleList*    pRendererList = NULL;
            SMILSourceInfo* pSourceInfo = NULL;
            const char* pPlayTo = pPlayToAssoc->m_playTo;

            // re-create layout/region/renderer sites if
            // sites have been detached upon playlist navigation
            if (m_bSitesDetached)
            {
                m_bSitesDetached = FALSE;
                rc = setupRootLayout(FALSE);
                HX_ASSERT(HXR_OK == rc);
            }

            // See if we need to layout this renderer
            //
            // First check to see if we have a region by id
            pRegion = getRegionByID(pPlayTo);
            if(pRegion)
            {
                bNoRegion = FALSE;
                // We will need to recompute the z-order
                // stack of this region's parent
                pZOrderBox = getTopLevelBox(pRegion);
            }
            else
            {
                // We didn't find it by id, so try finding
                // a region by regionName
                pRegion = getFirstRegionByName(pPlayTo);
                if (pRegion)
                {
                  bNoRegion = FALSE;
                  // We will need to recompute the z-order
                  // stack of this region's parent
                  pZOrderBox = getTopLevelBox(pRegion);
                }
                else
                {
                  // We didn't find a <region> by id and we 
                  // didn't find a <region> by the "regionName"
                  // indirection, so we are going to have to 
                  // fall back to the "default" region.
                  pRegion = setupDefaultLayout();
                  if (pRegion)
                  {
                      // Clear the flag
                      bNoRegion = FALSE;
                      // Change the parameters
                      pPlayToAssoc->m_playTo     = pRegion->m_region;
                      pPlayToAssoc->m_regionName = pRegion->m_region;
                      pPlayTo                    = (const char*) pRegion->m_region;
                      // We will need to recompute the z-order
                      // stack of this region's parent
                      pZOrderBox = getTopLevelBox(pRegion);
                  }
                }
            }

                // /vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
                // /XXXEH- To do: this is same code as found inside
                // AttachElementLayout(), below.  Combine into single function:
            pSourceInfo = new SMILSourceInfo;
            pSourceInfo->m_pStream = pStream;
            if(pSourceInfo->m_pStream)
            {
                pSourceInfo->m_pStream->AddRef();
            }
            pSourceInfo->m_pRenderer = pRenderer;
            if(pSourceInfo->m_pRenderer)
            {
                pSourceInfo->m_pRenderer->AddRef();
            }
            
            pSourceInfo->m_ulDelay = ulDelay;
            pSourceInfo->m_ulDuration = ulDuration - ulDelay;

            char cTemp[20]; /* Flawfinder: ignore */
            ::sprintf(cTemp,"%#010lx",(ULONG32)(void*)pSourceInfo); /* Flawfinder: ignore */
            pSourceInfo->m_tunerName   = (const char*) cTemp;
            ::sprintf(cTemp,"%#010lx",(ULONG32)(void*)pSourceInfo+1); /* Flawfinder: ignore */
            pSourceInfo->m_childTunerName = (const char*)cTemp;

            const char* pChildTuner = pSourceInfo->m_childTunerName;

            // get to the site manager and set an event hook
            IHXEventHookMgr* pHookMgr = NULL;
            if(HXR_OK ==
                m_pSiteMgr->QueryInterface(IID_IHXEventHookMgr, 
                                  (void**)&pHookMgr))
            {
                    CSmilEventHook* pChildEventHook = NULL;

                    // create event hook for playto
                    pChildEventHook = new CSmilEventHook((CSmilEventHookResponse*) this,
                                                         pPlayTo, pChildTuner, bNoRegion,
                                                         (const char*) pPlayToAssoc->m_id);
                    pChildEventHook->AddRef();

                    pHookMgr->AddHook(pChildEventHook, pChildTuner, 0);

                    pSourceInfo->m_pRendererEventHook = pChildEventHook;

                    pHookMgr->Release();
            }
            else
            {
                pSourceInfo->m_pRendererEventHook = NULL;
            }

            if (NULL == pPlayToAssoc->m_sourceMap[uStreamNumber])
            {
                pPlayToAssoc->m_sourceMap[uStreamNumber] = new CHXSimpleList();

                pPlayToAssoc->m_tunerName = pSourceInfo->m_tunerName;
                pPlayToAssoc->m_childTunerName = pSourceInfo->m_childTunerName;
                pPlayToAssoc->m_ulDelay = pSourceInfo->m_ulDelay;
                pPlayToAssoc->m_ulDuration = pSourceInfo->m_ulDuration;
                pPlayToAssoc->m_bLiveSource = ulLiveSource ? TRUE : FALSE;
                pPlayToAssoc->m_pRendererEventHook = pSourceInfo->m_pRendererEventHook;

                // add hyperlinks
                CSmilElement* pElement = m_pSmilParser->findElement(
                  pPlayToAssoc->m_id);
                if(pElement && pElement->m_pHyperlinks)
                {
                  CHXSimpleList::Iterator i = 
                        pElement->m_pHyperlinks->Begin();
                  for(; i != pElement->m_pHyperlinks->End(); ++i)
                  {
                      CSmilAAnchorElement* pAnchor = 
                        (CSmilAAnchorElement*)(*i);
                      //[SMIL 1.0 Compliance] Fixes PR 26473:
                      // We want to add in LIFO fashion so inner
                      // (nested) anchors will be found first in
                      // CSmilDocumentRenderer::findHyperlinkElement(),
                      // below.  In other words, we're giving an
                      // effective higher link "z-order" to the
                      // decendants of other links.  This used to
                      // call AddTail():
                      pPlayToAssoc->m_pHyperlinks->AddHead(pAnchor);
                  }
                }

                bHandleElement = TRUE;
            }

            pRendererList = (CHXSimpleList*) pPlayToAssoc->m_sourceMap[uStreamNumber];
            pRendererList->AddTail(pSourceInfo);

            IHXValues* pValues = 0;
            IHXBuffer* pPlayToBuffer = 0;
            IHXBuffer* pRegionName = 0;
            IHXCommonClassFactory* pFactory = m_pParent->getFactory();
            if ((HXR_OK == pFactory->CreateInstance(CLSID_IHXValues, (void**)&pValues)) &&
                (HXR_OK == pFactory->CreateInstance(CLSID_IHXBuffer, (void**)&pPlayToBuffer)) &&
                (HXR_OK == pFactory->CreateInstance(CLSID_IHXBuffer, (void**)&pRegionName)))
            {               
                pPlayToBuffer->Set((BYTE*)pChildTuner, strlen(pChildTuner)+1);
                pValues->SetPropertyCString("playto", pPlayToBuffer);
                HX_RELEASE(pPlayToBuffer);

                if(pPlayToAssoc->m_regionName.GetLength() > 0)
                {
                  const char* pName = pPlayToAssoc->m_regionName;
                  pRegionName->Set((BYTE*)pName, strlen(pName)+1);
                  pValues->SetPropertyCString("region", pRegionName);
                }
                HX_RELEASE(pRegionName);

                pLayoutStream->SetProperties(pValues);
                pValues->Release();
            }

            if(!pRegion->m_bImplicitRegion)
            {
                addSiteForRenderer(pPlayToAssoc, pSourceInfo, pRenderer, bNoRegion);
            }
                // /XXXEH- end of "To do: this is same code as...".
                // /^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
          }
      }
        // XXXMEH - now we re-adjust the z-order.
        if (pZOrderBox)
        {
            resolveZOrder(pZOrderBox, m_ulCurrentTime);
        }

      HX_RELEASE(pLayoutStream);
    }
    else    // non-windowed renderer
    {
      if(pPlayToAssoc)
      {
          SMILSourceInfo* pSourceInfo = NULL;
          CHXSimpleList* pRendererList = NULL;

          if (NULL == pPlayToAssoc->m_sourceMap[uStreamNumber])
          {
            pPlayToAssoc->m_sourceMap[uStreamNumber] = new CHXSimpleList();
            pPlayToAssoc->m_ulDuration = ulDuration - ulDelay;

            bHandleElement = TRUE;
          }
          
          pSourceInfo = new SMILSourceInfo;

          pSourceInfo->m_pStream = pStream;
          if(pSourceInfo->m_pStream)
          {
            pSourceInfo->m_pStream->AddRef();
          }
          pSourceInfo->m_pRenderer = pRenderer;
          if(pSourceInfo->m_pRenderer)
          {
            pSourceInfo->m_pRenderer->AddRef();
          }

          pSourceInfo->m_pRendererEventHook = NULL;
          pSourceInfo->m_ulDelay = ulDelay;

          // /Holy ancient typo, batman!!  This was "p->m = ulDur = ulDelay"
          // but it meant to say "p->m = ulDur - [not '='!] ulDelay".  Doh!.
          // Ran across this while fixing PR 79699 and this change is needed
          // for PR 81715 crash to happen (otherwise 81715 freezes player): 
          pSourceInfo->m_ulDuration = ulDuration - ulDelay;
          
          pRendererList = (CHXSimpleList*) pPlayToAssoc->m_sourceMap[uStreamNumber];
          pRendererList->AddTail(pSourceInfo);
      }
    }

    // We need to set the following parameters into the renderer:
    //
    // SMIL default namespace
    // rn:backgroundOpacity
    // rn:mediaOpacity
    // rn:chromaKey
    // rn:chromaKeyTolerance
    // rn:chromaKeyOpacity
    // any <param> children properties
    //
    if (pPlayToAssoc)
    {
        // QI the renderer for IHXValues
        IHXValues* pRendValues = NULL;
        pRenderer->QueryInterface(IID_IHXValues, (void**) &pRendValues);
        if (pRendValues)
        {
            // Set the default namespace
            if (m_pSMILDefaultNamespaceStr)
            {
                pRendValues->SetPropertyCString("SMILDefaultNamespace",
                                                m_pSMILDefaultNamespaceStr);
            }
            // Now, if any of the opacity/chromaKey parameters
            // were specified, then we need to set them into the 
            // renderer as well.
            //
            // First we need to get the source
            CSmilSource* pSource = getSource(pPlayToAssoc->m_id);
            if (pSource)
            {
                if (pSource->m_bBackgroundOpacitySpecified)
                {
                    pRendValues->SetPropertyULONG32("backgroundOpacity",
                                                    pSource->m_ulBackgroundOpacity);
                }
                if (pSource->m_bMediaOpacitySpecified)
                {
                    pRendValues->SetPropertyULONG32("mediaOpacity",
                                                    pSource->m_ulMediaOpacity);
                }
                if (pSource->m_bChromaKeySpecified)
                {
                    pRendValues->SetPropertyULONG32("chromaKey",
                                                    pSource->m_ulChromaKey);
                    pRendValues->SetPropertyULONG32("chromaKeyTolerance",
                                                    pSource->m_ulChromaKeyTolerance);
                    pRendValues->SetPropertyULONG32("chromaKeyOpacity",
                                                    pSource->m_ulChromaKeyOpacity);
                }

            // /To help fix PR 59951 (SMIL part of the fix), we need to
            // notify the renderer what the region width and height is;
            // that allows the renderer to resize its internal window to
            // fill its region if it has no intrinsic width or height:
            ULONG32 ulDisplayAreaWidth = 0;
            ULONG32 ulDisplayAreaHeight = 0;
            if (pSource->m_region.GetLength()>0  &&  pRegion)
            {
                ulDisplayAreaWidth =  // /Region size:
                      pRegion->m_rect.right - pRegion->m_rect.left;
                ulDisplayAreaHeight =  // /Region size:
                      pRegion->m_rect.bottom - pRegion->m_rect.top;
            }
            // /However, if the media element has a "width" attribute
            // set, we should use it instead:
            if (pSource->m_Rect.m_dWidth > 0)
            {
                ulDisplayAreaWidth = pSource->m_Rect.m_dWidth;
            }
            // /Same for if the media element has a "height" attribute
            // set; we should use it instead:
            if (pSource->m_Rect.m_dHeight > 0)
            {
                ulDisplayAreaHeight = pSource->m_Rect.m_dHeight;
            }
            // /Now, notify the renderer what size its display area is:
            if (ulDisplayAreaWidth > 0)
            {
                pRendValues->SetPropertyULONG32("regionWidth",
                      ulDisplayAreaWidth);
            }
            if (ulDisplayAreaHeight > 0)
            {
                pRendValues->SetPropertyULONG32("regionHeight",
                      ulDisplayAreaHeight);
            }

                // Add any param children (if there are any)
                // Note that these will be added as CString properties,
                // rather than ULONG32 properties as the opacity properties
                // Also note that the FALSE arguments below mean that
                // we will pass ALL <param> children to the client, regardless
                // of whether they have rn:delivery="server" or rn:delivery="client"
                if (hasParamChildren(pSource, FALSE))
                {
                    addParamProperties(pSource,
                                       pRendValues,
                                       m_pContext,
                                       FALSE);
                }
            }
        }
        HX_RELEASE(pRendValues);

      IHXPersistentRenderer* pPersistentRenderer = NULL;
      if (HXR_OK == pRenderer->QueryInterface(IID_IHXPersistentRenderer, (void**)&pPersistentRenderer))
      {
          pPlayToAssoc->m_bIsPersistentSource = TRUE;
      }
      HX_RELEASE(pPersistentRenderer);
    }

    // Set up listening for external events coming from this
    // renderer, if there are any
    if (m_pSmilParser && pPlayToAssoc)
    {
        // Are there any external events associated with
        // this renderer?
        if (m_pSmilParser->anyExternalEvents(pPlayToAssoc->m_id))
        {
            // We do have some external events, so first we
            // need to set up the SMIL renderer as an event sink,
            // if it is not already
            if (!m_bEventSinkWasSetup)
            {
                // TRUE adds us, FALSE removes us
                addRemoveEventSink(TRUE);
            }
            // Loop through the external events based off
            // of this renderer, and add filter rules based on
            // each event.
            ExternalEventInfo* pInfo =
                m_pSmilParser->getFirstExternalEvent(pPlayToAssoc->m_id);
            while (pInfo)
            {
                // Add a rule for this external event
                addEventSinkFilterRule(pPlayToAssoc->m_URL, NULL,
                                       pInfo->m_PrefixedEventName);
                // Get the next external event
                pInfo = m_pSmilParser->getNextExternalEvent(pPlayToAssoc->m_id);
            }
        }
    }

    // update the stream timing if it's been called in handleSourceUpdate()
    if(m_pDeferredSourceMap)
    {
      SMILDeferredSourceInfo* pDeferredInfo = NULL;
      const char* pDeferredID = (const char*)pPlayToAssoc->m_id;
      if(m_pDeferredSourceMap->Lookup(pDeferredID,
          (void*&)pDeferredInfo))
      {
          CSmilElement* pThisElement = 
            m_pSmilParser->findElement(pDeferredID);

#if 0     /* Remove: it's causing problems for discrete media updates
           * like PR 69780 & PR 79748 (which worked OK on 20020618 but
           * was broken (elsewhere in the code) after that:
           */
          if(pThisElement &&
            pThisElement->m_bBeginOffsetSet)
          {
            if((INT32)pDeferredInfo->m_ulDuration > pThisElement->m_lBeginOffset)
            {
                updateStreamTiming(pDeferredID, 
#if 0 /*Remove: it's causing problems for non-neg begin cases like PR 79699: */
                  (UINT32)((INT32)pDeferredInfo->m_ulDuration -
                  // /Fixes Timing Interop cases, including 1.33,
                  // prev_begin_in_a_par6.smil, which was playing 2 sec
                  // too long due to -2 sec beginOffset being used here
                  (pThisElement->m_lBeginOffset<0?
                  0:pThisElement->m_lBeginOffset) ) );
#else
                  (UINT32)((INT32)pDeferredInfo->m_ulDuration) );
#endif
            }
            else
            {
                updateStreamTiming(pDeferredID, 0);
            }
          }
          else
#endif
          {
            updateStreamTiming(pDeferredID, pDeferredInfo->m_ulDuration);
          }
      }
    }

    if (pPlayToAssoc)
    {
      const char* pID = (const char*)pPlayToAssoc->m_id;
      {
          CSmilElement* pThisElement = m_pSmilParser->findElement(pID);
          if (pThisElement)
          {
            // /make sure we don't reset the stream's duration or delay
            // if the renderer isn't init'd yet (PR 62688-case2) & others:
            // /DOH!! Somehow, I checked the following without "= TRUE",
            // causing PR 59851 to fail.  Fixes PR 59851 once again:
            pThisElement->m_bRendererInitialized = TRUE;
          }
      }
    }

    // Add this renderer to the media ID -> renderer map
    if (pPlayToAssoc && pRenderer)
    {
        addRendererToMap((const char*) pPlayToAssoc->m_id, pRenderer);
    }

    // If this renderer specifies an event handler, OR this renderer IS
    // an event handler for another renderer, then setup the event pipe
    if (pPlayToAssoc)
    {
        // Get this media's id
        const char* pszID = (const char*) pPlayToAssoc->m_id;
        // Look up the element
        CSmilElement* pEl = m_pSmilParser->findElement(pszID);
        if (pEl && pEl->m_pNode)
        {
            // Cast to a source
            CSmilSource* pSrc = (CSmilSource*) pEl;
            // Does this media specify a handler or is
            // this media a handler?
            if (pSrc->m_HandlerID.GetLength() > 0)
            {
                // This media specifies a handler
                setupEventPipe((const char*) pEl->m_pNode->m_id,
                               (const char*) pSrc->m_HandlerID,
                               NULL);
            }
            else if (pSrc->m_HandlerFor.GetLength() > 0)
            {
                // This media IS a handler
                setupEventPipe((const char*) pSrc->m_HandlerFor,
                               (const char*) pEl->m_pNode->m_id,
                               NULL);
            }
        }
    }

    if (bHandleElement)
    {
      handleElements();
    }

    return HXR_OK;
}

#if defined(_UNIX)  &&  (!(defined(_BEOS)))  &&  defined(USE_XWINDOWS)
XData*  CSmilDocumentRenderer::InitXVisualSupport(IHXSite* pSite, HXxWindow* pWnd)
{
    // create new XData object
    XData* pxData = new XData();

    // use the pointer to the X display and window
    // conveniently given us by CHXSiteWindowed::HandleEvent
    m_pPixmapDisplay = (Display*) pWnd->display;
    Window window = (Window) pWnd->window; 

    // save ptr to display in XData object also...
    pxData->m_Display = m_pPixmapDisplay;

    // get visual & set m_depth
    XWindowAttributes attr;
    XLockDisplay(m_pPixmapDisplay);
    XGetWindowAttributes(m_pPixmapDisplay, window, &attr);
    XUnlockDisplay(m_pPixmapDisplay);

    // get visual info & depth 
    XVisualInfo visInfoMask;
    memset(&visInfoMask, 0, sizeof(XVisualInfo));
    visInfoMask.visualid = attr.visual->visualid;

    if (m_pVisualInfo)
    {
      XFree(m_pVisualInfo);
      m_pVisualInfo = NULL;
    }

    int nv;
    XLockDisplay(m_pPixmapDisplay);
    m_pVisualInfo  = XGetVisualInfo(m_pPixmapDisplay, VisualIDMask, &visInfoMask, &nv);
    XUnlockDisplay(m_pPixmapDisplay);

    UINT32 nDepth = 32;
    nDepth = (UINT32)m_pVisualInfo->depth;

    pxData->m_colormap = attr.colormap; 

    // get bits per pixel information for the best depth we can display
    ULONG32 bitsPerPixel = 32;
    int i, n;
    XLockDisplay(m_pPixmapDisplay);
    XPixmapFormatValues *pixmap_formats = XListPixmapFormats(m_pPixmapDisplay, &n);
    XUnlockDisplay(m_pPixmapDisplay);
    if (pixmap_formats) 
    {
      for (i = 0; i < n; i++)
      {
          if (pixmap_formats[i].depth == nDepth)
          {
              bitsPerPixel = pixmap_formats[i].bits_per_pixel;
          }
       }
    }
    XFree(pixmap_formats);

    // get site size
    HXxSize siteWinSize;
    pSite->GetSize(siteWinSize);

    pxData->m_backgroundBitmapInfoHeader.biSize = sizeof(HXBitmapInfoHeader);  

    pxData->m_backgroundBitmapInfoHeader.biWidth = siteWinSize.cx;
    pxData->m_backgroundBitmapInfoHeader.biHeight = siteWinSize.cy;
    pxData->m_backgroundBitmapInfoHeader.biPlanes = 1; // just seems to be the convention  
    pxData->m_backgroundBitmapInfoHeader.biBitCount = bitsPerPixel;
    HX_ASSERT(pxData->m_backgroundBitmapInfoHeader.biBitCount % 8 == 0);

    // BI_RGB just seems to be the convention 
    switch (pxData->m_backgroundBitmapInfoHeader.biBitCount) 
    {
      case 8:
      case 24:
      case 32:
          pxData->m_backgroundBitmapInfoHeader.biCompression = BI_RGB;
          break;
      case 16:
          pxData->m_backgroundBitmapInfoHeader.biCompression = HXCOLOR_RGB565_ID;
          break;
      default:
          HX_ASSERT(FALSE);
    }

    pxData->m_backgroundBitmapInfoHeader.biSizeImage = //size of image (bytes):
            pxData->m_backgroundBitmapInfoHeader.biWidth * pxData->m_backgroundBitmapInfoHeader.biHeight *  
            pxData->m_backgroundBitmapInfoHeader.biBitCount;
    pxData->m_backgroundBitmapInfoHeader.biXPelsPerMeter = 0;   
    pxData->m_backgroundBitmapInfoHeader.biYPelsPerMeter = 0;   
    pxData->m_backgroundBitmapInfoHeader.biClrUsed = 0;  
    pxData->m_backgroundBitmapInfoHeader.biClrImportant = 0;  
    pxData->m_backgroundBitmapInfoHeader.rcolor = m_pVisualInfo->red_mask; 
    pxData->m_backgroundBitmapInfoHeader.gcolor = m_pVisualInfo->green_mask;   
    pxData->m_backgroundBitmapInfoHeader.bcolor = m_pVisualInfo->blue_mask; 

    XLockDisplay(m_pPixmapDisplay);
    pxData->m_Pixmap = XCreatePixmap(m_pPixmapDisplay, 
                       window,
                       pxData->m_backgroundBitmapInfoHeader.biWidth, 
                       pxData->m_backgroundBitmapInfoHeader.biHeight, 
                       nDepth);
    XUnlockDisplay(m_pPixmapDisplay);
    return pxData;
}
#endif

#if defined(_DEBUG)

void CSmilDocumentRenderer::DumpEventQueue()
{
    MLOG_EVENT(m_pErrorMessages,
               "======================================================\n"
               "Event Queue at %lu Current Group %d (%lu events):\n"    
               "            type    time grp ohs ign\n",
               m_ulCurrentTime, m_uCurrentGroupIndex,
               (m_pEventList? m_pEventList->GetCount() : 0));
    if (m_pEventList)
    {
        LISTPOSITION pos = m_pEventList->GetHeadPosition();
        while(pos)
        {
            CSmilLayoutEvent* pEvent =
                (CSmilLayoutEvent*) m_pEventList->GetNext(pos);
            if (pEvent)
            {
                MLOG_EVENT(m_pErrorMessages,
                           "%16s %7lu  %1u   %1lu   %1lu",
                           pEvent->getEventTypeName(),
                           pEvent->m_ulEventTime,
                           pEvent->m_uGroupIndex,
                           pEvent->m_bOnlyHideSite,
                           pEvent->m_bIgnorEvent);
                switch (pEvent->m_type)
                {
                    case CSmilLayoutEvent::eShowSite:
                    case CSmilLayoutEvent::eHideSite:
                        {
                            CSmilShowSiteEvent* pShow = (CSmilShowSiteEvent*) pEvent;
                            MLOG_EVENT(m_pErrorMessages,
                                       "\tmedia=%s\tregion=%s\n",
                                       pShow->getMediaID(),
                                       pShow->getRegionID());
                        }
                        break;
                    default:
                        MLOG_EVENT(m_pErrorMessages, "\n");
                }
            }
        }
    }
    MLOG_EVENT(m_pErrorMessages,
               "======================================================\n");
}

void CSmilDocumentRenderer::DumpZOrder(CSmilBasicBox* pBox, UINT32 ulIndentLevel)
{
    if (pBox)
    {
        for (UINT32 i = 0; i < ulIndentLevel; i++)
        {
            MLOG_LAYOUT(m_pErrorMessages, "\t");
        }
        BOOL        bIsRoot = (pBox->m_pParent == NULL ? TRUE : FALSE);
        const char* pszName = NULL;
        if (bIsRoot)
        {
            pszName = "root";
        }
        else
        {
            CSmilBasicRegion* pReg = (CSmilBasicRegion*) pBox;
            pszName = (const char*) pReg->m_region;
        }
        MLOG_LAYOUT(m_pErrorMessages, "site=0x%08x\t(%s)\tzOrder=%ld\n",
                    pBox->m_pSite, pszName, getSiteZIndex(pBox->m_pSite));
        // Dump children
        if (pBox->m_pChildList)
        {
            LISTPOSITION pos = pBox->m_pChildList->GetHeadPosition();
            while (pos)
            {
                CSmilBasicBox* pChildBox = (CSmilBasicBox*) pBox->m_pChildList->GetNext(pos);
                DumpZOrder(pChildBox, ulIndentLevel + 1);
            }
        }
    }
}

void CSmilDocumentRenderer::DumpAllGroupInfo(UINT32 ulTime)
{
    if (m_pParent)
    {
        IHXPlayer* pPlayer = m_pParent->getPlayer();
        if (pPlayer)
        {
           IHXGroupManager* pGroupManager = NULL;
           pPlayer->QueryInterface(IID_IHXGroupManager, (void**) &pGroupManager);
           if (pGroupManager)
           {
               MLOG_SRC(m_pErrorMessages,"Group Information Dump at %lu:\n", ulTime);
               UINT16 usNumGroups = pGroupManager->GetGroupCount();
               UINT16 usCurGroup  = 0;
               pGroupManager->GetCurrentGroup(usCurGroup);
               MLOG_SRC(m_pErrorMessages,"\tNumber of Groups: %u\n", usNumGroups);
               MLOG_SRC(m_pErrorMessages,"\tCurrent Group:    %u\n", usCurGroup);
               for (UINT16 i = 0; i < usNumGroups; i++)
               {
                   IHXGroup* pGroup = NULL;
                   pGroupManager->GetGroup(i, pGroup);
                   if (pGroup)
                   {
                       MLOG_SRC(m_pErrorMessages,"\tGroup %u Info:\n", i);
                       UINT16 usNumTracks = pGroup->GetTrackCount();
                       MLOG_SRC(m_pErrorMessages,"\t\tNumber of Tracks: %u\n", usNumTracks);
                       MLOG_SRC(m_pErrorMessages,"\t\tGroup Properties:\n");
                       IHXValues* pGrpProp = pGroup->GetGroupProperties();
                       if (pGrpProp)
                       {
                           DumpAllProperties(pGrpProp, 3);
                       }
                       HX_RELEASE(pGrpProp);
                       // Dump track properties
                       for (UINT16 j = 0; j < usNumTracks; j++)
                       {
                           IHXValues* pTrackValues = NULL;
                           pGroup->GetTrack(j, pTrackValues);
                           if (pTrackValues)
                           {
                               MLOG_SRC(m_pErrorMessages,"\t\tTrack %u Properties:\n", j);
                               DumpAllProperties(pTrackValues, 3);
                           }
                           HX_RELEASE(pTrackValues);
                       }
                   }
                   HX_RELEASE(pGroup);
               }
           }
           HX_RELEASE(pGroupManager);
        }
    }
}

void CSmilDocumentRenderer::DumpAllProperties(IHXValues* pValues, UINT32 ulNumTabIndent)
{
    if (pValues)
    {
        // Dump CString properties
//        char        szDbgStr[256];
        UINT32      i       = 0;
        IHXBuffer* pValue  = NULL;
        const char* pszName = NULL;
        HX_RESULT   rv      = pValues->GetFirstPropertyCString(pszName, pValue);
        while (SUCCEEDED(rv))
        {
//            for (i = 0; i < ulNumTabIndent; i++) DEBUGPRINTF(szDbgStr, "\t");
//            DEBUGPRINTF(szDbgStr, "%s = %s\n", pszName,
//                        (const char*) pValue->GetBuffer());
            HX_RELEASE(pValue);
            rv = pValues->GetNextPropertyCString(pszName, pValue);

        }
        // Dump ULONG32 properties
        UINT32 ulValue = 0;
        rv = pValues->GetFirstPropertyULONG32(pszName, ulValue);
        while (SUCCEEDED(rv))
        {
//            for (i = 0; i < ulNumTabIndent; i++) DEBUGPRINTF(szDbgStr, "\t");
//            DEBUGPRINTF(szDbgStr, "%s = %lu\n", pszName, ulValue);
            HX_RELEASE(pValue);
            rv = pValues->GetNextPropertyULONG32(pszName, ulValue);
        }
        // Dump Buffer properties
        rv = pValues->GetFirstPropertyBuffer(pszName, pValue);
        while (SUCCEEDED(rv))
        {
//            for (i = 0; i < ulNumTabIndent; i++) DEBUGPRINTF(szDbgStr, "\t");
//            DEBUGPRINTF(szDbgStr, "%s = ", pszName);
            UINT32 ulSize = pValue->GetSize();
            BYTE*  pBuf   = pValue->GetBuffer();
            for (i = 0; i < ulSize; i++)
            {
//                DEBUGPRINTF(szDbgStr, "%02X ", pBuf[i]);
            }
//            DEBUGPRINTF(szDbgStr, "\n");
            HX_RELEASE(pValue);
            rv = pValues->GetNextPropertyBuffer(pszName, pValue);
        }
    }
}

void CSmilDocumentRenderer::DumpAllSiteInfo(const char* pszFileName, UINT32 ulTime)
{
    if (pszFileName)
    {
        FILE* fp = fopen(pszFileName, "a");
        if (fp)
        {
            fprintf(fp, "======================================================\n");
            fprintf(fp, "Dumping all site info at time %lu:\n\n", ulTime);
            DumpBoxSiteInfo(m_pRootLayout, fp, 0);
            fprintf(fp, "======================================================\n");
            fclose(fp);
        }
    }
}

void CSmilDocumentRenderer::DumpBoxSiteInfo(CSmilBasicBox* pBox, FILE* fp, UINT32 ulIndentLevel)
{
    if (pBox && fp && ulIndentLevel < 64)
    {
        // Set up indent string
        char szIndent[64]; /* Flawfinder: ignore */
        for (UINT32 i = 0; i < ulIndentLevel; i++) szIndent[0] = '\t';
        szIndent[ulIndentLevel] = '\0';
        // Dump info on this box itself
        //
        // Is this box a root?
        if (pBox->m_pParent)
        {
            // It has a parent, so it can't be a root - 
            // it must be a region
            CSmilBasicRegion* pRegion = (CSmilBasicRegion*) pBox;
            fprintf(fp, "%sDumping site info for region %s:\n",
                    szIndent, (const char*) pRegion->m_region);
        }
        else
        {
            // No parent, so it is a root
            fprintf(fp, "%sDumping site info for root box:\n", szIndent);
        }
        // Dump the info on this box's site
        HXxSize  cSize = {0, 0};
        HXxPoint cPos  = {0, 0};
        if (pBox->m_pSite)
        {
            pBox->m_pSite->GetSize(cSize);
            pBox->m_pSite->GetPosition(cPos);
        }
        fprintf(fp, "%s\tsize=(%ld,%ld) pos=(%ld,%ld) vis=%lu z-index=%ld\n",
                szIndent, cSize.cx, cSize.cy, cPos.x, cPos.y,
                isSiteVisible(pBox->m_pSite), getSiteZIndex(pBox->m_pSite));
        // Dump info on this box's renderer sites
        if (pBox->m_pChildRendererSiteList)
        {
            fprintf(fp, "%s\trenderer children sites:\n", szIndent);
            LISTPOSITION pos = pBox->m_pChildRendererSiteList->GetHeadPosition();
            while (pos)
            {
                IHXSite* pChildRendSite =
                    (IHXSite*) pBox->m_pChildRendererSiteList->GetNext(pos);
                if (pChildRendSite)
                {
                    pChildRendSite->GetSize(cSize);
                    pChildRendSite->GetPosition(cPos);
                    CHXString cMediaID("unknown");
                    getMediaIDFromSite(pChildRendSite, cMediaID);
                    fprintf(fp, "%s\t\tid=%s size=(%ld,%ld) pos=(%ld,%ld) vis=%lu z-index=%ld\n",
                            szIndent, (const char*) cMediaID,
                            cSize.cx, cSize.cy, cPos.x, cPos.y,
                            isSiteVisible(pChildRendSite),
                            getSiteZIndex(pChildRendSite));
                }
            }
        }
        // Now recursively force updates on any children
        if (pBox->m_pChildList)
        {
            fprintf(fp, "%s\tbox children:\n", szIndent);
            LISTPOSITION pos = pBox->m_pChildList->GetHeadPosition();
            while (pos)
            {
                CSmilBasicBox* pChildBox =
                    (CSmilBasicBox*) pBox->m_pChildList->GetNext(pos);
                DumpBoxSiteInfo(pChildBox, fp, ulIndentLevel + 1);
            }
        }
    }
}

#endif // #if defined(_DEBUG)

HX_RESULT CSmilDocumentRenderer::getMediaIDFromSite(IHXSite*      pSite,
                                                    REF(CHXString) rcStr)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pSite && m_pSiteWatcherMap)
    {
        void* pVoid = NULL;
        if (m_pSiteWatcherMap->Lookup(pSite, pVoid) && pVoid)
        {
            CSmilSiteWatcher* pWatcher = (CSmilSiteWatcher*) pVoid;
            retVal = HXR_OK;
            rcStr = pWatcher->GetSourceID();
        }
    }

    return retVal;
}

void CSmilDocumentRenderer::queueSiteForAnimationRedraw(IHXSite* pSite)
{
    if (pSite && m_pAnimSiteRedrawMap)
    {
        m_pAnimSiteRedrawMap->SetAt((void*) pSite, (void*) pSite);
    }
}

void CSmilDocumentRenderer::queueRegionForRecompute(CSmilBasicRegion* pRegion)
{
    if (pRegion && m_pAnimRegionRecomputeMap)
    {
        m_pAnimRegionRecomputeMap->SetAt((void*) pRegion, (void*) pRegion);
    }
}

void CSmilDocumentRenderer::removePendingAnimationRedraw(IHXSite* pSite)
{
    if (pSite && m_pAnimSiteRedrawMap)
    {
        // XXXMEH - TODO - right now, if we have done a SetSize()
        // or SetPostion() on a site, then we remove any pending
        // ForceRedraw() on that site. However, both SetSize and
        // SetPosition() force redraws on other sites as well,
        // like the parent and siblings. We should optimize those
        // out as well, but we need a reliable way of determining
        // if those sites are going to get redrawn.
        m_pAnimSiteRedrawMap->RemoveKey((void*) pSite);
    }
}

void CSmilDocumentRenderer::turnSiteCompositionModeON()
{
    MLOG_ANIM(m_pErrorMessages,"turnSiteCompositionModeON()\tm_ulCurrentTime=%lu\ttick=%lu\n",
              m_ulCurrentTime, HX_GET_BETTERTICKCOUNT());
    if (m_pRootLayout && m_pRootLayout->m_pSite)
    {
        IHXSiteComposition* pSiteComp = NULL;
        m_pRootLayout->m_pSite->QueryInterface(IID_IHXSiteComposition, (void**) &pSiteComp);
        if (pSiteComp)
        {
            pSiteComp->SetCompositionMode(TRUE);
        }
        HX_RELEASE(pSiteComp);
    }
}

void CSmilDocumentRenderer::turnSiteCompositionModeOFF()
{
    MLOG_ANIM(m_pErrorMessages,"turnSiteCompositionModeOFF()\tm_ulCurrentTime=%lu\ttick=%lu\n",
              m_ulCurrentTime, HX_GET_BETTERTICKCOUNT());
    if (m_pRootLayout && m_pRootLayout->m_pSite)
    {
        IHXSiteComposition* pSiteComp = NULL;
        m_pRootLayout->m_pSite->QueryInterface(IID_IHXSiteComposition, (void**) &pSiteComp);
        if (pSiteComp)
        {
            pSiteComp->SetCompositionMode(FALSE);
        }
        HX_RELEASE(pSiteComp);
    }
}

void CSmilDocumentRenderer::lockSiteComposition()
{
    MLOG_ANIM(m_pErrorMessages,"lockSiteComposition()\tm_ulCurrentTime=%lu\ttick=%lu\n",
              m_ulCurrentTime, HX_GET_BETTERTICKCOUNT());
    if (m_pRootLayout && m_pRootLayout->m_pSite)
    {
        IHXSiteComposition* pSiteComp = NULL;
        m_pRootLayout->m_pSite->QueryInterface(IID_IHXSiteComposition, (void**) &pSiteComp);
        if (pSiteComp)
        {
            pSiteComp->LockComposition();
        }
        HX_RELEASE(pSiteComp);
    }
}

void CSmilDocumentRenderer::unlockSiteComposition()
{
    MLOG_ANIM(m_pErrorMessages,"unlockSiteComposition()\tm_ulCurrentTime=%lu\ttick=%lu\n",
              m_ulCurrentTime, HX_GET_BETTERTICKCOUNT());
    if (m_pRootLayout && m_pRootLayout->m_pSite)
    {
        IHXSiteComposition* pSiteComp = NULL;
        m_pRootLayout->m_pSite->QueryInterface(IID_IHXSiteComposition, (void**) &pSiteComp);
        if (pSiteComp)
        {
            pSiteComp->UnlockComposition();
        }
        HX_RELEASE(pSiteComp);
    }
}

void CSmilDocumentRenderer::bltSiteComposition()
{
    MLOG_ANIM(m_pErrorMessages,"bltSiteComposition()\tm_ulCurrentTime=%lu\ttick=%lu\n",
              m_ulCurrentTime, HX_GET_BETTERTICKCOUNT());
    if (m_pRootLayout && m_pRootLayout->m_pSite)
    {
        IHXSiteComposition* pSiteComp = NULL;
        m_pRootLayout->m_pSite->QueryInterface(IID_IHXSiteComposition, (void**) &pSiteComp);
        if (pSiteComp)
        {
            pSiteComp->BltComposition();
        }
        HX_RELEASE(pSiteComp);
    }
}

BOOL CSmilDocumentRenderer::isSiteCompositionLocked()
{
    BOOL bRet = FALSE;

    if (m_pRootLayout && m_pRootLayout->m_pSite)
    {
        IHXSiteComposition* pSiteComp = NULL;
        m_pRootLayout->m_pSite->QueryInterface(IID_IHXSiteComposition, (void**) &pSiteComp);
        if (pSiteComp)
        {
            bRet = pSiteComp->IsCompositionLocked();
        }
        HX_RELEASE(pSiteComp);
    }

    return bRet;
}

BOOL CSmilDocumentRenderer::isSiteCompositionModeON()
{
    BOOL bRet = FALSE;

    if (m_pRootLayout && m_pRootLayout->m_pSite)
    {
        IHXSiteComposition* pSiteComp = NULL;
        m_pRootLayout->m_pSite->QueryInterface(IID_IHXSiteComposition, (void**) &pSiteComp);
        if (pSiteComp)
        {
            bRet = pSiteComp->IsCompositionMode();
        }
        HX_RELEASE(pSiteComp);
    }

    return bRet;
}

void CSmilDocumentRenderer::GoToURLWithParamCheck(const char*        pszURL,
                                                  const char*        pszTarget,
                                                  const char*        pszSendTo,
                                                  IHXHyperNavigate* pHyper,
                                                  CSmilElement*      pElement,
                                                  IUnknown*          pContext,
                                      BOOL                 bUserInvoked,
                                                  BOOL               bHint)
{
    if (pszURL && pHyper && pElement && pContext)
    {
        // Are there any <rn:param> children of
        // this <area> tag?
      BOOL bHasParamChildren = hasParamChildren(pElement, FALSE);

        IHXCommonClassFactory* pFact = NULL;
        pContext->QueryInterface(IID_IHXCommonClassFactory, (void**) &pFact);
        if (pFact)
        {
            IHXValues* pParams = NULL;
            pFact->CreateInstance(CLSID_IHXValues, (void**) &pParams);
            if (pParams)
            {
            if (bHasParamChildren)
            {
                // Add the name/value pairs from all
                // <rn:param> children to the IHXValues
                addParamProperties(pElement, pParams, pContext, FALSE);
            }

            // /Add the sendTo value, if there is one:
            if (pszSendTo)
            {
                addStringProperty(pParams, pContext, "sendTo", pszSendTo);
            }

            // /Fixes open-outside-of-media-pane version of PR 84160:
            pParams->SetPropertyULONG32("AutoActivated",
                  (ULONG32)(!bUserInvoked));

                if (bHint)
                {
                    // Add "begin" parameter
                    pParams->SetPropertyULONG32("begin", pElement->m_ulDelay);
                    // QI for IHXHyperNavigateHint
                    IHXHyperNavigateHint* pHint = NULL;
                    pHyper->QueryInterface(IID_IHXHyperNavigateHint, (void**) &pHint);
                    if (pHint)
                    {
                        // Now call IHXHyperNavigateHint::Hint()
                        pHint->Hint(pszURL, pszTarget, pParams);
                    }
                    HX_RELEASE(pHint);
                }
                else
                {
                    // QI for IHXHyperNavigate2
                    IHXHyperNavigate2* pNav2 = NULL;
                    pHyper->QueryInterface(IID_IHXHyperNavigate2, (void**) &pNav2);
                    if (pNav2)
                    {
                        // Now call IHXHyperNavigate2::Execute()
                        pNav2->Execute(pszURL, pszTarget, NULL, NULL, pParams);
                    }
                    else
                    {
                        // We couldn't get IHXHyperNavigate2, so 
                        // just use IHXHyperNavigate.
                        pHyper->GoToURL(pszURL, pszTarget);
                    }
                    HX_RELEASE(pNav2);
                }
            }
            HX_RELEASE(pParams);
        }
        HX_RELEASE(pFact);
    }
}

UINT16 CSmilDocumentRenderer::getCurrentGroup()
{
    UINT16 usRet = 0;

    if (m_pParent)
    {
        IHXPlayer* pPlayer = m_pParent->getPlayer();
        if (pPlayer)
        {
           IHXGroupManager* pGroupManager = NULL;
           pPlayer->QueryInterface(IID_IHXGroupManager, (void**) &pGroupManager);
           if (pGroupManager)
           {
               pGroupManager->GetCurrentGroup(usRet);
           }
           HX_RELEASE(pGroupManager);
        }
    }

    return usRet;
}

void CSmilDocumentRenderer::deleteExternalMediaMarkerList()
{
    if (m_pExternalMediaMarkerList)
    {
        LISTPOSITION pos = m_pExternalMediaMarkerList->GetHeadPosition();
        while (pos)
        {
            CExternalMediaMarkerInfo* pInfo =
                (CExternalMediaMarkerInfo*) m_pExternalMediaMarkerList->GetNext(pos);
            HX_DELETE(pInfo);
        }
        m_pExternalMediaMarkerList->RemoveAll();
        HX_DELETE(m_pExternalMediaMarkerList);
    }
}

CExternalMediaMarkerInfo* CSmilDocumentRenderer::getExternalMediaMarkerInfo(const char* pszURL)
{
    CExternalMediaMarkerInfo* pRet = NULL;

    if (pszURL && m_pExternalMediaMarkerList)
    {
        LISTPOSITION pos = m_pExternalMediaMarkerList->GetHeadPosition();
        while (pos)
        {
            CExternalMediaMarkerInfo* pInfo =
                (CExternalMediaMarkerInfo*) m_pExternalMediaMarkerList->GetNext(pos);
            if (pInfo &&
                (!strcmp(pInfo->m_pszAddTrackURL, pszURL) ||
                 !strcmp(pInfo->m_pszRendererURL, pszURL)))
            {
                pRet = pInfo;
                break;
            }
        }
    }

    return pRet;
}

BOOL CSmilDocumentRenderer::isIDMappedToExternalMediaMarkerFile(CExternalMediaMarkerInfo* pInfo,
                                                                const char*               pszID)
{
    BOOL bRet = FALSE;

    if (pInfo && pInfo->m_pIDList && pszID)
    {
        LISTPOSITION pos = pInfo->m_pIDList->GetHeadPosition();
        while (pos)
        {
            char* pID = (char*) pInfo->m_pIDList->GetNext(pos);
            if (pID && !strcmp(pID, pszID))
            {
                bRet = TRUE;
                break;
            }
        }
    }

    return bRet;
}

HX_RESULT
CSmilDocumentRenderer::closeOldRenderers(BOOL bAll)
{
    if (m_pDelayedRendererCloseList)
    {
      LISTPOSITION pos = m_pDelayedRendererCloseList->GetHeadPosition();
      while (pos)
      {
          SMILDelayedRendererClose* pClose = 
            (SMILDelayedRendererClose*)m_pDelayedRendererCloseList->GetAt(pos);
          if (bAll || pClose->m_uGroup != m_uCurrentGroupIndex)
          {
            if (bAll)
            {
                // If we are not closing all, then the Site SHOULD allready be hidden.
                CSmilBasicRegion* pRegion = getRegionByID(pClose->m_pSiteInfo->m_regionID);
                IHXSite* pRegionSite = NULL;
                if (pRegion->m_eBackgroundColorType == CSS2TypeTransparent)
                {
                  pRegionSite = pClose->m_pSiteInfo->m_pRegionSite;
                }
    
                showSite(pClose->m_pSiteInfo->m_pRendererSite, FALSE);
                showSite(pRegionSite, FALSE);
            }
            actualRendererClosed(pClose->m_pRenderer, pClose->m_pInfo);
            HX_RELEASE(pClose->m_pRenderer);
            HX_RELEASE(pClose->m_pInfo);
            pos = m_pDelayedRendererCloseList->RemoveAt(pos);
            HX_DELETE(pClose);
          }
          else
          {
            m_pDelayedRendererCloseList->GetNext(pos);
          }
      }
    }
    return HXR_OK;
}

STDMETHODIMP
CSmilDocumentRenderer::RendererClosed(IHXRenderer* pRenderer, 
                              IHXValues* pInfo)
{
    HX_RESULT ret = HXR_OK;
    UINT32 ulGroupIndex = 0;
    UINT32 ulTrackIndex = 0;
    SMILSiteInfo* pSiteInfo = NULL;
    SMILGroupInfo* pGroupInfo = NULL;
    SMILPlayToAssoc* pPlayToAssoc = NULL;

    pInfo->GetPropertyULONG32("GroupIndex", ulGroupIndex);
    pInfo->GetPropertyULONG32("TrackIndex", ulTrackIndex);

    IHXBuffer* pBuffer = NULL;
    if (HXR_OK == pInfo->GetPropertyCString("id", pBuffer))
    {
      pPlayToAssoc = getPlayToAssocByMedia((const char*)pBuffer->GetBuffer());
    }
    HX_RELEASE(pBuffer);

    if (!pPlayToAssoc)
    {
      // XXX HP we shouldn't be here
      HX_ASSERT(FALSE);
      pPlayToAssoc = getPlayToAssoc((UINT16)ulGroupIndex, (UINT16)ulTrackIndex);
    }

    // RemoveTrack() has been called on this track
    if (pPlayToAssoc && pPlayToAssoc->m_bRemovePending)
    {
      ret = actualRendererClosed(pRenderer, pInfo);
    }
    else if (m_pSiteInfoByRendererMap &&
           m_pSiteInfoByRendererMap->Lookup(pRenderer, (void*&)pSiteInfo))
    {
      if (!pSiteInfo->m_bNoRegion &&
          m_pGroupInfoMap &&
          m_pGroupInfoMap->Lookup(ulGroupIndex, (void*&)pGroupInfo))
      {
          // check to see if this site ended at the end of this group,
          // if it did, we will delay actually closing the renderer 
          // until after the next group is started... That way we can
          // keep a site arround if there is a transition...
          if (pSiteInfo->m_ulDuration == pGroupInfo->m_ulDuration)
          {
            SMILDelayedRendererClose* pClose = new SMILDelayedRendererClose;
            if (pClose)
            {
                pClose->m_uGroup = (UINT16)ulGroupIndex;
                pClose->m_pRenderer = pRenderer;
                pClose->m_pRenderer->AddRef();
                pClose->m_pInfo = pInfo;
                pClose->m_pInfo->AddRef();
                pClose->m_pSiteInfo = pSiteInfo;

                if (!m_pDelayedRendererCloseList)
                {
                  m_pDelayedRendererCloseList = new CHXSimpleList;
                }
                if (m_pDelayedRendererCloseList)
                {
                  m_pDelayedRendererCloseList->AddTail(pClose);
                }
                else
                {
                  ret = HXR_OUTOFMEMORY;
                }
            }
            else
            {
                ret = HXR_OUTOFMEMORY;
            }
          }
          else
          {
            ret = actualRendererClosed(pRenderer, pInfo);
          }
      }
      else
      {
          // if there is no group info, just close the renderer.
          ret = actualRendererClosed(pRenderer, pInfo);
      }
    }
    else
    {
      // if there is no site info, just close the renderer.
      ret = actualRendererClosed(pRenderer, pInfo);
    }
   return ret;
}

HX_RESULT
CSmilDocumentRenderer::actualRendererClosed(IHXRenderer* pRenderer, 
                                  IHXValues* pInfo)
{
    UINT32 ulGroupIndex = 0;
    UINT32 ulTrackIndex = 0;
    UINT32 ulStreamNumber = 0;
    CHXSimpleList* pRendererList = NULL;
    SMILSiteInfo* pSiteInfo = NULL;
    SMILPlayToAssoc* pPlayToAssoc = NULL;

    pInfo->GetPropertyULONG32("GroupIndex", ulGroupIndex);
    pInfo->GetPropertyULONG32("TrackIndex", ulTrackIndex);
    pInfo->GetPropertyULONG32("StreamNumber", ulStreamNumber);

    if (m_pPlayToAssocList)
    {
      IHXBuffer* pBuffer = NULL;
      if (HXR_OK == pInfo->GetPropertyCString("id", pBuffer))
      {
          pPlayToAssoc = getPlayToAssocByMedia((const char*)pBuffer->GetBuffer());
      }
      HX_RELEASE(pBuffer);

      if (!pPlayToAssoc)
      {
          // XXX HP we shouldn't be here
          HX_ASSERT(FALSE);
          pPlayToAssoc = getPlayToAssoc((UINT16)ulGroupIndex, (UINT16)ulTrackIndex);
      }

      if (pPlayToAssoc)
      {     
          pPlayToAssoc->m_sourceMap.Lookup(ulStreamNumber, (void*&) pRendererList);
            // Remove any event sinks set up on this renderer
            addRemoveEventSink((const char*) pPlayToAssoc->m_id, FALSE);
            // Remove this renderer from the map
            removeRendererFromMap((const char*) pPlayToAssoc->m_id);
      }
    }

//{FILE* f1 = ::fopen("c:\\temp\\out.txt", "a+"); ::fprintf(f1, "RendererClosed pRenderer=%lu\n", pRenderer);::fclose(f1);}

    if (m_pSiteInfoByRendererMap && 
      m_pSiteInfoByRendererMap->Lookup(pRenderer, (void*&) pSiteInfo))
    {
      RemoveEvents(ulGroupIndex, pSiteInfo->m_pRendererSite);
      
      m_pSiteInfoByRendererMap->RemoveKey((void*)pRenderer);

      CSmilBasicRegion* pRegion = getRegionByID(pSiteInfo->m_regionID);
      IHXSite* pRegionSite = NULL;
      if (pRegion && pRegion->m_eBackgroundColorType == CSS2TypeTransparent)
      {
          pRegionSite = pSiteInfo->m_pRegionSite;
      }

#ifdef XXX_HIDE_SITES_ON_CLOSE
      showSite(pSiteInfo->m_pRendererSite, FALSE);
      // we only want to hide the region if there is no more 
      // renderer site shares the same region   
      if (!pRendererList || pRendererList->GetCount() == 1)
      {
          IHXSite* pRegionSite = NULL;
          if (!pRegion->m_bBgColorSet)
          {
            pRegionSite = pSiteInfo->m_pRegionSite;
          }    
          showSite(pRegionSite, FALSE);
      }
#endif

        // Hide the site, since we're about to blow
        // it away anyway.
      showSite(pSiteInfo->m_pRendererSite, FALSE);
      m_pSiteMgr->RemoveSite(pSiteInfo->m_pRendererSite);
      pSiteInfo->m_pRendererSite->DetachWatcher();
        // Get the site watcher from the site watcher map
        if (m_pSiteWatcherMap)
        {
            void* pVoid = NULL;
            if (m_pSiteWatcherMap->Lookup((void*) pSiteInfo->m_pRendererSite, pVoid) &&
                pVoid)
            {
                // Get the CSmilSiteWatcher pointer
                CSmilSiteWatcher* pWatcher = (CSmilSiteWatcher*) pVoid;
                // Remove the site watcher from the site watcher map
                m_pSiteWatcherMap->RemoveKey((void*) pSiteInfo->m_pRendererSite);
                // Remove the site watcher from the map
                if (pPlayToAssoc)
                {
                    removeRendererSiteWatcherFromMap((const char*) pPlayToAssoc->m_id);
                }
                // Release our ref on the site watcher
                HX_RELEASE(pWatcher);
            }
        }
      if(pRegion &&
          pRegion->m_pSite)
      {
          pRegion->m_pSite->DestroyChild(pSiteInfo->m_pRendererSite);
      }

        //XXXgfw this is too fix PR56050. This release deletes the
        //site but a pointer is left to it in m_pChildRendererSiteList
        //This list is iterated through inside of resetRendererSites
        //and SetSize is called a site that doesn't exist.
        //
        //So, this is my fix without knowing much about this code. :-)
        if( pRegion && pSiteInfo )
        {
            pRegion->removeRendererSiteChild(pSiteInfo->m_pRendererSite);
        }

        // Remove the site from the map
        if (pPlayToAssoc)
        {
            removeRendererSiteFromMap((const char*) pPlayToAssoc->m_id);
        }
        
      HX_RELEASE(pSiteInfo->m_pRendererSite);

        // remove the site info struct from m_pSiteInfoList
        // but *don't* delete it yet - we will do that down
        // at the bottom of this method
        removeSiteInfo(pSiteInfo);
    }

    if (pPlayToAssoc)
    {
      LISTPOSITION    pos;
      SMILSourceInfo* pSMILSourceInfo = NULL;

      if (pRendererList)
      {
          CHXSimpleList::Iterator  i = pRendererList->Begin();

          for (; i != pRendererList->End(); ++i)
          {
            pSMILSourceInfo = (SMILSourceInfo*) (*i);
      
            if (pSMILSourceInfo->m_pRenderer == pRenderer)
            {
                pos = pRendererList->Find(pSMILSourceInfo);
                pRendererList->RemoveAt(pos);     
      
                if (pSMILSourceInfo->m_pRendererEventHook)
                {
                  // get to the site manager and set an event hook
                  IHXEventHookMgr* pHookMgr = NULL;
                  if(HXR_OK == m_pSiteMgr->QueryInterface(IID_IHXEventHookMgr, 
                                                (void**)&pHookMgr))
                  {
                      pHookMgr->RemoveHook(pSMILSourceInfo->m_pRendererEventHook, 
                                     pSMILSourceInfo->m_pRendererEventHook->GetChannelName(), 0);     
                  }
                  pHookMgr->Release();
                }
                
                HX_RELEASE(pSMILSourceInfo->m_pRendererEventHook);
                HX_RELEASE(pSMILSourceInfo->m_pStream);
                HX_RELEASE(pSMILSourceInfo->m_pRenderer);

                HX_DELETE(pSMILSourceInfo);
                break;
            }
          }

          if (pRendererList->GetCount())
          {
            pSMILSourceInfo = (SMILSourceInfo*)pRendererList->GetHead();
          
            pPlayToAssoc->m_tunerName = pSMILSourceInfo->m_tunerName;
            pPlayToAssoc->m_childTunerName = pSMILSourceInfo->m_childTunerName;
            pPlayToAssoc->m_ulDelay = pSMILSourceInfo->m_ulDelay;
            pPlayToAssoc->m_ulDuration = pSMILSourceInfo->m_ulDuration;
            pPlayToAssoc->m_pRendererEventHook = pSMILSourceInfo->m_pRendererEventHook;
          }
      }

      if (pPlayToAssoc->m_pSiteInfoList->GetCount() > 0 &&
          pSiteInfo)
      {
          pos = pPlayToAssoc->m_pSiteInfoList->Find(pSiteInfo);
          if (pos)
          {
            pPlayToAssoc->m_pSiteInfoList->RemoveAt(pos);
          }
            HX_DELETE(pSiteInfo);
      }
    }

    return HXR_OK;
}

HX_RESULT CSmilDocumentRenderer::addSiteForRenderer(SMILPlayToAssoc* pPlayToAssoc,
                                                    SMILSourceInfo*  pSMILSourceInfo,
                                                    IHXRenderer*    pRenderer,
                                                    BOOL             bNoRegion)
{
    HX_RESULT retVal = HXR_OK;

    if (m_pRootLayout)
    {
        // We need to decide whether this renderer is playing to
        // a single region (where the <ref>'s "region" attribute
        // is a <region> id) or multiple regions (where the <ref>'s
        // "region" attribute is a <region> regionName)
        //
        // Try to get a region by regionID
        CSmilBasicRegion* pRegion = getRegionByID(pPlayToAssoc->m_playTo);
        if (pRegion)
        {
            // We got a region by region id, so use that
            retVal = addSiteForRendererByRegion(pPlayToAssoc, pSMILSourceInfo,
                                                pRenderer, bNoRegion, pRegion);
        }
        else
        {
            // We couldn't get a region by region id, so try
            // and get regions by regionName
            pRegion = getFirstRegionByName(pPlayToAssoc->m_playTo);
            while (pRegion && SUCCEEDED(retVal))
            {
                // We got a region by region id, so use that
                retVal = addSiteForRendererByRegion(pPlayToAssoc, pSMILSourceInfo,
                                                    pRenderer, bNoRegion, pRegion);
                if (SUCCEEDED(retVal))
                {
                    pRegion = getNextRegionByName(pPlayToAssoc->m_playTo);
                }
            }
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT CSmilDocumentRenderer::addSiteForRendererByRegion(SMILPlayToAssoc*  pPlayToAssoc,
                                                            SMILSourceInfo*   pSMILSourceInfo,
                                                            IHXRenderer*     pRenderer,
                                                            BOOL              bNoRegion,
                                                            CSmilBasicRegion* pRegion)
{
    HX_RESULT retVal = HXR_OK;

    if(pPlayToAssoc && pSMILSourceInfo &&
       pRenderer    && pRegion && pRegion->m_pSite &&
       m_pSiteMgr)
    {
        // First create a child of the region site
        IHXSite* pRendererSite = NULL;
        retVal = pRegion->m_pSite->CreateChild(pRendererSite);
        if (SUCCEEDED(retVal))
        {
            // Now decide if we need to show the renderer site
            if(pSMILSourceInfo->m_ulDelay > 0)
            {
                // show site after m_ulDelay
                showSite(pRendererSite, FALSE);
            }
            else 
            {
                // show it now, don't wait for first time sync

                // unless there is a begin transition, this prevents
                // the site from being shown and then clipped back.
                if (pPlayToAssoc->m_beginTransition.GetLength() == 0)
                {
                    // There is no transition, so show everything
                    showSite(pRegion->m_pSite, TRUE);
                    showSite(pRendererSite, TRUE);
                }
                else
                {
                    // There IS a transition, so make the
                    // renderer site invisible. This should stop the
                    // flashing we see at the beginning of 
                    // crossfades.
                    showSite(pRendererSite, FALSE);
                }
            }
            // Put this site into this region's list
            // of child renderer sites
            pRegion->addRendererSiteChild(pRendererSite);
            // Add to the media id to site map
            addRendererSiteToMap((const char*) pPlayToAssoc->m_id,
                                 pRendererSite);
            // Now create a site watcher for this renderer site
            CSmilSiteWatcher* pRendererWatch = new CSmilSiteWatcher(this,
                                                                    pRegion->m_region,
                                                                    TRUE,
                                                                    pPlayToAssoc->m_id);
            if (pRendererWatch)
            {
                // AddRef the watcher
                pRendererWatch->AddRef();
                // Get the top-level box
                CSmilBasicBox* pTopBox = getTopLevelBox(pRegion);
                if (pTopBox &&
                    pTopBox->m_eResizeBehavior == ResizeZoom &&
                    !m_bDoNotZoom)
                {
                    // Initialize the watcher with the current zoom factor. This
                    // is important if the player STARTS up in some other zoom
                    // mode besides original size.
                    pRendererWatch->SetZoomScaleFactors(pRegion->m_dZoomScaleFactorX,
                                                        pRegion->m_dZoomScaleFactorY);
                    // Set the resize behavior
                    pRendererWatch->SetResizeBehavior(pTopBox->m_eResizeBehavior);
                }
                // Attach the watcher to the renderer site
                retVal = pRendererSite->AttachWatcher(pRendererWatch);
                if (SUCCEEDED(retVal))
                {
                    // Create the site watcher map if necessary
                    if (!m_pSiteWatcherMap)
                    {
                        m_pSiteWatcherMap = new CHXMapPtrToPtr();
                    }
                    if (m_pSiteWatcherMap)
                    {
                        // Add the site watcher to the site watcher map
                        m_pSiteWatcherMap->SetAt((void*) pRendererSite,
                                                 (void*) pRendererWatch);
                        // Add to the media id to site watcher map
                        addRendererSiteWatcherToMap((const char*) pPlayToAssoc->m_id,
                                                    pRendererWatch);
                        // /We need to set the "sensitivity" of the renderer
                        // site (see SMIL 2.0 Interop Media test #3.1):
                        if (pPlayToAssoc->m_id.GetLength() > 0  && m_pSmilParser)
                        {
                            CSmilElement* pElement = m_pSmilParser->findElement(pPlayToAssoc->m_id);
                            HX_ASSERT(pElement);
                            ULONG32 ulSensitivityStrLen = 0;
                            if (pElement &&
                                pElement->m_sensitivityToMouseEvents.GetLength() > 0)
                            {
                                setSiteProperty(pRendererSite,
                                                "sensitivity",
                                                (const char*) pElement->m_sensitivityToMouseEvents);
                            }
                        }

                        // Set "channel" in the site's properties
                        retVal = setSiteProperty(pRendererSite,
                                                 "channel",
                                                 pSMILSourceInfo->m_childTunerName);
                        if (SUCCEEDED(retVal))
                        {
//                            setSiteProperty(pRendererSite,
//                                            "playto",
//                                            pSMILSourceInfo->m_childTunerName);
                            // Add the site to the site manager
                            retVal = m_pSiteMgr->AddSite(pRendererSite);
                            if (SUCCEEDED(retVal))
                            {
                                // Check the showBackground property on the region. If it is
                                // "always", then go ahead and show the site. If not, we'll
                                // hide it and let the show event show it
                                if (pRegion->m_pSmilRegion &&
                                    pRegion->m_pSmilRegion->m_eShowBackground == ShowBackgroundWhenActive)
                                {
                                    // showBackground is "whenActive", so we hide
                                    // the site.
                                    showSite(pRegion->m_pSite, FALSE);
                                }
                                else
                                {
                                    // showBackground is "always", so we force the
                                    // site to be shown
                                    showSite(pRegion->m_pSite, TRUE);
                                }
                                // in any case, stick the event on the list...
                                UINT32 ulShowTime = pSMILSourceInfo->m_ulDelay;

                                CSmilShowSiteEvent* pShowEvent = 
                                    new CSmilShowSiteEvent(pPlayToAssoc->m_uGroupIndex,
                                                           ulShowTime,
                                                           pRendererSite,
                                                           pRegion->m_pSite,
                                                           TRUE,
                                                           FALSE,
                                                           this,
                                                           pPlayToAssoc->m_id,
                                                           pRegion->m_region,
                                                           pRegion->m_eShowBackground);
                                insertEvent(pShowEvent);

                                // Compute the time at which to remove the element. Here
                                // we take fill behavior into account.
                                UINT32 ulRemoveTime = 0;
                                HX_RESULT rv = m_pSmilParser->computeRemoveTime((const char*) pPlayToAssoc->m_id,
                                                                                ulRemoveTime);
                                if (FAILED(rv))
                                {
                                    // We couldn't currently compute the remove
                                    // time, so we set it way out, assuming it will
                                    // get pulled back later
                                    ulRemoveTime = WAY_IN_THE_FUTURE;
                                }

                                // Get the fill behavior for this media
                                FillType eFill = getMediaFillBehavior((const char*) pPlayToAssoc->m_id);

                                BOOL bIgnorHide = FALSE;
                                SMILGroupInfo* pGroupInfo = 0;
                                if (m_pGroupInfoMap &&
                                    m_pGroupInfoMap->Lookup(pPlayToAssoc->m_uGroupIndex, 
                                    (void*&)pGroupInfo))
                                {
                                    if (pGroupInfo->m_bDurationSet)
                                    {
                                        if (pGroupInfo->m_ulDuration == ulRemoveTime &&
                                            eFill                    != FillRemove)
                                        {
                                            bIgnorHide = TRUE;
                                        }
                                    }
                                }
    
                                // XXXMEH - we will go ahead and schedule the hide
                                // for after the duration IF we know what the duration is.
                                // This would essentially be fill="remove" behavior. Later,
                                // when our parent duration gets updated in handleFillTimeUpdate()
                                // we will move this event forward or backwards if necessary.
                                // XXXMEH - we should also check our parent's duration here.
                                // If the parent has an explicit dur, then it will already be
                                // resolved and may cut off the child.
                                if (!pPlayToAssoc->m_bLiveSource)
                                {
                                    CSmilShowSiteEvent* pHideEvent = 
                                        new CSmilShowSiteEvent(pPlayToAssoc->m_uGroupIndex,
                                                               ulRemoveTime, 
                                                               pRendererSite,
                                                               pRegion->m_pSite,
                                                               FALSE,
                                                               bIgnorHide,
                                                               this,
                                                               pPlayToAssoc->m_id,
                                                               pRegion->m_region,
                                                               pRegion->m_eShowBackground);
                                    insertEvent(pHideEvent);
                                    // Set the event time into the element
                                    setElementRemoveTime(pPlayToAssoc->m_id, ulRemoveTime);
                                }

                                SMILSiteInfo* pSiteInfo     = new SMILSiteInfo;
                                pSiteInfo->m_pRendererSite  = pRendererSite;
                                pSiteInfo->m_pRegionSite    = pRegion->m_pSite;
                                pSiteInfo->m_uGroupIndex    = pPlayToAssoc->m_uGroupIndex;
                                pSiteInfo->m_ulDelay        = pSMILSourceInfo->m_ulDelay;
                                pSiteInfo->m_ulDuration     = pSMILSourceInfo->m_ulDuration +
                                                              pSMILSourceInfo->m_ulDelay;
                        pSiteInfo->m_ulResumeTime   = 0;
                                pSiteInfo->m_bRemoveSite    = pPlayToAssoc->m_bRemoveSite;
                                pSiteInfo->m_bNoRegion      = bNoRegion;
                                pSiteInfo->m_regionID       = pPlayToAssoc->m_playTo;
                                pSiteInfo->m_pRenderer      = pRenderer;
                                pSiteInfo->m_ulLexicalOrder = pPlayToAssoc->m_ulLexicalOrder;
                                pSiteInfo->m_MediaID        = pPlayToAssoc->m_id;
                                pSiteInfo->m_lZIndex        = 0;

                                // Look up any z-index override on the media element
                                CSmilSource* pSrc = getSource(pPlayToAssoc->m_id);
                                if (pSrc &&
                                    pSrc->m_eZIndexType != CSS2TypeAuto)
                                {
                                    pSiteInfo->m_lZIndex = pSrc->m_lZIndex;
                                }

                                pPlayToAssoc->m_pSiteInfoList->AddTail(pSiteInfo);

                                insertSiteInfo(pSiteInfo);
    
                                m_pSiteInfoByRendererMap->SetAt(pRenderer, pSiteInfo);
    
                                // If the rn:backgroundOpacity or rn:mediaOpacity attributes
                                // are set, then set those properties into the site.
                                if (pSrc)
                                {
                                    if (pSrc->m_bBackgroundOpacitySpecified)
                                    {
                                        setSitePropertyULONG32(pRendererSite,
                                                               "backgroundOpacity",
                                                               pSrc->m_ulBackgroundOpacity);
                                    }
                                    if (pSrc->m_bMediaOpacitySpecified)
                                    {
                                        setSitePropertyULONG32(pRendererSite,
                                                               "mediaOpacity",
                                                               pSrc->m_ulMediaOpacity);
                                    }
                                }

#if defined(HELIX_FEATURE_SMIL2_TRANSITIONS)
                                // schedule a start transition if there is one...
                                if (pPlayToAssoc->m_beginTransition.GetLength() > 0)
                                {
                                    // Create a begin transition event
                                    CSmilTransitionEvent* pTransEvent = new CSmilTransitionEvent(pSMILSourceInfo->m_ulDelay,
                                                                                                 pPlayToAssoc,
                                                                                                 pSiteInfo,
                                                                                                 TRUE,
                                                                                                 this);
                                    // Insert this event in the event queue
                                    insertEvent(pTransEvent);

                                }
                                // schedule an end transition if there is one.
                                if (pPlayToAssoc->m_endTransition.GetLength() > 0 &&
                                    !pPlayToAssoc->m_bLiveSource)
                                {
                                    CSmilTransitionInfo* pTInfo = getTransition(pPlayToAssoc->m_endTransition);
                                    if (pTInfo)
                                    {
                                        CSmilTransitionEvent* pTransEvent =
                                            new CSmilTransitionEvent(pSMILSourceInfo->m_ulDelay +
                                                                     pSMILSourceInfo->m_ulDuration -
                                                                     pTInfo->m_pTrans->m_ulDuration,
                                                                     pPlayToAssoc,
                                                                     pSiteInfo,
                                                                     FALSE,
                                                                     this);
                                        insertEvent(pTransEvent);
                                    }
                                }
#endif /* #if defined(HELIX_FEATURE_SMIL2_TRANSITIONS) */
                            }
                        }
                    }
                    else
                    {
                        retVal = HXR_OUTOFMEMORY;
                    }
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }


    return retVal;
}

HX_RESULT CSmilDocumentRenderer::setSiteProperty(IHXSite*   pSite,
                                                 const char* pszKey,
                                                 const char* pszValue)
{
    MLOG_LAYOUT(m_pErrorMessages,
                "setSiteProperty(0x%08x,%s,%s) m_ulCurrentTime=%lu tick=%lu\n",
                pSite, pszKey, pszValue, m_ulCurrentTime, HX_GET_BETTERTICKCOUNT());
    HX_RESULT retVal = HXR_FAIL;

    if (pSite && m_pContext && pszKey && pszValue)
    {
        IHXValues* pValues = NULL;
        pSite->QueryInterface(IID_IHXValues, (void**) &pValues);
        if (pValues)
        {
            IHXCommonClassFactory* pFactory = NULL;
            m_pContext->QueryInterface(IID_IHXCommonClassFactory, (void**) &pFactory);
            if (pFactory)
            {
                IHXBuffer* pBuffer = NULL;
                pFactory->CreateInstance(CLSID_IHXBuffer, (void**) &pBuffer);
                if (pBuffer)
                {
                    retVal = pBuffer->Set((const UCHAR*) pszValue,
                                          strlen(pszValue) + 1);
                    if (SUCCEEDED(retVal))
                    {
                        retVal = pValues->SetPropertyCString(pszKey,
                                                             pBuffer);
                    }
                }
                HX_RELEASE(pBuffer);
            }
            HX_RELEASE(pFactory);
        }
        HX_RELEASE(pValues);
    }

    return retVal;
}

HX_RESULT CSmilDocumentRenderer::setSitePropertyULONG32(IHXSite*   pSite,
                                                        const char* pszKey,
                                                        UINT32      ulValue)
{
    MLOG_LAYOUT(m_pErrorMessages,
                "setSitePropertyULONG32(0x%08x,%s,%lu) m_ulCurrentTime=%lu tick=%lu\n",
                pSite, pszKey, ulValue, m_ulCurrentTime, HX_GET_BETTERTICKCOUNT());
    HX_RESULT retVal = HXR_FAIL;

    if (pSite && m_pContext && pszKey)
    {
        IHXValues* pValues = NULL;
        retVal = pSite->QueryInterface(IID_IHXValues, (void**) &pValues);
        if (SUCCEEDED(retVal))
        {
            retVal = pValues->SetPropertyULONG32(pszKey, ulValue);
        }
        HX_RELEASE(pValues);
    }

    return retVal;
}

HX_RESULT CSmilDocumentRenderer::getSiteProperty(IHXSite*        pSite,
                                                 const char*      pszKey,
                                                 REF(IHXBuffer*) rpValueStr)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pSite && m_pContext && pszKey)
    {
        IHXValues* pValues = NULL;
        pSite->QueryInterface(IID_IHXValues, (void**) &pValues);
        if (pValues)
        {
            HX_RELEASE(rpValueStr);
            retVal = pValues->GetPropertyCString(pszKey, rpValueStr);
        }
        HX_RELEASE(pValues);
    }

    return retVal;
}

HX_RESULT
CSmilDocumentRenderer::insertIntoZOrderList(CHXSimpleList* pList, 
                                  SMILSiteInfo* pInfo)
{
    LISTPOSITION lPos = pList->GetHeadPosition();
    LISTPOSITION lPrev = lPos;

    // /Helps fix PR 81510 (& duplicate PR 83796): use resume time instead
    // of delay if it's more recent than delay meaning it got paused and
    // needs to come to the front when it resumes (in an excl):
    ULONG32 ulNewSiteRelevantDelay = pInfo->m_ulDelay;
    if (pInfo->m_ulResumeTime > ulNewSiteRelevantDelay)
    {
      ulNewSiteRelevantDelay = pInfo->m_ulResumeTime;
    }

    BOOL bInserted = FALSE;
    while(lPos)
    {
      SMILSiteInfo* pThisInfo = (SMILSiteInfo*)pList->GetNext(lPos);

      // /Helps fix PR 81510 (& duplicate PR 83796) by using resume time
      // in case it got paused and needs to come to the front when it
      // resumes in an excl:
      ULONG32 ulThisSiteRelevantDelay = pThisInfo->m_ulDelay;
      if (pThisInfo->m_ulResumeTime > ulThisSiteRelevantDelay)
      {
          ulThisSiteRelevantDelay = pThisInfo->m_ulResumeTime;
      }

      if (pThisInfo->m_uGroupIndex != pInfo->m_uGroupIndex)
      {
          if (pThisInfo->m_uGroupIndex > pInfo->m_uGroupIndex)
          {
            pList->InsertBefore(lPrev, pInfo);
            bInserted = TRUE;
            break;
          }
      }
      else if (ulThisSiteRelevantDelay == ulNewSiteRelevantDelay &&
          pThisInfo->m_ulLexicalOrder > pInfo->m_ulLexicalOrder)
      {
          pList->InsertBefore(lPrev, pInfo);
          bInserted = TRUE;
          break;
      }
      else if (ulThisSiteRelevantDelay > ulNewSiteRelevantDelay)
      {
          pList->InsertBefore(lPrev, pInfo);
          bInserted = TRUE;
          break;
      }
      lPrev = lPos;
    }
    if(!bInserted)
    {
      // not inserted, stick it on the end of the list
      pList->AddTail(pInfo);
    }
    return HXR_OK;
}

HX_RESULT CSmilDocumentRenderer::setRendererZOrder(SMILSiteInfo* pSiteInfo)
{
    HX_RESULT retVal = HXR_OK;

    if (pSiteInfo && !pSiteInfo->m_bNoRegion && m_pSiteInfoList)
    {
        if (getNumberOfChildSites(pSiteInfo->m_pRegionSite) > 1)
        {
            // Create a list of all sites that have the same
            // region as us.
            CHXSimpleList commonRegionList;
            LISTPOSITION pos = m_pSiteInfoList->GetHeadPosition();
            while (pos)
            {
                SMILSiteInfo* pInfo = (SMILSiteInfo*) m_pSiteInfoList->GetNext(pos);
                if (pInfo && pInfo->m_pRegionSite == pSiteInfo->m_pRegionSite)
                {
                    insertIntoZOrderList(&commonRegionList, pInfo);
                }
            }
            // Run through and set the z-index in increasing order
            INT32 lOrder = 0;
            pos          = commonRegionList.GetHeadPosition();
            while (pos)
            {
                SMILSiteInfo* pInfo = (SMILSiteInfo*) commonRegionList.GetNext(pos);
                if (pInfo)
                {
                    setSiteZIndex(pInfo->m_pRendererSite, lOrder++);
                }
            }
        }
    }

    return retVal;
}

/*
 * IHXGroupSink methods
 */

STDMETHODIMP
CSmilDocumentRenderer::GroupAdded(UINT16  uGroupIndex,
                        IHXGroup*   pGroup)
{
    if(!m_pGroupInfoMap)
    {
      m_pGroupInfoMap = new CHXMapLongToObj;
    }

    // save group info
    SMILGroupInfo* pGroupInfo = 0;
    if(!m_pGroupInfoMap->Lookup(uGroupIndex, (void*&)pGroupInfo))
    {
      IHXPlayer* pPlayer = m_pParent->getPlayer();
      IHXGroupManager* pMgr = 0;
      UINT32 ulTotalTracks = 0;
      UINT32 ulDuration = 0;

      if(HXR_OK == pPlayer->QueryInterface(IID_IHXGroupManager, 
                                   (void**)&pMgr))
      {
          IHXGroup* pGroup = NULL;
          IHXGroup2* pGroup2 = NULL;
          if (HXR_OK == pMgr->GetGroup(uGroupIndex, pGroup) &&
            HXR_OK == pGroup->QueryInterface(IID_IHXGroup2, (void**)&pGroup2))
          {
            IHXValues* pGroupProperties = NULL;

            pGroup2->GetPersistentComponentProperties(m_ulPersistentComponentID,
                                            pGroupProperties);
            if(pGroupProperties)
            {
                pGroupProperties->GetPropertyULONG32("total_tracks", 
                  ulTotalTracks);
                pGroupProperties->GetPropertyULONG32("duration", 
                  ulDuration);
            }
            HX_RELEASE(pGroupProperties);
          }
          HX_RELEASE(pGroup2);
          HX_RELEASE(pGroup);
      }
      HX_RELEASE(pMgr);
      
      pGroupInfo = new SMILGroupInfo;
      pGroupInfo->m_nTracks = (int)ulTotalTracks;
      pGroupInfo->m_nTracksAdded = 0;
      pGroupInfo->m_nTrackDurationsSet = 0;
      pGroupInfo->m_ulDuration = ulDuration;
      // if we have an explicit duration on the source, say that
      // we have allready got all the track durations set
      if (pGroupInfo->m_ulDuration)
      {
          pGroupInfo->m_bDurationSet = TRUE;
          pGroupInfo->m_ulDuration += m_pSmilParser->m_ulPersistentComponentDelay;

          PersistentDurationSet(pGroupInfo->m_ulDuration,
                          m_pSmilParser->m_ulPersistentComponentDelay,
                          FALSE);
      }
      else
      {
          pGroupInfo->m_bDurationSet = FALSE;
      }
      (*m_pGroupInfoMap)[uGroupIndex] = pGroupInfo;
    }
    else
    {
      pGroupInfo->m_nTracksAdded++;
    }

    return HXR_OK;
}

STDMETHODIMP
CSmilDocumentRenderer::GroupRemoved(UINT16      uGroupIndex,
                         IHXGroup*  pGroup)
{
    return HXR_OK;
}


STDMETHODIMP
CSmilDocumentRenderer::AllGroupsRemoved()
{
    return HXR_NOTIMPL;
}


STDMETHODIMP
CSmilDocumentRenderer::TrackAdded(UINT16  uGroupIndex,
                         UINT16           uTrackIndex,
                         IHXValues* pTrack)
{
    MLOG_SRC(m_pErrorMessages,"TrackAdded(%u,%u,0x%08x)\n",
             uGroupIndex, uTrackIndex, pTrack);
    if(pTrack)
    {
      IHXBuffer* pBuf = 0;
      const char* pID = 0;
      const char* pRepeatID = 0;
      const char* pRegionName = 0;
      const char* pBeginTransition = NULL;
      const char* pEndTransition = NULL;
      UINT32 ulDelay = 0;

      if(HXR_OK == pTrack->GetPropertyCString("id", pBuf))
      {
          pID = (const char*)pBuf->GetBuffer();
          pBuf->Release();
      }

      if(HXR_OK == pTrack->GetPropertyCString("repeatid", pBuf))
      {
          pRepeatID = (const char*)pBuf->GetBuffer();
          pBuf->Release();
      }

      if(HXR_OK == pTrack->GetPropertyCString("region", pBuf))
      {
          pRegionName = (const char*)pBuf->GetBuffer();
          pBuf->Release();
      }

      if(HXR_OK == pTrack->GetPropertyCString("beginTransition", pBuf))
      {
          pBeginTransition = (const char*)pBuf->GetBuffer();
          pBuf->Release();
      }

      if(HXR_OK == pTrack->GetPropertyCString("endTransition", pBuf))
      {
          pEndTransition = (const char*)pBuf->GetBuffer();
          pBuf->Release();
      }

        // If this is an external media marker file, then
        // save the group and track index
        BOOL bXMMSource = FALSE;
      if(HXR_OK == pTrack->GetPropertyCString("ExternalMarkerFile", pBuf))
      {
            bXMMSource = TRUE;
            pBuf->Release();
            pTrack->GetPropertyCString("url", pBuf);
            CExternalMediaMarkerInfo* pInfo = getExternalMediaMarkerInfo((const char*) pBuf->GetBuffer());
            if (pInfo)
            {
                pInfo->m_usGroupIndex = uGroupIndex;
                pInfo->m_usTrackIndex = uTrackIndex;
            }
          pBuf->Release();
      }

      UINT32 num = 0;
      pTrack->GetPropertyULONG32("lexicalNum", num);

      if(HXR_OK == pTrack->GetPropertyCString("playto", pBuf))
      {
          const char* pPlayTo = (const char*)pBuf->GetBuffer();
          setPlayToAssoc(uGroupIndex, uTrackIndex, pID, pRepeatID, pPlayTo, pRegionName, 
            pBeginTransition, pEndTransition, num, bXMMSource);
          pBuf->Release();
      }
      else  // non-windowed renderer
      {
          setPlayToAssoc(uGroupIndex, uTrackIndex, pID, pRepeatID, 0, pRegionName, 
            pBeginTransition, pEndTransition, num, bXMMSource);
      }

      SMILPlayToAssoc* pAssoc = getPlayToAssoc(uGroupIndex, uTrackIndex);
      if(pAssoc)
      {
            if (m_pSmilParser)
            {
                CSmilElement* pElement = m_pSmilParser->findElement(pID);
                if (pElement)
                {
                    if (pElement->m_eActualFill == FillFreeze ||
                        pElement->m_eActualFill == FillHold   ||
                        pElement->m_eErase      == EraseNever)
                    {
                        pAssoc->m_bRemoveSite = FALSE;
                    }
                }
                
            }
      }

      SMILGroupInfo* pGroupInfo = 0;
      if(m_pGroupInfoMap->Lookup(uGroupIndex, (void*&)pGroupInfo))
      {
          pGroupInfo->m_nTracksAdded++;
      }
    }
    return HXR_OK;
}

STDMETHODIMP
CSmilDocumentRenderer::TrackRemoved(UINT16      uGroupIndex,
                            UINT16  uTrackIndex,
                            IHXValues*    pTrack)
{
    HX_RESULT     hr = HXR_OK;
    UINT32  ulDurationPlayed = 0;

    SMILPlayToAssoc* pPlayToAssoc = getPlayToAssoc(uGroupIndex, uTrackIndex);

    if (!pPlayToAssoc)
    {
      hr = HXR_UNEXPECTED;
      goto cleanup;
    }

    HX_ASSERT(pPlayToAssoc->m_bRemovePending);

    // caculate the actual duration this track has been played
    if (uGroupIndex == m_uCurrentGroupIndex)
    {
      if (m_ulCurrentTime > pPlayToAssoc->m_ulDelay)
      {
          ulDurationPlayed = m_ulCurrentTime - pPlayToAssoc->m_ulDelay;
      }
    }

    hr = m_pSmilParser->trackRemoved(pPlayToAssoc->m_id, ulDurationPlayed);

    removeTracksPlayToAssoc(uGroupIndex, uTrackIndex);

    // process the updated sources 
    if (HXR_OK == hr)
    {
      hr = handleElements();
    }

cleanup:

    return hr;
}

STDMETHODIMP
CSmilDocumentRenderer::TrackStarted(UINT16      uGroupIndex,
                         UINT16           uTrackIndex,
                         IHXValues* pTrack)
{
    if(m_bSettingFragment)
    {
      // find out if all tracks for this group have been
      // added, then go ahead and seek
      UINT16 uFragmentGroup = m_pSmilParser->getFragmentGroup(m_pFragment);
      if(uFragmentGroup == uGroupIndex)
      {
          SMILGroupInfo* pGroupInfo = 0;

          m_nFragmentTracks++;
          if(m_pGroupInfoMap->Lookup(uFragmentGroup, (void*&)pGroupInfo))
          {
            if(pGroupInfo->m_nTracks == m_nFragmentTracks)
            {
                // /Set offset here, and then call seek on the player
                // in this's OnBegin() method which is when the player
                // is fully initialized and ready to be seeked:
                    BOOL bTmp = m_bFragFoundAndResolved;
                m_ulFragmentTimeOffset =
                  m_pSmilParser->getFragmentOffset(m_pFragment,
                  bTmp /*<--Passed by reference.*/);
                    m_bFragFoundAndResolved = bTmp;

                m_bSettingFragment = FALSE;
            }
          }
      }
    }

    // /Fire off the "beginEvent" event for the associated element:
    HX_RESULT rslt = HXR_OK;
    SMILPlayToAssoc* pPlayToAssoc =
          getPlayToAssoc(uGroupIndex, uTrackIndex);   
    if (pPlayToAssoc)
    {
      // /Fixes PR 79300: need to set m_bIsDeferredInExcl to FALSE now that
      // it has started playing, otherwise siblings will calculate their
      // post-deferred begins as indefinite:
      CSmilElement* pThisElement = m_pSmilParser->findElement(
            pPlayToAssoc->m_id);
      if (pThisElement  &&  pThisElement->m_bIsDeferredInExcl)
      {
          pThisElement->m_bIsDeferredInExcl = FALSE;
          pThisElement->m_ulTimeDeferralOccurred = (UINT32)-1;
          // /Fixes PR 86103: we need to force re-compute of end time hide
          // event since the end time, during the defer period, has been
          // treated as indefinite|unresolved:
          pThisElement->checkElementFillBehavior();
      }

      rslt = m_pSmilParser->tryToResolveBeginEndEvents("beginEvent",
            (const char*)pPlayToAssoc->m_id,
            // /XXXEH- hack until core calls us at time when track media
            // actually starts playing (which is at track's delay, not
            // at group's begin); the following hack at least gets the
            // right ballpark, but subsequent real-time adjustments to
            // this track's begin time (like if it doesn't end up playing
            // at all) will result in incorrect playback in some cases:
            pPlayToAssoc->m_ulDelay +// /XXXEH <-hack:remove w/fixed core
            // /XXXEH- ...and use m_ulCurrentTime instead of 0, below:
            0 /*m_ulCurrentTime*/);
      if SUCCEEDED(rslt)
      {
          handleElements();
      }
    }

    return rslt;
}

STDMETHODIMP
CSmilDocumentRenderer::TrackStopped(UINT16      uGroupIndex,
                         UINT16           uTrackIndex,
                         IHXValues* pTrack)
{
    HX_RESULT rslt = HXR_OK;
    SMILPlayToAssoc* pPlayToAssoc =
          getPlayToAssoc(uGroupIndex, uTrackIndex);
    if (pPlayToAssoc)
    {
      rslt = m_pSmilParser->tryToResolveBeginEndEvents("endEvent",
            (const char*)pPlayToAssoc->m_id, m_ulCurrentTime);
      // /For any paused elements in an excl awaiting this element
      // finishing, we need to call this so they know they can resume:
      HX_RESULT rsltResume =
            m_pSmilParser->tryToResolveBeginEndEvents("resumeEvent",
            (const char*)pPlayToAssoc->m_id, m_ulCurrentTime);
      HX_ASSERT(HXR_OK == rsltResume);
      // /Helps fix PR 85885: for any deferred elements that were deferred
      // while another was already deferred in an excl awaiting this element
      // finishing, we need to call this so they know they can begin:
      HX_RESULT rsltUndefer =
            m_pSmilParser->tryToResolveBeginEndEvents("undeferEvent",
            (const char*)pPlayToAssoc->m_id, m_ulCurrentTime);
      HX_ASSERT(HXR_OK == rsltUndefer);

      // /This "fixes" PR 69639 (although I have no idea why it broke in
      // the first place; perhaps the core used to call TrackRemoved() and
      // now it doesn't?  If so, that would explain it.   If element gets
      // played all the way through, then gets re-started, a new track is
      // created and thus a new track# is assigned, so we need to remove
      // the playToAssoc so pausing of the track happens on the correct
      // one (via element-id-based lookup of track index):
      // /Setting this flag instead of removing track's playtoAssoc fixes
      // PR 69975:
      pPlayToAssoc->m_bTrackStopped = TRUE;

      if SUCCEEDED(rslt)
      {
          if (!m_pHandlePendingSchedulingCallback)
          {
            m_pHandlePendingSchedulingCallback = new HandlePendingSchedulingCallback;
            m_pHandlePendingSchedulingCallback->m_pSmilDocumentRenderer = this;
            m_pHandlePendingSchedulingCallback->AddRef();
          }

          if (m_pHandlePendingSchedulingCallback->m_bIsCallbackPending)
          {
            m_pScheduler->Remove(m_pHandlePendingSchedulingCallback->m_PendingHandle);
          }
                    
          m_pHandlePendingSchedulingCallback->m_bIsCallbackPending = TRUE;
          m_pHandlePendingSchedulingCallback->m_PendingHandle = 
          m_pScheduler->RelativeEnter(m_pHandlePendingSchedulingCallback, 0);
      }
    }

    return rslt;
}

STDMETHODIMP
CSmilDocumentRenderer::CurrentGroupSet(UINT16   uGroupIndex,
                            IHXGroup*     pGroup)
{
    INT16   uPrevGroupIndex = m_uCurrentGroupIndex;

    m_uCurrentGroupIndex = (INT16)uGroupIndex;
    m_ulCurrentTime = 0;

    if(uPrevGroupIndex != -1)
    {
      closeOldRenderers(FALSE);

      m_pSmilParser->resetTimeline();

      removeGroupEvents(uPrevGroupIndex);

      removeActiveTransitions();

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)
        removeActiveAnimations();
#endif /* #if defined(HELIX_FEATURE_SMIL2_ANIMATION) */
      
      removeGroupsPlayToAssoc(uPrevGroupIndex);

      m_ulEventListPosition = 0;

      if(m_pStatusMessage)
      {
          m_pStatusMessage->SetStatus(NULL);
      }

      // make hide events for sites from the last Group
      addOldRendererHideEvents(uGroupIndex);
    }

// XXXMEH - temp remove
#if 0
    // Schedule the animate events for this group if
    // the group index is not 0. If it is 0, then these
    // events have already been scheduled in handleAnimate()
    if (m_pAnimationMap && uGroupIndex != 0)
    {
        POSITION pos = m_pAnimationMap->GetStartPosition();
        while (pos)
        {
            const char* pszKey = NULL;
            void*       pVoid  = NULL;
            m_pAnimationMap->GetNextAssoc(pos, pszKey, pVoid);
            if (pszKey && pVoid)
            {
                // Cast from the void*
                CSmilAnimateElement* pAnimate = (CSmilAnimateElement*) pVoid;
                // Is this animation element in the current group?
                if (pAnimate->m_pNode &&
                    pAnimate->m_pNode->m_nGroup == uGroupIndex)
                {
                    // We need to schedule an animate event
                    CSmilAnimateEvent* pEvent = new CSmilAnimateEvent(pAnimate->m_pNode->m_nGroup,
                                                                      pAnimate->m_ulDelay,
                                                                      FALSE,
                                                                      pAnimate,
                                                                      this,
                                                                      m_pSmilParser);
                    if (pEvent)
                    {
                        // Put the event only the timeline
                        insertEvent(pEvent);
                    }
                }
            }
        }
    }
#endif

    return HXR_OK;
}

STDMETHODIMP
CSmilDocumentRenderer::PrefetchTrackAdded(UINT16      uGroupIndex,
                                UINT16    uPrefetchTrackId,
                                IHXValues*      pTrack)
{
    return HXR_OK;
}

STDMETHODIMP
CSmilDocumentRenderer::PrefetchTrackRemoved(UINT16    uGroupIndex,
                                  UINT16  uPrefetchTrackId,
                                  IHXValues*    pTrack)
{
    return HXR_OK;
}

STDMETHODIMP
CSmilDocumentRenderer::PrefetchTrackDone(UINT16     uGroupIndex,
                               UINT16         uPrefetchTrackId,
                               HX_RESULT  status)
{
    HX_RESULT rslt = HXR_OK;
    CHXString* pId;
    HX_ASSERT(m_pPrefetchTrackElementMap);
    if (m_pPrefetchTrackElementMap  &&
          m_pPrefetchTrackElementMap->Lookup(uPrefetchTrackId,
          (void*&)pId))
    {
      // /This does nothing if duration is already a valid amount.
      // Returns HXR_FAILED if resetDuration() is not called on the element.
      HX_RESULT rsltOfDurationSetting =
            m_pSmilParser->handlePrefetchFinished(*pId,
            0==m_ulCurrentTime? 100:m_ulCurrentTime);
      if (SUCCEEDED(rsltOfDurationSetting))
      {
          handleElements();
      }

      rslt = m_pSmilParser->tryToResolveBeginEndEvents("endEvent",
            *pId, m_ulCurrentTime);
      if SUCCEEDED(rslt)
      {
          if (!m_pHandlePendingSchedulingCallback)
          {
            m_pHandlePendingSchedulingCallback = new HandlePendingSchedulingCallback;
            m_pHandlePendingSchedulingCallback->m_pSmilDocumentRenderer = this;
            m_pHandlePendingSchedulingCallback->AddRef();
          }

          if (m_pHandlePendingSchedulingCallback->m_bIsCallbackPending)
          {
            m_pScheduler->Remove(m_pHandlePendingSchedulingCallback->m_PendingHandle);
          }
                    
          m_pHandlePendingSchedulingCallback->m_bIsCallbackPending = TRUE;
          m_pHandlePendingSchedulingCallback->m_PendingHandle = 
          m_pScheduler->RelativeEnter(m_pHandlePendingSchedulingCallback, 0);
      }
    }
    
    return HXR_OK;
}

HX_RESULT
CSmilDocumentRenderer::addOldRendererHideEvents(UINT16 uGroupIndex)
{
    CHXSimpleList regions;
    if (m_pDelayedRendererCloseList)
    {
      CHXSimpleList::Iterator i = m_pDelayedRendererCloseList->Begin();
      for (;i != m_pDelayedRendererCloseList->End(); ++i)
      {
          SMILDelayedRendererClose* pClose = (SMILDelayedRendererClose*)(*i);
          CSmilBasicRegion* pRegion = getRegionByID(pClose->m_pSiteInfo->m_regionID);
          regions.AddTail(pRegion);

          IHXSite* pRegionSite = NULL;
          if (pRegion->m_eBackgroundColorType == CSS2TypeTransparent)
          {
            pRegionSite = pRegion->m_pSite;
          }

            CSmilShowSiteEvent* pHideEvent = 
                new CSmilShowSiteEvent(uGroupIndex,
                                       0,
                                       pClose->m_pSiteInfo->m_pRendererSite,
                                       pRegionSite,
                                       FALSE);
          
          insertEvent(pHideEvent);
            // Set the event time into the element
            setElementRemoveTime(pHideEvent->getMediaID(), pHideEvent->m_ulEventTime);
      }
    }
    
    // hide any "transparent" sites, that are not going to be hidden at 
    // the start of the next presentation.
    if(m_pRegionMap)
    {
      CHXMapStringToOb::Iterator i = m_pRegionMap->Begin();
      for(; i != m_pRegionMap->End(); ++i)
      {
          CSmilBasicRegion* pRegion = (CSmilBasicRegion*)(*i);
          if(!pRegion->m_bNested && 
             pRegion->m_eBackgroundColorType == CSS2TypeTransparent &&
               !regions.Find(pRegion))
          {
            showSite(pRegion->m_pSite, FALSE);
          }
      }
    }
    return HXR_OK;
}

void
CSmilDocumentRenderer::removeGroupEvents(UINT16 uGroupIndex)
{
    if(m_pEventList)
    {
      LISTPOSITION lPos = m_pEventList->GetHeadPosition();
      while(lPos)
      {
          // handle all events at or before ulTimeValue
          CSmilLayoutEvent* pEvent = (CSmilLayoutEvent*)m_pEventList->GetAt(lPos);
          if(pEvent->m_uGroupIndex == uGroupIndex)
          {
            delete(pEvent);
            lPos = m_pEventList->RemoveAt(lPos);
          }
          else
          {
            m_pEventList->GetNext(lPos);
          }
      }
    }
}

void
CSmilDocumentRenderer::removeActiveTransitions()
{
    if(m_pActiveTransitions)
    {
      LISTPOSITION lPos = m_pActiveTransitions->GetHeadPosition();
      while(lPos)
      {
          SMILTransitionState* pState = (SMILTransitionState*)
            m_pActiveTransitions->GetAt(lPos);
          doTransition(lPos, pState->m_ulEndTime);
          lPos = m_pActiveTransitions->RemoveAt(lPos);
          HX_DELETE(pState);
      }
    }
}

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)

void CSmilDocumentRenderer::removeActiveAnimations()
{
    if(m_pActiveAnimations)
    {
        if (atLeastOneActiveAnimation(m_ulCurrentTime))
        {
            if (!isSiteCompositionModeON())
            {
                turnSiteCompositionModeON();
            }
        }
        // m_pAnimSiteRedrawMap is a list of sites
        // which need to ForceRedraw() as a result on an
        // animation on this time sync.
        if (!m_pAnimSiteRedrawMap)
        {
            m_pAnimSiteRedrawMap = new CHXMapPtrToPtr();
        }
        if (m_pAnimSiteRedrawMap)
        {
            m_pAnimSiteRedrawMap->RemoveAll();
        }
        // m_pAnimRegionRecomputeMap is a list of sites
        // which need to ForceRedraw() as a result on an
        // animation on this time sync.
        if (!m_pAnimRegionRecomputeMap)
        {
            m_pAnimRegionRecomputeMap = new CHXMapPtrToPtr();
        }
        if (m_pAnimRegionRecomputeMap)
        {
            m_pAnimRegionRecomputeMap->RemoveAll();
        }
        // m_pAnimTopLayoutMap is a collection of topLayout's
        // which have been animated programmatically and
        // need resizing.
        if (m_pAnimTopLayoutMap)
        {
            m_pAnimTopLayoutMap->RemoveAll();
        }
        // If there is a soundlevel animation going on, then
        // we need to try to grab the mutex
        if (m_pSoundLevelMutex) m_pSoundLevelMutex->Lock();
        // Clear the animate root layout flag
        m_bAnimateRootLayout = FALSE;
        LISTPOSITION lPos = m_pActiveAnimations->GetHeadPosition();
        while(lPos)
        {
            CSmilAnimateInfo* pInfo = (CSmilAnimateInfo*)
                m_pActiveAnimations->GetNext(lPos);
            // Restore the original value
            setValue(pInfo, pInfo->m_pUnder, pInfo->m_pDepend, TRUE);
            // XXXMEH - if this is a soundLevel animation, then
            // we need to turn off the animation
            if (pInfo->m_pSandwich->GetAttributeName() == kAttrNameSoundLevel)
            {
                finishSoundLevelAnimation(pInfo);
            }
            // Now we need to delete this info object
            HX_DELETE(pInfo);
        }
        m_pActiveAnimations->RemoveAll();
        // If there was a soundlevel animation going on, then
        // we can release the mutex
        if (m_pSoundLevelMutex) m_pSoundLevelMutex->Unlock();
        // Assign any new values
        // Recompute the root-layout if necessary
        if (m_bAnimateRootLayout && m_pRootLayout && m_pRootLayout->m_pRoot)
        {
            HXxSize cNewRootSize = {0, 0};
            cNewRootSize.cx = (INT32) floor(m_pRootLayout->m_pRoot->m_dWidth  + 0.5);
            cNewRootSize.cy = (INT32) floor(m_pRootLayout->m_pRoot->m_dHeight + 0.5);
            SiteSizeChanged(m_pRootLayout->m_pSite, &cNewRootSize);
        }
#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)
        // Animate any topLayout's
        if (m_pAnimTopLayoutMap &&
            m_pAnimTopLayoutMap->GetCount() > 0)
        {
            POSITION mpos = m_pAnimTopLayoutMap->GetStartPosition();
            while (mpos)
            {
                void* pKey = NULL;
                void* pVal = NULL;
                m_pAnimTopLayoutMap->GetNextAssoc(mpos, pKey, pVal);
                if (pKey)
                {
                    CSmilBasicViewport* pVP = (CSmilBasicViewport*) pKey;
                    if (pVP && pVP->m_pPort)
                    {
                        HXxSize cNewRootSize = {0, 0};
                        cNewRootSize.cx = (INT32) floor(pVP->m_pPort->m_dWidth  + 0.5);
                        cNewRootSize.cy = (INT32) floor(pVP->m_pPort->m_dHeight + 0.5);
                        SiteSizeChanged(pVP->m_pSite, &cNewRootSize);
                    }
                }
            }
            // Clear the map
            m_pAnimTopLayoutMap->RemoveAll();
        }
#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */
        // We've collected all the regions for which
        // we need to call recomputeBoxLayout()
        if (m_pAnimRegionRecomputeMap)
        {
            POSITION mpos = m_pAnimRegionRecomputeMap->GetStartPosition();
            while (mpos)
            {
                void* pKey = NULL;
                void* pVal = NULL;
                m_pAnimRegionRecomputeMap->GetNextAssoc(mpos, pKey, pVal);
                if (pKey)
                {
                    CSmilBasicRegion* pRegion = (CSmilBasicRegion*) pKey;
                    recomputeBoxLayout(pRegion, TRUE);
                }
            }
            // Clear the map
            m_pAnimRegionRecomputeMap->RemoveAll();
        }
        // We've collected all the sites which need
        // a ForceRedraw(), so do that now
        if (m_pAnimSiteRedrawMap)
        {
            POSITION mpos = m_pAnimSiteRedrawMap->GetStartPosition();
            while (mpos)
            {
                void* pKey = NULL;
                void* pVal = NULL;
                m_pAnimSiteRedrawMap->GetNextAssoc(mpos, pKey, pVal);
                if (pKey)
                {
                    IHXSite* pSite = (IHXSite*) pKey;
                    forceFullRedraw(pSite);
                }
            }
            // Clear the map
            m_pAnimSiteRedrawMap->RemoveAll();
        }
        unlockSiteComposition();
        bltSiteComposition();
        lockSiteComposition();
    }
    if (isSiteCompositionModeON())
    {
        turnSiteCompositionModeOFF();
    }
}

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

#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)

void CSmilDocumentRenderer::removeViewports()
{
    if (m_pViewportList)
    {
        LISTPOSITION pos = m_pViewportList->GetHeadPosition();
        while (pos)
        {
            CSmilBasicViewport* pPort =
                (CSmilBasicViewport*) m_pViewportList->GetNext(pos);
            HX_DELETE(pPort);
        }
        m_pViewportList->RemoveAll();
    }
}

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

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)

HX_RESULT CSmilDocumentRenderer::addAnimation(CAnimationSandwichLayer* pLayer)
{
    MLOG_ANIM(m_pErrorMessages,"addAnimation() targEl=%s m_ulCurrentTime=%lu tick=%lu\n",
              (const char*) pLayer->GetTargetElementID(),
              m_ulCurrentTime, HX_GET_BETTERTICKCOUNT());
    HX_RESULT retVal = HXR_OK;

    if (pLayer)
    {
        // If there is a soundlevel animation going on, then
        // we need to try to grab the mutex
        if (m_pSoundLevelMutex) m_pSoundLevelMutex->Lock();
        // If there isn't an active animation list, then
        // create one
        if (!m_pActiveAnimations)
        {
            m_pActiveAnimations = new CHXSimpleList();
        }
        if (m_pActiveAnimations)
        {
            // Run through the list and see if there are any
            // existing animations of this element's attribute.
            CSmilAnimateInfo* pInfo = NULL;
            BOOL              bNew  = TRUE;
            const char*       pszID = pLayer->GetTargetElementID();
            LISTPOSITION      pos   = m_pActiveAnimations->GetHeadPosition();
            while (pos)
            {
                CSmilAnimateInfo* pListInfo =
                    (CSmilAnimateInfo*) m_pActiveAnimations->GetNext(pos);
                if (pListInfo && pListInfo->m_pSandwich)
                {
                    const char* pszListID = pListInfo->m_pSandwich->GetTargetElementID();
                    if (pszID && pszListID)
                    {
                        if (!strcmp(pszID, pszListID) &&
                            pLayer->GetAttributeName() == pListInfo->m_pSandwich->GetAttributeName())
                        {
                            // This is the sandwich we're looking for
                            pInfo = pListInfo;
                            bNew  = FALSE;
                            break;
                        }
                    }
                }
            }
            // Are we already animating this attribute?
            if (bNew)
            {
                // We didn't find an existing CSmilAnimateInfo, so
                // we need to create one and put it on the list
                pInfo = new CSmilAnimateInfo();
                if (pInfo)
                {
                    // Create a sandwich
                    pInfo->m_pSandwich = new CAnimationSandwich(pLayer->GetTargetElementID(),
                                                                pLayer->GetTargetElementTag(),
                                                                pLayer->GetAttributeName());
                    if (pInfo->m_pSandwich)
                    {
                        // Add this layer to the sandwich
                        retVal = pInfo->m_pSandwich->AddLayer(pLayer);
                        if (SUCCEEDED(retVal))
                        {
                            // Save the underlying value
                            HX_DELETE(pInfo->m_pUnder);
                            retVal = getUnderlyingValue(pInfo, pInfo->m_pUnder);
                            if (SUCCEEDED(retVal))
                            {
                                HX_DELETE(pInfo->m_pDepend);
                                retVal = getDependentValue(pInfo, pInfo->m_pDepend);
                                if (SUCCEEDED(retVal))
                                {
                                    // Now put this on the list
                                    m_pActiveAnimations->AddTail((void*) pInfo);
                                    // XXMEH - if this is a soundLevel animation,
                                    // we have to tell the core to start calling us
                                    if (pLayer->GetAttributeName() == kAttrNameSoundLevel)
                                    {
                                        // First we need to create the mutex
                                        if (!m_pSoundLevelMutex && m_pParent)
                                        {
                                            IHXCommonClassFactory* pFactory =
                                                m_pParent->getFactory();
                                            if (pFactory)
                                            {
                                                pFactory->CreateInstance(CLSID_IHXMutex,
                                                                         (void**) &m_pSoundLevelMutex);
                                            }
                                            // If we just created the mutex, the
                                            // we need to lock it
                                            if (m_pSoundLevelMutex) m_pSoundLevelMutex->Lock();
                                        }
                                        // And now we can start the animation
                                        startSoundLevelAnimation(pInfo);
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        retVal = HXR_OUTOFMEMORY;
                    }
                }
                else
                {
                    retVal = HXR_OUTOFMEMORY;
                }
                if (FAILED(retVal))
                {
                    HX_DELETE(pInfo);
                }
            }
            else
            {
                // There was already an existing CSmilAnimateInfo, so 
                // we just need to add this layer to it
                if (pInfo && pInfo->m_pSandwich)
                {
                    // Is this a soundLevel animation?
                    if (pLayer->GetAttributeName() == kAttrNameSoundLevel)
                    {
                        // We are about to add a layer to an existing
                        // soundLevel animation. Since soundLevel animations
                        // get their information about 2s ahead of time, then
                        // we need to stop this animation and cause another
                        // rewind by starting a new animation. So first
                        // we need to stop this current animation. Note
                        // that we set bUseCurrentLevel to TRUE so that
                        // we will end the soundLevel animation with
                        // the value at the current level instead of
                        // reverting to the animation's underlying value.
                        finishSoundLevelAnimation(pInfo, TRUE);
                    }
                    retVal = pInfo->m_pSandwich->AddLayer(pLayer);
                    if (SUCCEEDED(retVal))
                    {
                        // Is this a soundLevel animation?
                        if (pLayer->GetAttributeName() == kAttrNameSoundLevel)
                        {
                            // If this is a soundLevel animation, then we have
                            // just added an additional layer to an existing
                            // soundLevel animation. Therefore, we should
                            // restart a soundLevel animation.
                            startSoundLevelAnimation(pInfo);
                        }
                    }
                }
                else
                {
                    retVal = HXR_FAIL;
                }
            }
        }
        else
        {
            retVal = HXR_OUTOFMEMORY;
        }
        // If there is a soundlevel animation going on,
        // then release the mutex
        if (m_pSoundLevelMutex) m_pSoundLevelMutex->Unlock();
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT CSmilDocumentRenderer::removeAnimation(CSmilAnimateElement* pAnimate)
{
    HX_RESULT retVal = HXR_OK;

    if (pAnimate)
    {
        // If there is a soundlevel animation going on, then
        // we need to try to grab the mutex
        if (m_pSoundLevelMutex) m_pSoundLevelMutex->Lock();
        // Does this animation begin on an event?
        BOOL bBeginOnEvent = pAnimate->hasEventBasedBeginTime();
        // Does this animation end on an event?
        BOOL bEndOnEvent = pAnimate->hasEventBasedEndTime();
        // If we end on an event AND we have fill="freeze"
        // behavior, then we don't want to end, but rather
        // shorten the active duration to be the current time.
        // Then we will automatically go into a freeze state.
        if (bEndOnEvent &&
            pAnimate->m_eActualFill == FillFreeze)
        {
            // Find the right animation layer
            LISTPOSITION pos = m_pActiveAnimations->GetHeadPosition();
            while (pos)
            {
                CSmilAnimateInfo* pInfo =
                    (CSmilAnimateInfo*) m_pActiveAnimations->GetNext(pos);
                if (pInfo &&
                    pInfo->m_pSandwich &&
                    pInfo->m_pSandwich->MatchingSandwich(pAnimate))
                {
                    // Freeze all layers in this sandwich which come
                    // from this animate element and are currently active.
                    pInfo->m_pSandwich->FreezeLayers(pAnimate->m_pNode->m_id, m_ulCurrentTime);
                }
            }
        }
        else if (!bBeginOnEvent ||
                 (bEndOnEvent && pAnimate->m_eActualFill == FillRemove))
        {
            if (m_pActiveAnimations &&
                m_pActiveAnimations->GetCount() > 0)
            {
                if (!isSiteCompositionModeON())
                {
                    turnSiteCompositionModeON();
                }
                // m_pAnimSiteRedrawMap is a list of sites
                // which need to ForceRedraw() as a result on an
                // animation on this time sync.
                if (!m_pAnimSiteRedrawMap)
                {
                    m_pAnimSiteRedrawMap = new CHXMapPtrToPtr();
                }
                if (m_pAnimSiteRedrawMap)
                {
                    m_pAnimSiteRedrawMap->RemoveAll();
                }
                // m_pAnimRegionRecomputeMap is a list of sites
                // which need to ForceRedraw() as a result on an
                // animation on this time sync.
                if (!m_pAnimRegionRecomputeMap)
                {
                    m_pAnimRegionRecomputeMap = new CHXMapPtrToPtr();
                }
                if (m_pAnimRegionRecomputeMap)
                {
                    m_pAnimRegionRecomputeMap->RemoveAll();
                }
                // m_pAnimTopLayoutMap is a collection of topLayout's
                // which have been animated programmatically and
                // need resizing.
                if (m_pAnimTopLayoutMap)
                {
                    m_pAnimTopLayoutMap->RemoveAll();
                }
                // Clear the animate root layout flag
                m_bAnimateRootLayout = FALSE;
                // Run through the active animations
                LISTPOSITION pos = m_pActiveAnimations->GetHeadPosition();
                while (pos)
                {
                    CSmilAnimateInfo* pInfo =
                        (CSmilAnimateInfo*) m_pActiveAnimations->GetAt(pos);
                    if (pInfo && pInfo->m_pSandwich &&
                        pInfo->m_pSandwich->MatchingSandwich(pAnimate))
                    {
                        // Remove this animate element from this sandwich. 
                        pInfo->m_pSandwich->RemoveLayer(pAnimate->m_pNode->m_id);
                        // Are there any layers left in this sandwich?
                        if (pInfo->m_pSandwich->GetNumLayers())
                        {
                            m_pActiveAnimations->GetNext(pos);
                        }
                        else
                        {
                            // There are no more layers left in the
                            // sandwich, so this animation is done. So,
                            // we need to restore whatever original values
                            // were present
                            setValue(pInfo, pInfo->m_pUnder, pInfo->m_pDepend, TRUE);
                            // XXXMEH - if this is a soundLevel animation, then
                            // we need to turn off the animation
                            if (pInfo->m_pSandwich->GetAttributeName() == kAttrNameSoundLevel)
                            {
                                finishSoundLevelAnimation(pInfo);
                            }
                            // Now we need to delete this info object
                            HX_DELETE(pInfo);
                            // And remove it from the list
                            // We have to do the following hack since
                            // CHXSimpleList does not advance the position to 
                            // NULL if you RemoveAt() the last element in the list.
                            BOOL bLast = (m_pActiveAnimations->GetAt(pos) ==
                                          m_pActiveAnimations->GetTail());
                            pos        = m_pActiveAnimations->RemoveAt(pos);
                            if (bLast && pos)
                            {
                                m_pActiveAnimations->GetNext(pos);
                            }
                        }
                    }
                    else
                    {
                        m_pActiveAnimations->GetNext(pos);
                    }
                }
                // Recompute the root-layout if necessary
                if (m_bAnimateRootLayout && m_pRootLayout && m_pRootLayout->m_pRoot)
                {
                    HXxSize cNewRootSize = {0, 0};
                    cNewRootSize.cx = (INT32) floor(m_pRootLayout->m_pRoot->m_dWidth  + 0.5);
                    cNewRootSize.cy = (INT32) floor(m_pRootLayout->m_pRoot->m_dHeight + 0.5);
                    SiteSizeChanged(m_pRootLayout->m_pSite, &cNewRootSize);
                }
#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)
                // Animate any topLayout's
                if (m_pAnimTopLayoutMap &&
                    m_pAnimTopLayoutMap->GetCount() > 0)
                {
                    POSITION mpos = m_pAnimTopLayoutMap->GetStartPosition();
                    while (mpos)
                    {
                        void* pKey = NULL;
                        void* pVal = NULL;
                        m_pAnimTopLayoutMap->GetNextAssoc(mpos, pKey, pVal);
                        if (pKey)
                        {
                            CSmilBasicViewport* pVP = (CSmilBasicViewport*) pKey;
                            if (pVP && pVP->m_pPort)
                            {
                                HXxSize cNewRootSize = {0, 0};
                                cNewRootSize.cx = (INT32) floor(pVP->m_pPort->m_dWidth  + 0.5);
                                cNewRootSize.cy = (INT32) floor(pVP->m_pPort->m_dHeight + 0.5);
                                SiteSizeChanged(pVP->m_pSite, &cNewRootSize);
                            }
                        }
                    }
                    // Clear the map
                    m_pAnimTopLayoutMap->RemoveAll();
                }
#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */
                // We've collected all the regions for which
                // we need to call recomputeBoxLayout()
                if (m_pAnimRegionRecomputeMap)
                {
                    POSITION mpos = m_pAnimRegionRecomputeMap->GetStartPosition();
                    while (mpos)
                    {
                        void* pKey = NULL;
                        void* pVal = NULL;
                        m_pAnimRegionRecomputeMap->GetNextAssoc(mpos, pKey, pVal);
                        if (pKey)
                        {
                            CSmilBasicRegion* pRegion = (CSmilBasicRegion*) pKey;
                            recomputeBoxLayout(pRegion, TRUE);
                        }
                    }
                    // Clear the map
                    m_pAnimRegionRecomputeMap->RemoveAll();
                }
                // We've collected all the sites which need
                // a ForceRedraw(), so do that now
                if (m_pAnimSiteRedrawMap)
                {
                    POSITION mpos = m_pAnimSiteRedrawMap->GetStartPosition();
                    while (mpos)
                    {
                        void* pKey = NULL;
                        void* pVal = NULL;
                        m_pAnimSiteRedrawMap->GetNextAssoc(mpos, pKey, pVal);
                        if (pKey)
                        {
                            IHXSite* pSite = (IHXSite*) pKey;
                            forceFullRedraw(pSite);
                        }
                    }
                    // Clear the map
                    m_pAnimSiteRedrawMap->RemoveAll();
                }
                unlockSiteComposition();
                bltSiteComposition();
                lockSiteComposition();
            }
            if (!m_pActiveAnimations ||
                (m_pActiveAnimations && m_pActiveAnimations->GetCount() == 0))
            {
                if (isSiteCompositionModeON())
                {
                    turnSiteCompositionModeOFF();
                }
            }
        }
        // If there is a soundlevel animation going on,
        // then release the mutex
        if (m_pSoundLevelMutex) m_pSoundLevelMutex->Unlock();
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

void CSmilDocumentRenderer::removeAnimation(CSmilAnimateInfo* pInfo)
{
    if (pInfo && m_pActiveAnimations)
    {
        // If there is a soundlevel animation going on, then
        // we need to try to grab the mutex
        if (m_pSoundLevelMutex) m_pSoundLevelMutex->Lock();
        // Now we can remove the animation
        LISTPOSITION pos = m_pActiveAnimations->GetHeadPosition();
        while (pos)
        {
            CSmilAnimateInfo* pLInfo =
                (CSmilAnimateInfo*) m_pActiveAnimations->GetAt(pos);
            if (pInfo == pLInfo)
            {
                m_pActiveAnimations->RemoveAt(pos);
                break;
            }
            else
            {
                m_pActiveAnimations->GetNext(pos);
            }
        }
        // If there is a soundlevel animation going on,
        // then release the mutex
        if (m_pSoundLevelMutex) m_pSoundLevelMutex->Unlock();
    }
}

void CSmilDocumentRenderer::doAnimation(CSmilAnimateInfo* pInfo, UINT32 ulTime)
{
    if (pInfo && pInfo->m_pSandwich && pInfo->m_pUnder)
    {
        // Compute the animation value
        CAttr cRet = pInfo->m_pSandwich->GetValue(ulTime, pInfo->m_pUnder, pInfo->m_pDepend);
        MLOG_ANIM(m_pErrorMessages,"doAnimation(,%lu) cRet=(%5.1lf,%5.1lf,%5.1lf,%5.1lf)\n",
                  ulTime,
                  cRet.GetValueDouble(0),
                  cRet.GetValueDouble(1),
                  cRet.GetValueDouble(2),
                  cRet.GetValueDouble(3));
        // Adjust the sandwich
        pInfo->m_pSandwich->AdjustLayers(ulTime);
        // Apply the resulting value
        setValue(pInfo, &cRet, pInfo->m_pDepend,
                 (pInfo->m_pSandwich->GetNumLayers() ? FALSE : TRUE));
    }
}

HX_RESULT CSmilDocumentRenderer::getUnderlyingValue(CSmilAnimateInfo* pInfo,
                                                    REF(CAttr*)       rpValue)
{
    HX_RESULT retVal = HXR_OK;

    if (pInfo && pInfo->m_pSandwich)
    {
        // Get the target element id
        const char* pszID = pInfo->m_pSandwich->GetTargetElementID();
        // Get the target element tag
        SMILNodeTag eTag = pInfo->m_pSandwich->GetTargetElementTag();
        // Get the attribute name
        UINT32 ulAttr = pInfo->m_pSandwich->GetAttributeName();
        // Depending on the type of target element, we will
        // go lookup a different object
        if (eTag == SMILRegion)
        {
            // Get the region
            CSmilBasicRegion* pRegion = getRegionByID(pszID);
            if (pRegion)
            {
                double   dVal[4]  = {0.0, 0.0, 0.0, 0.0};
                CSS2Type eType[4] = {CSS2TypeAuto, CSS2TypeAuto,
                                     CSS2TypeAuto, CSS2TypeAuto};
                switch (ulAttr)
                {
                    case kAttrNameSoundLevel:
                        dVal[0] = pRegion->m_dSoundLevel;
                        break;
                    case kAttrNameZIndex:
                        dVal[0] = pRegion->m_lZIndex;
                        break;
                    case kAttrNameBackgroundColor:
                        {
                            UINT32 ulColor = pRegion->m_ulBackgroundColor;
                            dVal[0] = ((ulColor & 0x00FF0000) >> 16); // R
                            dVal[1] = ((ulColor & 0x0000FF00) >>  8); // G
                            dVal[2] =  (ulColor & 0x000000FF);        // B
                            dVal[3] = ((ulColor & 0xFF000000) >> 24); // A
                        }
                        break;
                    case kAttrNameLeft:
                    case kAttrNameTop:
                        // Note that we have to put both left and
                        // top in the same underlying value, since 
                        // an <animateMotion> could be inside the sandwich
                        // for left or top.
                        dVal[0]  = pRegion->m_LayoutRect.m_dLeft;
                        eType[0] = pRegion->m_LayoutRect.m_eLeftType;
                        dVal[1]  = pRegion->m_LayoutRect.m_dTop;
                        eType[1] = pRegion->m_LayoutRect.m_eTopType;
                        break;
                    case kAttrNameRight:
                        dVal[0]  = pRegion->m_LayoutRect.m_dRight;
                        eType[0] = pRegion->m_LayoutRect.m_eRightType;
                        break;
                    case kAttrNameBottom:
                        dVal[0]  = pRegion->m_LayoutRect.m_dBottom;
                        eType[0] = pRegion->m_LayoutRect.m_eBottomType;
                        break;
                    case kAttrNameWidth:
                        dVal[0]  = pRegion->m_LayoutRect.m_dWidth;
                        eType[0] = pRegion->m_LayoutRect.m_eWidthType;
                        break;
                    case kAttrNameHeight:
                        dVal[0]  = pRegion->m_LayoutRect.m_dHeight;
                        eType[0] = pRegion->m_LayoutRect.m_eHeightType;
                        break;
                }
                // Create the atttribute
                HX_DELETE(rpValue);
                rpValue = new CAttr(ulAttr,
                                    dVal[0], eType[0], dVal[1], eType[1],
                                    dVal[2], eType[2], dVal[3], eType[3]);
                if (!rpValue)
                {
                    retVal = HXR_OUTOFMEMORY;
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
        else if (eTag == SMILAnchor)
        {
            // Find the CSmilAnchorElement object
            CSmilAnchorElement* pAnchor =
                m_pSmilParser->getAnchorOrAreaElement(pInfo->m_pSandwich->GetTargetElementID());
            if (pAnchor && pAnchor->m_pNode && pAnchor->m_pNode->m_pValues)
            {
                IHXBuffer* pCoords = NULL;
                retVal = pAnchor->m_pNode->m_pValues->GetPropertyCString("coords", pCoords);
                if (SUCCEEDED(retVal))
                {
                    HX_DELETE(rpValue);
                    rpValue = new CAttr(kAttrNameCoords,
                                        (const char*) pCoords->GetBuffer());
                    if (!rpValue)
                    {
                        retVal = HXR_OUTOFMEMORY;
                    }
                }
                HX_RELEASE(pCoords);
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
        else if (eTag == SMILText       ||
                 eTag == SMILImg        ||
                 eTag == SMILRef        ||
                 eTag == SMILAudio      ||
                 eTag == SMILVideo      ||
                 eTag == SMILAnimation  ||
                 eTag == SMILTextstream ||
                 eTag == SMILBrush)
        {
            // Get the source
            CSmilSource* pSource = getSource(pszID);
            if (pSource)
            {
                double   dVal[4]  = {0.0, 0.0, 0.0, 0.0};
                CSS2Type eType[4] = {CSS2TypeAuto, CSS2TypeAuto,
                                     CSS2TypeAuto, CSS2TypeAuto};
                switch (ulAttr)
                {
                    case kAttrNameLeft:
                    case kAttrNameTop:
                        // Note that we have to put both left and
                        // top in the same underlying value, since 
                        // an <animateMotion> could be inside the sandwich
                        // for left or top.
                        dVal[0]  = pSource->m_Rect.m_dLeft;
                        eType[0] = pSource->m_Rect.m_eLeftType;
                        dVal[1]  = pSource->m_Rect.m_dTop;
                        eType[1] = pSource->m_Rect.m_eTopType;
                        break;
                    case kAttrNameRight:
                        dVal[0]  = pSource->m_Rect.m_dRight;
                        eType[0] = pSource->m_Rect.m_eRightType;
                        break;
                    case kAttrNameBottom:
                        dVal[0]  = pSource->m_Rect.m_dBottom;
                        eType[0] = pSource->m_Rect.m_eBottomType;
                        break;
                    case kAttrNameWidth:
                        dVal[0]  = pSource->m_Rect.m_dWidth;
                        eType[0] = pSource->m_Rect.m_eWidthType;
                        break;
                    case kAttrNameHeight:
                        dVal[0]  = pSource->m_Rect.m_dHeight;
                        eType[0] = pSource->m_Rect.m_eHeightType;
                        break;
                    case kAttrNameZIndex:
                        dVal[0] = pSource->m_lZIndex;
                        break;
                    case kAttrNameBackgroundColor:
                        {
                            UINT32 ulColor = pSource->m_ulBackgroundColor;
                            dVal[0] = ((ulColor & 0x00FF0000) >> 16); // R
                            dVal[1] = ((ulColor & 0x0000FF00) >>  8); // G
                            dVal[2] =  (ulColor & 0x000000FF);        // B
                            dVal[3] = ((ulColor & 0xFF000000) >> 24); // A
                        }
                        break;
                    case kAttrNameColor:
                        {
                            UINT32 ulColor = pSource->m_ulColor;
                            dVal[0] = ((ulColor & 0x00FF0000) >> 16); // R
                            dVal[1] = ((ulColor & 0x0000FF00) >>  8); // G
                            dVal[2] =  (ulColor & 0x000000FF);        // B
                            dVal[3] = ((ulColor & 0xFF000000) >> 24); // A
                        }
                        break;
                    case kAttrNameMediaOpacity:
                        {
                            dVal[0] = (double) pSource->m_ulMediaOpacity;
                        }
                        break;
                    case kAttrNameBackgroundOpacity:
                        {
                            dVal[0] = (double) pSource->m_ulBackgroundOpacity;
                        }
                        break;
                }
                // Create the atttribute
                HX_DELETE(rpValue);
                rpValue = new CAttr(ulAttr,
                                   dVal[0], eType[0], dVal[1], eType[1],
                                   dVal[2], eType[2], dVal[3], eType[3]);
                if (!rpValue)
                {
                    retVal = HXR_OUTOFMEMORY;
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
        else if (eTag == SMILRootLayout)
        {
            if (m_pRootLayout && m_pRootLayout->m_pRoot)
            {
                double   dVal[4]  = {0.0, 0.0, 0.0, 0.0};
                CSS2Type eType[4] = {CSS2TypeAuto, CSS2TypeAuto,
                                     CSS2TypeAuto, CSS2TypeAuto};
                switch (ulAttr)
                {
                    case kAttrNameWidth:
                        dVal[0]  = m_pRootLayout->m_pRoot->m_dWidth;
                        eType[0] = m_pRootLayout->m_pRoot->m_eWidthType;
                        break;
                    case kAttrNameHeight:
                        dVal[0]  = m_pRootLayout->m_pRoot->m_dHeight;
                        eType[0] = m_pRootLayout->m_pRoot->m_eHeightType;
                        break;
                }
                // Create the atttribute
                HX_DELETE(rpValue);
                rpValue = new CAttr(ulAttr,
                                    dVal[0], eType[0], dVal[1], eType[1],
                                    dVal[2], eType[2], dVal[3], eType[3]);
                if (!rpValue)
                {
                    retVal = HXR_OUTOFMEMORY;
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)
        else if (eTag == SMILViewport)
        {
            CSmilBasicViewport* pPort = getViewport(pszID);
            if (pPort && pPort->m_pPort)
            {
                double   dVal[4]  = {0.0, 0.0, 0.0, 0.0};
                CSS2Type eType[4] = {CSS2TypeAuto, CSS2TypeAuto,
                                     CSS2TypeAuto, CSS2TypeAuto};
                switch (ulAttr)
                {
                    case kAttrNameWidth:
                        dVal[0]  = pPort->m_pPort->m_dWidth;
                        eType[0] = pPort->m_pPort->m_eWidthType;
                        break;
                    case kAttrNameHeight:
                        dVal[0]  = pPort->m_pPort->m_dHeight;
                        eType[0] = pPort->m_pPort->m_eHeightType;
                        break;
                }
                // Create the atttribute
                HX_DELETE(rpValue);
                rpValue = new CAttr(ulAttr,
                                    dVal[0], eType[0], dVal[1], eType[1],
                                    dVal[2], eType[2], dVal[3], eType[3]);
                if (!rpValue)
                {
                    retVal = HXR_OUTOFMEMORY;
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */
        else if (eTag == SMILParam)
        {
            if (m_pSmilParser)
            {
                CSmilElement* pEl = m_pSmilParser->findElement(pszID);
                if (pEl && pEl->m_pNode && pEl->m_pNode->m_tag == SMILParam)
                {
                    // Cast to a CSmilParamElement
                    CSmilParamElement* pParam = (CSmilParamElement*) pEl;
                    // Make sure we have a value attribute
                    if (pParam->m_pValue)
                    {
                        // Create the atttribute
                        HX_DELETE(rpValue);
                        rpValue = new CAttr(ulAttr,
                                            (const char*) pParam->m_pValue->GetBuffer());
                        if (!rpValue)
                        {
                            retVal = HXR_OUTOFMEMORY;
                        }
                    }
                }
                else
                {
                    retVal = HXR_FAIL;
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
    }

    return retVal;
}

HX_RESULT CSmilDocumentRenderer::getDependentValue(CSmilAnimateInfo* pInfo,
                                                   REF(CAttr*)       rpDepend)
{
    HX_RESULT retVal = HXR_OK;

    if (pInfo && pInfo->m_pSandwich)
    {
        // Clear any existing attribute
        HX_DELETE(rpDepend);
        // Get the target element id
        const char* pszID = pInfo->m_pSandwich->GetTargetElementID();
        // Get the target element tag
        SMILNodeTag eTag = pInfo->m_pSandwich->GetTargetElementTag();
        // Get the attribute name
        UINT32 ulAttr = pInfo->m_pSandwich->GetAttributeName();
        // Depending on the type of target element, we will
        // go lookup a different object
        if (eTag == SMILRegion)
        {
            // We need to compute the size of this region's parent.
            HXxSize           cParentSize = {0, 0};
            CSmilBasicRegion* pRegion     = getRegionByID(pszID);
            if (pRegion && pRegion->m_pParent)
            {
                cParentSize.cx = HXxRECT_WIDTH(pRegion->m_pParent->m_Rect);
                cParentSize.cy = HXxRECT_HEIGHT(pRegion->m_pParent->m_Rect);
            }
            double   dVal[2]  = {0.0, 0.0};
            CSS2Type eType[2] = {CSS2TypeAuto, CSS2TypeAuto};
            BOOL   bCreate = TRUE;
            switch (ulAttr)
            {
                case kAttrNameLeft:
                case kAttrNameTop:
                    // Note that we have to put both left and
                    // top in the same depenent value, since 
                    // an <animateMotion> could be inside the sandwich
                    // for left or top.
                    dVal[0]  = cParentSize.cx;
                    dVal[1]  = cParentSize.cy;
                    eType[0] = eType[1] = CSS2TypeLength;
                    break;
                case kAttrNameRight:
                case kAttrNameWidth:
                    dVal[0]  = cParentSize.cx;
                    eType[0] = CSS2TypeLength;
                    break;
                case kAttrNameBottom:
                case kAttrNameHeight:
                    dVal[0]  = cParentSize.cy;
                    eType[0] = CSS2TypeLength;
                    break;
                default:
                    // We only need a dependent value for
                    // position and length attributes
                    bCreate = FALSE;
                    break;
            }
            // Was this an attribute for which we need
            // to create a dependent value?
            if (bCreate)
            {
                // Create the atttribute
                HX_DELETE(rpDepend);
                rpDepend = new CAttr(ulAttr, dVal[0], eType[0], dVal[1], eType[1]);
                if (!rpDepend)
                {
                    retVal = HXR_OUTOFMEMORY;
                }
            }
        }
        else if (eTag == SMILText       ||
                 eTag == SMILImg        ||
                 eTag == SMILRef        ||
                 eTag == SMILAudio      ||
                 eTag == SMILVideo      ||
                 eTag == SMILAnimation  ||
                 eTag == SMILTextstream ||
                 eTag == SMILBrush)
        {
            // Get the source
            CSmilSource* pSource = getSource(pszID);
            if (pSource)
            {
                // Get the region this is playing to
                CSmilBasicRegion* pRegion = getRegionByID((const char*) pSource->m_region);
                if (pRegion)
                {
                    HXxSize cRegionSize = {HXxRECT_WIDTH(pRegion->m_Rect),
                                           HXxRECT_HEIGHT(pRegion->m_Rect)};
                    double   dVal[3]  = {0.0, 0.0};
                    CSS2Type eType[3] = {CSS2TypeAuto, CSS2TypeAuto};
                    BOOL   bCreate    = TRUE;
                    switch (ulAttr)
                    {
                        case kAttrNameLeft:
                        case kAttrNameTop:
                            // Note that we have to put both left and
                            // top in the same depenent value, since 
                            // an <animateMotion> could be inside the sandwich
                            // for left or top.
                            dVal[0]  = cRegionSize.cx;
                            dVal[1]  = cRegionSize.cy;
                            eType[0] = eType[1] = CSS2TypeLength;
                            break;
                        case kAttrNameRight:
                        case kAttrNameWidth:
                            dVal[0]  = cRegionSize.cx;
                            eType[0] = CSS2TypeLength;
                            break;
                        case kAttrNameBottom:
                        case kAttrNameHeight:
                            dVal[0]  = cRegionSize.cy;
                            eType[0] = CSS2TypeLength;
                            break;
                        default:
                            // We only need a dependent value for
                            // position and length attributes
                            bCreate = FALSE;
                            break;
                    }
                    // Was this an attribute for which we need
                    // to create a dependent value?
                    if (bCreate)
                    {
                        // Create the atttribute
                        HX_DELETE(rpDepend);
                        rpDepend = new CAttr(ulAttr, dVal[0], eType[0], dVal[1], eType[1]);
                        if (!rpDepend)
                        {
                            retVal = HXR_OUTOFMEMORY;
                        }
                    }
                }
                else
                {
                    retVal = HXR_FAIL;
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT CSmilDocumentRenderer::setValue(CSmilAnimateInfo* pInfo, CAttr* pValue,
                                          CAttr* pDepend, BOOL bUnderlyingValue)
{
    HX_RESULT retVal = HXR_OK;

    if (pInfo && pInfo->m_pSandwich && pValue)
    {
        // Get the target element id
        const char* pszID = pInfo->m_pSandwich->GetTargetElementID();
        // Get the target element tag
        SMILNodeTag eTag = pInfo->m_pSandwich->GetTargetElementTag();
        // Get the attribute name
        UINT32 ulAttr = pInfo->m_pSandwich->GetAttributeName();
        // Depending on the type of target element, we will
        // go lookup a different object
        if (eTag == SMILRegion)
        {
            CSmilBasicRegion* pRegion = getRegionByID(pszID);
            if (pRegion)
            {
                switch (ulAttr)
                {
                    case kAttrNameSoundLevel:
                        {
                            // XXXMEH - set the sound level - the core
                            // will call us back to get this value
                            pRegion->m_dSoundLevel = pValue->GetValueDouble();
                        }
                        break;
                    case kAttrNameZIndex:
                        {
                            // Get the computed z-index value
                            INT32 lZIndex = (INT32) (pValue->GetValueDouble() + 0.5);
                            // Set the z-index value in the CSmilBasicRegion
                            pRegion->m_lZIndex = lZIndex;
                            // XXXMEH - animation optimization
                            // Go ahead and resolve z-order here, but this 
                            // could be done more efficiently by collecting
                            // changes until the end.
                            resolveZOrder(getTopLevelBox(pRegion), m_ulCurrentTime);
                        }
                        break;
                    case kAttrNameBackgroundColor:
                        {
                            // Get the color
                            UINT32 ulRed   = (UINT32) pValue->GetValueDouble(0);
                            UINT32 ulGreen = (UINT32) pValue->GetValueDouble(1);
                            UINT32 ulBlue  = (UINT32) pValue->GetValueDouble(2);
                            UINT32 ulAlpha = (UINT32) pValue->GetValueDouble(3);
                            UINT32 ulColor = (ulAlpha << 24) | (ulRed << 16) |
                                             (ulGreen <<  8) | ulBlue;
                            // Set this color into the region
                            pRegion->m_ulBackgroundColor = ulColor;
                            // Set this color into the site user
                            if (pRegion->m_pSiteUser)
                            {
                                // Get the CSmilSiteUser
                                CSmilSiteUser* pUser = (CSmilSiteUser*) pRegion->m_pSiteUser;
                                // Get the current background color
                                UINT32 ulCurColor = pUser->GetBackgroundColor();
                                // If it's different from the current color, then
                                // change it
                                if (ulColor != ulCurColor)
                                {
                                    // Set the background color
                                    pUser->SetBackgroundColor(pRegion->m_ulBackgroundColor);
                                    // Queue the site for redraw
                                    queueSiteForAnimationRedraw(pRegion->m_pSite);
#if 0
                                    // XXXMEH - TEST - this is a further optimization, but
                                    // doesn't seem to be working due to a site bug, so ifdef
                                    // it out for now
                                    // Check to see if the "SiteNeverBlts" property
                                    // of the site needs to get updated
                                    if (isTransparent(ulColor) &&
                                        !isTransparent(ulCurColor))
                                    {
                                        setSiteProperty(pRegion->m_pSite, "SiteNeverBlts", "1");
                                    }
                                    else if (!isTransparent(ulColor) &&
                                             isTransparent(ulCurColor))
                                    {
                                        setSiteProperty(pRegion->m_pSite, "SiteNeverBlts", "0");
                                    }
#endif
                                }
                            }
                        }
                        break;
                    case kAttrNameLeft:
                        pRegion->m_LayoutRect.m_dLeft       = pValue->GetValueDouble(0);
                        pRegion->m_LayoutRect.m_eLeftType   = pValue->GetCSS2Type(0);
                        queueRegionForRecompute(pRegion);
                        break;
                    case kAttrNameTop:
                        pRegion->m_LayoutRect.m_dTop        = pValue->GetValueDouble(1);
                        pRegion->m_LayoutRect.m_eTopType    = pValue->GetCSS2Type(1);
                        queueRegionForRecompute(pRegion);
                        break;
                    case kAttrNameRight:
                        pRegion->m_LayoutRect.m_dRight      = pValue->GetValueDouble(0);
                        pRegion->m_LayoutRect.m_eRightType  = pValue->GetCSS2Type(0);
                        queueRegionForRecompute(pRegion);
                        break;
                    case kAttrNameBottom:
                        pRegion->m_LayoutRect.m_dBottom     = pValue->GetValueDouble(0);
                        pRegion->m_LayoutRect.m_eBottomType = pValue->GetCSS2Type(0);
                        queueRegionForRecompute(pRegion);
                        break;
                    case kAttrNameWidth:
                        pRegion->m_LayoutRect.m_dWidth      = pValue->GetValueDouble(0);
                        pRegion->m_LayoutRect.m_eWidthType  = pValue->GetCSS2Type(0);
                        queueRegionForRecompute(pRegion);
                        break;
                    case kAttrNameHeight:
                        pRegion->m_LayoutRect.m_dHeight     = pValue->GetValueDouble(0);
                        pRegion->m_LayoutRect.m_eHeightType = pValue->GetCSS2Type(0);
                        queueRegionForRecompute(pRegion);
                        break;
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
        else if (eTag == SMILAnchor)
        {
            // Find the CSmilAnchorElement object
            CSmilAnchorElement* pAnchor =
                m_pSmilParser->getAnchorOrAreaElement(pInfo->m_pSandwich->GetTargetElementID());
            if (pAnchor)
            {
                // Get the string value we want to set it to
                const char* pszCoords = pValue->GetValueString();
                if (pszCoords)
                {
                    // Parse this string value
                    retVal = m_pSmilParser->parseAnchorCoords(pszCoords, pAnchor);
                }
                else
                {
                    retVal = HXR_FAIL;
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
        else if (eTag == SMILText       ||
                 eTag == SMILImg        ||
                 eTag == SMILRef        ||
                 eTag == SMILAudio      ||
                 eTag == SMILVideo      ||
                 eTag == SMILAnimation  ||
                 eTag == SMILTextstream ||
                 eTag == SMILBrush)
        {
            if (ulAttr == kAttrNameLeft   ||
                ulAttr == kAttrNameTop    ||
                ulAttr == kAttrNameRight  ||
                ulAttr == kAttrNameBottom ||
                ulAttr == kAttrNameWidth  ||
                ulAttr == kAttrNameHeight)
            {
                // Get the source
                CSmilSource* pSource = getSource(pszID);
                if (pSource)
                {
                    // Set the attribute value
                    switch (ulAttr)
                    {
                        case kAttrNameLeft:
                            pSource->m_Rect.m_dLeft       = pValue->GetValueDouble();
                            pSource->m_Rect.m_eLeftType   = pValue->GetCSS2Type();
                            break;
                        case kAttrNameTop:
                            pSource->m_Rect.m_dTop        = pValue->GetValueDouble(1);
                            pSource->m_Rect.m_eTopType    = pValue->GetCSS2Type(1);
                            break;
                        case kAttrNameRight:
                            pSource->m_Rect.m_dRight      = pValue->GetValueDouble();
                            pSource->m_Rect.m_eRightType  = pValue->GetCSS2Type();
                            break;
                        case kAttrNameBottom:
                            pSource->m_Rect.m_dBottom     = pValue->GetValueDouble();
                            pSource->m_Rect.m_eBottomType = pValue->GetCSS2Type();
                            break;
                        case kAttrNameWidth:
                            pSource->m_Rect.m_dWidth      = pValue->GetValueDouble();
                            pSource->m_Rect.m_eWidthType  = pValue->GetCSS2Type();
                            break;
                        case kAttrNameHeight:
                            pSource->m_Rect.m_dHeight     = pValue->GetValueDouble();
                            pSource->m_Rect.m_eHeightType = pValue->GetCSS2Type();
                            break;
                    }
                    // XXXMEH - animation optimization
                    // We will go ahead and copy the new values to the site
                    // watcher, although it would be more efficient to collect
                    // the changes to the end and execute them together.
                    CSmilSiteWatcher* pWatcher = getRendererSiteWatcher(pszID);
                    if (pWatcher)
                    {
                        // Set the layout rect
                        pWatcher->SetSubRegionRect(pSource->m_Rect);
                        // Get the renderer site
                        IHXSite* pSite = NULL;
                        getRendererSite(pszID, pSite);
                        if (pSite)
                        {
                            // Re-position the site. It doesn't matter what
                            // position we pass in, because the site watcher's
                            // ChangingPosition() method will override it.
                            HXxPoint cPos = {1, 1};
                            pSite->SetPosition(cPos);
                            // Re-size the site. It doesn't matter what
                            // size we pass in, because the site watcher's
                            // ChangingSize() method will override it.
                            HXxSize cSize = {1, 1};
                            pSite->SetSize(cSize);
                        }
                        HX_RELEASE(pSite);
                    }
                }
                else
                {
                    retVal = HXR_FAIL;
                }
            }
            else if (ulAttr == kAttrNameZIndex ||
                     ulAttr == kAttrNameBackgroundColor)
            {
                // Get the source
                CSmilSource* pSource = getSource(pszID);
                if (pSource)
                {
                    // Get the region this is playing to
                    CSmilBasicRegion* pRegion = getRegionByID((const char*) pSource->m_region);
                    if (pRegion)
                    {
                        if (ulAttr == kAttrNameZIndex)
                        {
                            // Get the value to set
                            INT32 lZIndex = (INT32) (pValue->GetValueDouble() + 0.5);
                            // Set the z-index value in the CSmilBasicRegion
                            pRegion->m_lZIndex = lZIndex;
                            // XXXMEH - animation optimization
                            // Re-evaluate all the z-indices - note that
                            // we must re-evaluate from the parent of the region.
                            resolveZOrder(pRegion->m_pParent, m_ulCurrentTime);
                        }
                        else if (ulAttr == kAttrNameBackgroundColor)
                        {
                            // We are setting the backgroundColor for the region
                            // we are playing to
                            UINT32 ulRed   = (UINT32) pValue->GetValueDouble(0);
                            UINT32 ulGreen = (UINT32) pValue->GetValueDouble(1);
                            UINT32 ulBlue  = (UINT32) pValue->GetValueDouble(2);
                            UINT32 ulAlpha = (UINT32) pValue->GetValueDouble(3);
                            UINT32 ulColor = (ulAlpha << 24) | (ulRed << 16) |
                                             (ulGreen <<  8) | ulBlue;
                            // Get the site user for the region site
                            CSmilSiteUser* pUser = (CSmilSiteUser*) pRegion->m_pSiteUser;
                            if (pUser)
                            {
                                // Check and make sure that a media backgroundColor
                                // override did not just reset the color back to
                                // the region color
                                if (!bUnderlyingValue ||
                                    (bUnderlyingValue &&
                                     m_ulCurrentTime != pUser->GetLastMediaEndOverrideTime()))
                                {
                                    // Set the background color
                              pUser->SetBackgroundColor(ulColor);
                                }
                            }
                            // XXXMEH - animation optimzation
                            // We will go ahead and force redraw on the site, but
                            // this can be optimized by collecting changes until
                            // the end of the animation
                            if (pRegion->m_pSite)
                            {
                                HXxSize cSize = {0, 0};
                                pRegion->m_pSite->GetSize(cSize);
                                HXxRect cRect = {0, 0, cSize.cx, cSize.cy};
                                pRegion->m_pSite->DamageRect(cRect);
                                pRegion->m_pSite->ForceRedraw();
                            }
                        }
                    }
                    else
                    {
                        retVal = HXR_FAIL;
                    }
                }
                else
                {
                    retVal = HXR_FAIL;
                }
            }
            else if (ulAttr == kAttrNameColor)
            {
                // Get the color we want to set
                UINT32 ulRed   = (UINT32) pValue->GetValueDouble(0);
                UINT32 ulGreen = (UINT32) pValue->GetValueDouble(1);
                UINT32 ulBlue  = (UINT32) pValue->GetValueDouble(2);
                UINT32 ulAlpha = (UINT32) pValue->GetValueDouble(3);
                UINT32 ulColor = (ulAlpha << 24) | (ulRed << 16) |
                                 (ulGreen <<  8) | ulBlue;
                // Set the color into the renderer
                BOOL bChanged = FALSE;
                retVal = setRendererULONG32Property(pszID, "color",
                                                    ulColor, bChanged);
                if (SUCCEEDED(retVal) && bChanged)
                {
                    // Get the renderer site
                    IHXSite* pRendererSite = NULL;
                    retVal = getRendererSite(pszID, pRendererSite);
                    if (SUCCEEDED(retVal))
                    {
                        // Queue the site for redraw
                        queueSiteForAnimationRedraw(pRendererSite);
                    }
                    HX_RELEASE(pRendererSite);
                }
            }
            else if (ulAttr == kAttrNameMediaOpacity ||
                     ulAttr == kAttrNameBackgroundOpacity)
            {
                // Get the correct property string
                const char* pszPropStr = (ulAttr == kAttrNameMediaOpacity ? "mediaOpacity" : "backgroundOpacity");
                // Get the value to set
                UINT32 ulOpacity = (UINT32) pValue->GetValueDouble();
                // Set the property
                BOOL bChanged = FALSE;
                retVal = setRendererULONG32Property(pszID, pszPropStr,
                                                    ulOpacity, bChanged);
                if (SUCCEEDED(retVal) && bChanged)
                {
                    // Get the renderer site
                    IHXSite* pRendererSite = NULL;
                    retVal = getRendererSite(pszID, pRendererSite);
                    if (SUCCEEDED(retVal))
                    {
                        // Queue the site for redraw
                        queueSiteForAnimationRedraw(pRendererSite);
                    }
                    HX_RELEASE(pRendererSite);
                }
            }
        }
        else if (eTag == SMILRootLayout)
        {
            if (m_pRootLayout && m_pRootLayout->m_pRoot)
            {
                switch (ulAttr)
                {
                    case kAttrNameWidth:
                        if (m_pRootLayout->m_pRoot->m_dWidth != pValue->GetValueDouble(0))
                        {
                            m_pRootLayout->m_pRoot->m_dWidth     = pValue->GetValueDouble(0);
                            m_pRootLayout->m_pRoot->m_eWidthType = pValue->GetCSS2Type(0);
                            m_bAnimateRootLayout                 = TRUE;
                        }
                        break;
                    case kAttrNameHeight:
                        if (m_pRootLayout->m_pRoot->m_dHeight != pValue->GetValueDouble(0))
                        {
                            m_pRootLayout->m_pRoot->m_dHeight     = pValue->GetValueDouble(0);
                            m_pRootLayout->m_pRoot->m_eHeightType = pValue->GetCSS2Type(0);
                            m_bAnimateRootLayout                  = TRUE;
                        }
                        break;
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)
        else if (eTag == SMILViewport)
        {
            CSmilBasicViewport* pVP = getViewport(pszID);
            if (pVP && pVP->m_pPort)
            {
                BOOL bQueueVP = FALSE;
                switch (ulAttr)
                {
                    case kAttrNameWidth:
                        if (pVP->m_pPort->m_dWidth != pValue->GetValueDouble(0))
                        {
                            pVP->m_pPort->m_dWidth     = pValue->GetValueDouble(0);
                            pVP->m_pPort->m_eWidthType = pValue->GetCSS2Type(0);
                            bQueueVP = TRUE;
                        }
                        break;
                    case kAttrNameHeight:
                        if (pVP->m_pPort->m_dHeight != pValue->GetValueDouble(0))
                        {
                            pVP->m_pPort->m_dHeight     = pValue->GetValueDouble(0);
                            pVP->m_pPort->m_eHeightType = pValue->GetCSS2Type(0);
                            bQueueVP = TRUE;
                        }
                        break;
                }
                if (bQueueVP)
                {
                    if (!m_pAnimTopLayoutMap)
                    {
                        m_pAnimTopLayoutMap = new CHXMapPtrToPtr();
                    }
                    if (m_pAnimTopLayoutMap)
                    {
                        m_pAnimTopLayoutMap->SetAt((void*) pVP, (void*) pVP);
                    }
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */
        else if (eTag == SMILParam)
        {
            if (m_pSmilParser)
            {
                CSmilElement* pEl = m_pSmilParser->findElement(pszID);
                if (pEl &&
                    pEl->m_pNode &&
                    pEl->m_pNode->m_tag == SMILParam &&
                    m_pSmilParser->isMediaObject(pEl->m_pNode->m_pParent))
                {
                    // Get the CSmilParamElement
                    CSmilParamElement* pParam = (CSmilParamElement*) pEl;
                    // Get the media element id
                    const char* pszMediaID = (const char*) pEl->m_pNode->m_pParent->m_id;
                    // Set this property into the renderer
                    BOOL bChanged = FALSE;
                    retVal = setRendererCStringProperty(pszMediaID,
                                                        (const char*) pParam->m_pName,
                                                        pValue->GetValueString(),
                                                        bChanged);
                    if (SUCCEEDED(retVal) && bChanged)
                    {
                        MLOG_ANIM(m_pErrorMessages,
                                  "setValue() t=%lu Changed param id=%s value to %s\n",
                                  m_ulCurrentTime, pszID,
                                  pValue->GetValueString());
                        // Get the renderer site
                        IHXSite* pRendererSite = NULL;
                        retVal = getRendererSite(pszMediaID, pRendererSite);
                        if (SUCCEEDED(retVal))
                        {
                            // Queue the site for redraw
                            queueSiteForAnimationRedraw(pRendererSite);
                        }
                        HX_RELEASE(pRendererSite);
                    }
                }
                else
                {
                    retVal = HXR_FAIL;
                }
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
    }

    return retVal;
}

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

const char* CSmilDocumentRenderer::getDefaultNamespace()
{
    const char* pRet = NULL;

    if (m_pSmilParser)
    {
        pRet = m_pSmilParser->getDefaultNamespace();
    }

    return pRet;
}

void CSmilDocumentRenderer::resetRendererSites(CSmilBasicRegion* pRegion)
{
    if (pRegion && pRegion->m_pChildRendererSiteList)
    {
        LISTPOSITION pos = pRegion->m_pChildRendererSiteList->GetHeadPosition();
        while (pos)
        {
            IHXSite* pRndSite =
                (IHXSite*) pRegion->m_pChildRendererSiteList->GetNext(pos);
            if (pRndSite)
            {
                // Reset the size of the site. Note that
                // since we have a site watcher, it doesn't
                // matter what we pass in, because the site
                // watcher will override it.
                HXxSize cSize = {1, 1};
                pRndSite->SetSize(cSize);
                // Reset the position of the site. Note that
                // since we have a site watcher, it doesn't
                // matter what we pass in, because the site
                // watcher will override it.
                HXxPoint cPos = {1, 1};
                pRndSite->SetPosition(cPos);
            }
        }
    }
}

CSmilSiteWatcher* CSmilDocumentRenderer::getRendererSiteWatcher(const char* pszMediaID)
{
    CSmilSiteWatcher* pRet = NULL;

    if (pszMediaID && m_pMediaID2RendererSiteWatcherMap)
    {
        // Look up the site watcher
        void* pVoid = NULL;
        if (m_pMediaID2RendererSiteWatcherMap->Lookup(pszMediaID, pVoid) && pVoid)
        {
            pRet = (CSmilSiteWatcher*) pVoid;
        }
    }

    return pRet;
}

void CSmilDocumentRenderer::addRendererSiteWatcherToMap(const char*       pszMediaID,
                                                        CSmilSiteWatcher* pWatch)
{
    if (pszMediaID && pWatch)
    {
        // Do we already have the map?
        if (!m_pMediaID2RendererSiteWatcherMap)
        {
            // Create it
            m_pMediaID2RendererSiteWatcherMap = new CHXMapStringToOb();
        }
        if (m_pMediaID2RendererSiteWatcherMap)
        {
            // Is this renderer already in the map?
            void* pVoid = NULL;
            if (!m_pMediaID2RendererSiteWatcherMap->Lookup(pszMediaID, pVoid))
            {
                // AddRef the site
                pWatch->AddRef();
                // Add it to the map
                m_pMediaID2RendererSiteWatcherMap->SetAt(pszMediaID, (void*) pWatch);
            }
        }
    }
}

void CSmilDocumentRenderer::removeRendererSiteWatcherFromMap(const char* pszMediaID)
{
    if (pszMediaID && m_pMediaID2RendererSiteWatcherMap)
    {
        // Is this renderer site in the map
        void* pVoid = NULL;
        if (m_pMediaID2RendererSiteWatcherMap->Lookup(pszMediaID, pVoid) && pVoid)
        {
            // Release the renderer site
            CSmilSiteWatcher* pWatch = (CSmilSiteWatcher*) pVoid;
            HX_RELEASE(pWatch);
            // Remove it from the map
            m_pMediaID2RendererSiteWatcherMap->RemoveKey(pszMediaID);
        }
    }
}

void CSmilDocumentRenderer::clearRendererSiteWatcherMap()
{
    if (m_pMediaID2RendererSiteWatcherMap)
    {
        POSITION pos = m_pMediaID2RendererSiteWatcherMap->GetStartPosition();
        while (pos)
        {
            const char* pszKey = NULL;
            void*       pVoid  = NULL;
            m_pMediaID2RendererSiteWatcherMap->GetNextAssoc(pos, pszKey, pVoid);
            if (pVoid)
            {
                // Release the renderer site
                CSmilSiteWatcher* pWatch = (CSmilSiteWatcher*) pVoid;
                HX_RELEASE(pWatch);
            }
        }
        // Remove all entries from the map
        m_pMediaID2RendererSiteWatcherMap->RemoveAll();
    }
}

HX_RESULT CSmilDocumentRenderer::getRendererSite(const char*    pszMediaID,
                                                 REF(IHXSite*) rpSite)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pszMediaID && m_pMediaID2RendererSiteMap)
    {
        // Is the media ID in the map?
        void* pVoid = NULL;
        if (m_pMediaID2RendererSiteMap->Lookup(pszMediaID, pVoid) && pVoid)
        {
            HX_RELEASE(rpSite);
            rpSite = (IHXSite*) pVoid;
            rpSite->AddRef();
            retVal = HXR_OK;
        }
    }

    return retVal;
}

void CSmilDocumentRenderer::addRendererSiteToMap(const char* pszMediaID,
                                                 IHXSite*   pSite)
{
    if (pszMediaID && pSite)
    {
        // Do we already have the map?
        if (!m_pMediaID2RendererSiteMap)
        {
            // Create it
            m_pMediaID2RendererSiteMap = new CHXMapStringToOb();
        }
        if (m_pMediaID2RendererSiteMap)
        {
            // Is this renderer already in the map?
            void* pVoid = NULL;
            if (!m_pMediaID2RendererSiteMap->Lookup(pszMediaID, pVoid))
            {
                // AddRef the site
                pSite->AddRef();
                // Add it to the map
                m_pMediaID2RendererSiteMap->SetAt(pszMediaID, (void*) pSite);
            }
        }
    }
}

void CSmilDocumentRenderer::removeRendererSiteFromMap(const char* pszMediaID)
{
    if (pszMediaID && m_pMediaID2RendererSiteMap)
    {
        // Is this renderer site in the map
        void* pVoid = NULL;
        if (m_pMediaID2RendererSiteMap->Lookup(pszMediaID, pVoid) && pVoid)
        {
            // Release the renderer site
            IHXSite* pSite = (IHXSite*) pVoid;
            HX_RELEASE(pSite);
            // Remove it from the map
            m_pMediaID2RendererSiteMap->RemoveKey(pszMediaID);
        }
    }
}

void CSmilDocumentRenderer::clearRendererSiteMap()
{
    if (m_pMediaID2RendererSiteMap)
    {
        POSITION pos = m_pMediaID2RendererSiteMap->GetStartPosition();
        while (pos)
        {
            const char* pszKey = NULL;
            void*       pVoid  = NULL;
            m_pMediaID2RendererSiteMap->GetNextAssoc(pos, pszKey, pVoid);
            if (pVoid)
            {
                // Release the renderer site
                IHXSite* pSite = (IHXSite*) pVoid;
                HX_RELEASE(pSite);
            }
        }
        // Remove all entries from the map
        m_pMediaID2RendererSiteMap->RemoveAll();
    }
}

HX_RESULT CSmilDocumentRenderer::getRenderer(const char*        pszMediaID,
                                             REF(IHXRenderer*) rpRenderer)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pszMediaID && m_pMediaID2RendererMap)
    {
        // Is the media ID in the map?
        void* pVoid = NULL;
        if (m_pMediaID2RendererMap->Lookup(pszMediaID, pVoid) && pVoid)
        {
            HX_RELEASE(rpRenderer);
            rpRenderer = (IHXRenderer*) pVoid;
            rpRenderer->AddRef();
            retVal = HXR_OK;
        }
    }

    return retVal;
}

void CSmilDocumentRenderer::addRendererToMap(const char* pszMediaID,
                                             IHXRenderer* pRenderer)
{
    if (pszMediaID && pRenderer)
    {
        // Do we already have the map?
        if (!m_pMediaID2RendererMap)
        {
            // Create it
            m_pMediaID2RendererMap = new CHXMapStringToOb();
        }
        if (m_pMediaID2RendererMap)
        {
            // Is this renderer already in the map?
            void* pVoid = NULL;
            if (!m_pMediaID2RendererMap->Lookup(pszMediaID, pVoid))
            {
                // AddRef the renderer
                pRenderer->AddRef();
                // Add it to the map
                m_pMediaID2RendererMap->SetAt(pszMediaID, (void*) pRenderer);
            }
        }
    }
}

void CSmilDocumentRenderer::removeRendererFromMap(const char* pszMediaID)
{
    if (pszMediaID && m_pMediaID2RendererMap)
    {
        // Is this entry in the map?
        void* pVoid = NULL;
        if (m_pMediaID2RendererMap->Lookup(pszMediaID, pVoid) && pVoid)
        {
            // Release the renderer
            IHXRenderer* pRend = (IHXRenderer*) pVoid;
            HX_RELEASE(pRend);
            // Remove it from the map
            m_pMediaID2RendererMap->RemoveKey(pszMediaID);
        }
    }
}

void CSmilDocumentRenderer::clearRendererMap()
{
    if (m_pMediaID2RendererMap)
    {
        // Run through the map, and release the renderer
        POSITION pos = m_pMediaID2RendererMap->GetStartPosition();
        while (pos)
        {
            const char* pszKey = NULL;
            void*       pVoid  = NULL;
            m_pMediaID2RendererMap->GetNextAssoc(pos, pszKey, pVoid);
            if (pVoid)
            {
                IHXRenderer* pRend = (IHXRenderer*) pVoid;
                HX_RELEASE(pRend);
            }
        }
        // Remove all entries from the map
        m_pMediaID2RendererMap->RemoveAll();
    }
}

SMILPlayToAssoc*
CSmilDocumentRenderer::getPlayToAssoc(UINT16 uGroupIndex, UINT16 uTrackIndex)
{
    SMILPlayToAssoc* pPlayToAssoc = 0;
    if(m_pPlayToAssocList)
    {
      CHXSimpleList::Iterator i;
      for(i=m_pPlayToAssocList->Begin();i!=m_pPlayToAssocList->End();++i)
      {
          SMILPlayToAssoc* pThisAssoc = (SMILPlayToAssoc*)(*i);
          if((pThisAssoc->m_uGroupIndex == uGroupIndex) &&
             (pThisAssoc->m_uTrackIndex == uTrackIndex))
          {
            pPlayToAssoc = pThisAssoc;
            break;
          }
      }
    }
    return pPlayToAssoc;
}

SMILPlayToAssoc*
CSmilDocumentRenderer::getPlayToAssoc(const char* pPlayTo)
{
    SMILPlayToAssoc* pPlayToAssoc = 0;
    UINT16 uCurGroupIndex = getCurrentGroup();
    if(m_pPlayToAssocList)
    {
      CHXSimpleList::Iterator i;
      for(i=m_pPlayToAssocList->Begin();i!=m_pPlayToAssocList->End();++i)
      {
          SMILPlayToAssoc* pThisAssoc = (SMILPlayToAssoc*)(*i);
          if(pThisAssoc->m_playTo == pPlayTo  &&
                // /Fixes bug where already-removed media was being
                // returned as active:
                !pThisAssoc->m_bRemovePending  &&
                // /Fixes PR 55355 where media from wrong group was
                // being returned since delay values are relative to
                // start of group:
                uCurGroupIndex == pThisAssoc->m_uGroupIndex)
          {
            if (!pPlayToAssoc)
            {
                pPlayToAssoc = pThisAssoc;
            }
            // /Fixes bugs (including PR 55355) where multiple media
            // play to the same region and this function was always
            // returning the first media's playToAssoc even if it had
            // ended or been covered by other media; this resulted in
            // the new media not getting region mouse events:
            else if (pThisAssoc->m_ulDelay >= pPlayToAssoc->m_ulDelay  &&
                  // /Find the one earlier than but closest to the
                  // current time; if none such exists, find the one
                  // closest to and later than current time (for pre-
                  // scheduling of show/hide event purposes):
                  (pThisAssoc->m_ulDelay <= m_ulCurrentTime  ||
                  (pPlayToAssoc->m_ulDelay > m_ulCurrentTime  &&
                  pThisAssoc->m_ulDelay < pPlayToAssoc->m_ulDelay)) )
            {
                pPlayToAssoc = pThisAssoc;
            }
          }
      }
    }
    return pPlayToAssoc;
}

SMILPlayToAssoc* CSmilDocumentRenderer::getPlayToAssocByMedia(const char* pszMediaID)
{
    SMILPlayToAssoc* pPlayToAssoc = NULL;

    if (pszMediaID && m_pPlayToAssocList)
    {
        LISTPOSITION pos = m_pPlayToAssocList->GetHeadPosition();
        while (pos)
        {
            SMILPlayToAssoc* pListMember =
                (SMILPlayToAssoc*) m_pPlayToAssocList->GetNext(pos);
            if (pListMember && pListMember->m_id == pszMediaID)
            {
                pPlayToAssoc = pListMember;
                break;
            }
        }
    }

    return pPlayToAssoc;
}
                
void
CSmilDocumentRenderer::setPlayToAssoc(UINT16 uGroupIndex, UINT16 uTrackIndex, 
                              const char* pID, 
                              const char* pRepeatID,
                              const char* pPlayTo,
                              const char* pRegionName, 
                              const char* pBeginTransition,
                              const char* pEndTransition,
                              UINT32 lexicalOrder,
                                      BOOL   bXMMSource)
{
    SMILPlayToAssoc* pPlayToAssoc = getPlayToAssoc(uGroupIndex, uTrackIndex);
    if(!pPlayToAssoc)
    {
      pPlayToAssoc                = new SMILPlayToAssoc;
      pPlayToAssoc->m_uGroupIndex = uGroupIndex;
      pPlayToAssoc->m_uTrackIndex = uTrackIndex;
      pPlayToAssoc->m_id          = pID;
      pPlayToAssoc->m_repeatid    = pRepeatID;
      pPlayToAssoc->m_playTo      = pPlayTo;
      pPlayToAssoc->m_ulDelay     = 0;
      pPlayToAssoc->m_ulDuration  = 0;
      pPlayToAssoc->m_bIsPersistentSource = FALSE;
      pPlayToAssoc->m_bDurationResolved = FALSE;
      pPlayToAssoc->m_bRemoveSite = TRUE; // default is remove
      pPlayToAssoc->m_bRemovePending = FALSE;
      pPlayToAssoc->m_pHyperlinks = new CHXSimpleList;
      pPlayToAssoc->m_pRendererEventHook  = NULL;
      pPlayToAssoc->m_pSiteInfoList     = new CHXSimpleList;
      pPlayToAssoc->m_ulLexicalOrder = lexicalOrder;
        pPlayToAssoc->m_bXMMSource     = bXMMSource;
      pPlayToAssoc->m_bTrackStopped = FALSE;
      

      if (pBeginTransition)
      {
          pPlayToAssoc->m_beginTransition  = pBeginTransition;
      }

      if (pEndTransition)
      {
          pPlayToAssoc->m_endTransition = pEndTransition;
      }


      if(pRegionName)
      {
          pPlayToAssoc->m_regionName  = pRegionName;
      }

      char cTemp[20]; /* Flawfinder: ignore */
      ::sprintf(cTemp,"%#010lx",(ULONG32)(void*)pPlayToAssoc); /* Flawfinder: ignore */
      pPlayToAssoc->m_tunerName   = (const char*) cTemp;
      ::sprintf(cTemp,"%#010lx",(ULONG32)(void*)pPlayToAssoc+1); /* Flawfinder: ignore */
      pPlayToAssoc->m_childTunerName = (const char*)cTemp;

      // see if the pPlayTo points to a valid CSmilBasicRegion
      BOOL bNoRegion = TRUE;
      if(pPlayTo)
      {
            // First, assume that pPlayTo is an id and see
            // if we can find the region by id
          CSmilBasicRegion* pRegion = getRegionByID(pPlayTo);
          if(pRegion)
          {
            bNoRegion = FALSE;
          }
            else
            {
                // We didn't find it by id, so try and find it
                // by regionName
                pRegion = getFirstRegionByName(pPlayTo);
                if (pRegion)
                {
                    bNoRegion = FALSE;
                }
            }
      }

      if(bNoRegion)
      {
          // region name is tuner name if it's anonymous
          pPlayToAssoc->m_playTo = pPlayToAssoc->m_childTunerName;
      }
      else
      {
          pPlayToAssoc->m_playTo = pPlayTo;
      }

      if(!m_pPlayToAssocList)
      {
          m_pPlayToAssocList = new CHXSimpleList;
      }
      m_pPlayToAssocList->AddTail(pPlayToAssoc);
    }
}

void 
CSmilDocumentRenderer::removeSourcemap(SMILPlayToAssoc* pPlayToAssoc)
{
    // get to the site manager and remove event hook
    IHXEventHookMgr* pHookMgr = NULL;
    m_pSiteMgr->QueryInterface(IID_IHXEventHookMgr, (void**)&pHookMgr);

    CHXMapLongToObj::Iterator j = pPlayToAssoc->m_sourceMap.Begin();
    for(; j != pPlayToAssoc->m_sourceMap.End(); ++j)
    {
      CHXSimpleList* pRendererList = (CHXSimpleList*)(*j);

      HX_ASSERT(pRendererList);
      // /Check for NULL (fixes part of PR 120999) in case we are flippling
      // back and forth between nested meta clip and another clip in a list:
      if (pRendererList)
      {
          CHXSimpleList::Iterator k = pRendererList->Begin();
          for (; k != pRendererList->End(); ++k)
          {
              SMILSourceInfo* pSMILSourceInfo = (SMILSourceInfo*)(*k);

              if (pSMILSourceInfo->m_pRendererEventHook && pHookMgr)        
              {
                pHookMgr->RemoveHook(pSMILSourceInfo->m_pRendererEventHook, 
                                 pSMILSourceInfo->m_pRendererEventHook->GetChannelName(), 0);
              }

              HX_RELEASE(pSMILSourceInfo->m_pRendererEventHook);
              HX_RELEASE(pSMILSourceInfo->m_pStream);
              HX_RELEASE(pSMILSourceInfo->m_pRenderer);
      
              HX_DELETE(pSMILSourceInfo);
          }
        }
      HX_DELETE(pRendererList);
    }
    pPlayToAssoc->m_sourceMap.RemoveAll();
    HX_RELEASE(pHookMgr);
}

void
CSmilDocumentRenderer::removeAllPlayToAssoc()
{
    if(m_pPlayToAssocList)
    {
      CHXSimpleList::Iterator i = m_pPlayToAssocList->Begin();
      for(; i != m_pPlayToAssocList->End(); ++i)
      {
          SMILPlayToAssoc* pPlayToAssoc = (SMILPlayToAssoc*)(*i);
          
          HX_DELETE(pPlayToAssoc->m_pHyperlinks);
          
          removeSourcemap(pPlayToAssoc);

          if (pPlayToAssoc->m_pSiteInfoList)
          {
            pPlayToAssoc->m_pSiteInfoList->RemoveAll();
            HX_DELETE(pPlayToAssoc->m_pSiteInfoList);
          }
          
          HX_DELETE(pPlayToAssoc);
      }
    }
    HX_DELETE(m_pPlayToAssocList);
}

void
CSmilDocumentRenderer::removeGroupsPlayToAssoc(UINT16 uGroupIndex)
{
    if(m_pPlayToAssocList)
    {
      CHXSimpleList::Iterator i = m_pPlayToAssocList->Begin();
      for(; i != m_pPlayToAssocList->End(); ++i)
      {
          SMILPlayToAssoc* pPlayToAssoc = (SMILPlayToAssoc*)(*i);
          
          /*
          if the PlayToAssoc has the same group number as the group index,
          then remove it.
          */
          
          if ( pPlayToAssoc->m_uGroupIndex == uGroupIndex )
          {
            removeSourcemap(pPlayToAssoc);

            if (pPlayToAssoc->m_pSiteInfoList)
            {
                pPlayToAssoc->m_pSiteInfoList->RemoveAll();
            }
            
            break;
          }     
      }
    }
}

void
CSmilDocumentRenderer::removeTracksPlayToAssoc(UINT16 uGroupIndex, UINT16 uTrackIndex)
{
    if (!m_pPlayToAssocList)
    {
      return;
    }

    LISTPOSITION pos = m_pPlayToAssocList->GetHeadPosition();
    while(pos)
    {
      SMILPlayToAssoc* pPlayToAssoc = (SMILPlayToAssoc*)m_pPlayToAssocList->GetAt(pos);

      if (pPlayToAssoc->m_uGroupIndex == uGroupIndex &&
          pPlayToAssoc->m_uTrackIndex == uTrackIndex)
      {
          removeSourcemap(pPlayToAssoc);

          HX_DELETE(pPlayToAssoc->m_pHyperlinks);               
          HX_DELETE(pPlayToAssoc->m_pSiteInfoList);
          HX_DELETE(pPlayToAssoc);

          m_pPlayToAssocList->RemoveAt(pos);
          break;
      }
      m_pPlayToAssocList->GetNext(pos);
    }
    
    return;
}


BOOL
CSmilDocumentRenderer::isMediaPausedAndDisabled(const char* pID)
{
    BOOL bIsPausedAndDisabled = FALSE;
    CHXString* pTmpCStr;
    if (m_pPausedAndDisabledIDMap  &&
          m_pPausedAndDisabledIDMap->Lookup(pID, (void*&)pTmpCStr))
    {
      bIsPausedAndDisabled = TRUE;
    }

    return bIsPausedAndDisabled;
}


BOOL
CSmilDocumentRenderer::reenablePausedAndDisabledMedia(const char* pID,
      UINT16 uGroupIndex)
{
    BOOL bWasPausedAndDisabledAndNowIsNot = FALSE;
    CHXString* pTmpCStr;
    if (m_pPausedAndDisabledIDMap  &&
          m_pPausedAndDisabledIDMap->Lookup(pID, (void*&)pTmpCStr))
    {
      bWasPausedAndDisabledAndNowIsNot = TRUE;
      if(!m_pPausedAndDisabledIDMap->RemoveKey(pID))
      {
          HX_ASSERT(0); //RemoveKey() should never fail, but...
          (*m_pPausedAndDisabledIDMap)[pID] = NULL;
      }
      HX_DELETE(pTmpCStr);
    }
    float* pfPrePauseBrightness = NULL;
    if (m_pPausedAndDisabledBrightnessMap  &&
          m_pPausedAndDisabledBrightnessMap->Lookup(pID,
          (void*&)pfPrePauseBrightness))
    {
      if(!m_pPausedAndDisabledBrightnessMap->RemoveKey(pID))
      {
          HX_ASSERT(0); //RemoveKey() should never fail, but...
          (*m_pPausedAndDisabledBrightnessMap)[pID] = NULL;
      }
      // /Reset it to old brightness.  First get the renderer site:
      HX_ASSERT(m_pSiteInfoList);
      if(m_pSiteInfoList)
      {
          CHXSimpleList::Iterator i = m_pSiteInfoList->Begin();
          for(; i != m_pSiteInfoList->End(); ++i)
          {
            SMILSiteInfo* pSiteInfo =(SMILSiteInfo*)(*i);
            if(pSiteInfo->m_uGroupIndex == uGroupIndex  &&
                  pSiteInfo->m_MediaID == pID)
            {
                IHXVideoControl* pVidCntrls = NULL;
                HX_RESULT pnrVC =
                      pSiteInfo->m_pRendererSite->QueryInterface(
                      IID_IHXVideoControl, (void**)&pVidCntrls);
                if (SUCCEEDED(pnrVC))
                {
                  HX_RESULT rsltSetBrtness = pVidCntrls->SetBrightness(
                        *pfPrePauseBrightness);
                  pSiteInfo->m_pRendererSite->ForceRedraw();
                  HX_RELEASE(pVidCntrls);
                }
                HX_DELETE(pfPrePauseBrightness);
            }
          }
      }
    }

    return bWasPausedAndDisabledAndNowIsNot;
}



CSmilAAnchorElement*
CSmilDocumentRenderer::findHyperlinkElement(const char* pID, // region ID
                                            const char* pMediaID,
                                            UINT16      uXPos,
                                            UINT16      uYPos,
                                            BOOL        bResolveBeginOfFragmentTarget)
{
    // This code is not needed with new windowless site impl. of fullscreen 
    if (IsFullScreen())
    {
      IHXSiteWindowless* pSiteWndless = NULL;
      if (m_pRootLayout && m_pRootLayout->m_pSite)
      {
          m_pRootLayout->m_pSite->QueryInterface(IID_IHXSiteWindowless, (void**) &pSiteWndless);
      }

      // need to do this conversion only for the OLD site implementation
      if (m_pRootLayout && m_pRootLayout->m_pSite && pSiteWndless == NULL)
      {
          // If we are at full screen, then we need to scale the x and y
          // mouse locations down to the original size, since for full
          // screen we do not get a resizeSite.
          //
            // Get the current size
          HXxSize cCurSize;
          m_pRootLayout->m_pSite->GetSize(cCurSize);
            // Scale it down
          if (cCurSize.cx  &&  ANCHOR_POS_DONTCARE != uXPos)
          {
            uXPos = (UINT16) (uXPos * m_topSiteOriginalSize.cx / cCurSize.cx);
          }
          if (cCurSize.cy  &&  ANCHOR_POS_DONTCARE != uYPos)
          {
            uYPos = (UINT16) (uYPos * m_topSiteOriginalSize.cy / cCurSize.cy);
          }
      }
      
      HX_RELEASE(pSiteWndless);
    }

    // /In order to fix PR 55110, we need to find the link that is on top in
    // the region, not just the first active link in the list.  The one that
    // is on top is the one that has the highest delay time of all those that
    // are active at the time and place of the click:
    CSmilAAnchorElement* pMostRecentValidAnchor = NULL;
    ULONG32 ulMostRecentValidAnchorBegin = (UINT32)-1;
    CHXSimpleList::Iterator i = m_pPlayToAssocList->Begin();
    for(; i != m_pPlayToAssocList->End(); ++i)
    {
      SMILPlayToAssoc* pAssoc = (SMILPlayToAssoc*)(*i);
      if(strcmp((const char*)pAssoc->m_playTo, pID) == 0 &&
           strcmp((const char*)pAssoc->m_id, pMediaID) == 0 &&
         pAssoc->m_uGroupIndex == m_uCurrentGroupIndex &&
         pAssoc->m_pHyperlinks)
      {
          if(pAssoc->m_ulDelay == (UINT32)-1)
          {
            // not resolved yet
            return NULL;
          }

          // see if this site is current (visible) now;
          // if it isn't then no hyperlinks can be active
          // on that site.
          if (pAssoc->m_pSiteInfoList)
          {
            CHXSimpleList::Iterator j = pAssoc->m_pSiteInfoList->Begin();

            for (; j != pAssoc->m_pSiteInfoList->End(); ++j)
            {
                BOOL bCurrentSite = TRUE; // if no site info, then
                                    // the site is always current
                SMILSiteInfo* pSiteInfo = (SMILSiteInfo*)(*j);

                ULONG32 ulTime = m_ulCurrentTime;
                if (m_bSMILPresentationHasEnded)
                {
                  // /1/2 sec grace for last time sync to come
                  // after end of presentation:
                  ulTime -= (ulTime>500? 500: ulTime);
                }

                if(pSiteInfo->m_bRemoveSite)
                {
                  // /Fix for PR 34836:
                  // live sources that have 0 dur, which means they play
                  // forever, should have MAX_ULONG32, not 0, used as
                  // their dur when calculating whether or not they're
                  // visible at the current time:
                  ULONG32 ulDur = pAssoc->m_ulDuration;
                  if (pAssoc->m_bLiveSource  &&  0 == ulDur)
                  {
                      ulDur = (ULONG32)(-1);
                  }
                  bCurrentSite = ulTime >= pSiteInfo->m_ulDelay &&
                               ulTime <= pSiteInfo->m_ulDelay + 
                               ulDur;
                }
                else
                {
                  bCurrentSite = ulTime >= pSiteInfo->m_ulDelay;
                }
          
                if(bCurrentSite)
                {
                  UINT32 ulRelativeTime = ulTime - pAssoc->m_ulDelay;

                  CHXSimpleList::Iterator j = pAssoc->m_pHyperlinks->Begin();
                  for(; j != pAssoc->m_pHyperlinks->End(); ++j)
                  {
                      CSmilAAnchorElement* pAnchor = (CSmilAAnchorElement*)(*j);

                      CSmilBasicRegion* pRegion =     getRegionByID(pAssoc->m_playTo);
                      if (pRegion)
                      {
                        HXxRect mediaRect;
                        mediaRect.left = 0;
                        mediaRect.top = 0;
                        if(pRegion->m_bMediaSizeSet)
                        {
                            mediaRect.right = pRegion->m_mediaSize.cx;
                            mediaRect.bottom = pRegion->m_mediaSize.cy;
                        }
                        else
                        {
                            mediaRect.right = pRegion->m_rect.right;
                            mediaRect.bottom = pRegion->m_rect.bottom;
                        }

                        // /ulRelativeTime is begin of anchor element
                        // relative to begin of element that got this
                        // mouse event, so we need to adjust for begin
                        // offset from its sync-base in the case where
                        // this is an 'a' element which actually has
                        // the same sync-base, i.e., its sync-base is
                        // *not* (its child) element that got the mouse
                        // event:
                        ULONG32 ulBeginAdjustmentIfIsAAnchor = 0;
                        ULONG32 ulCurElementDelay = pAnchor->m_ulDelay;
                        if (pAnchor->isAAnchor())
                        {
                            CSmilElement* pThisElement =
                                  m_pSmilParser->findElement(pAssoc->m_id);
                            if (pThisElement  &&  pThisElement->
                                  m_bCurBeginIsOffsetFromSyncBase)
                            {
                              ulBeginAdjustmentIfIsAAnchor =
                                    pThisElement->
                                    m_ulBeginOffsetFromSyncBase;
                            }
                            ulRelativeTime +=
                                  ulBeginAdjustmentIfIsAAnchor;
                            // /If anchor delay isn't set, just use it's
                            // child media's delay:
                            if ((UINT32)-1 == ulCurElementDelay)
                            {
                              ulCurElementDelay = pThisElement->m_ulDelay;
                            }
                        }

                        if(pAnchor->isCurrentLink(ulRelativeTime,
                              uXPos, uYPos, mediaRect,
                              (ResizeZoom == pRegion->m_eResizeBehavior),
                              pRegion->m_dZoomScaleFactorX,
                              pRegion->m_dZoomScaleFactorY))
                        {
                            if ((UINT32)-1 ==
                                  ulMostRecentValidAnchorBegin  ||
                                  ulMostRecentValidAnchorBegin <=
                                  ulCurElementDelay)
                            {
                              ulMostRecentValidAnchorBegin = ulCurElementDelay;
                              pMostRecentValidAnchor = pAnchor;
                            }
                        }
                      }
                  }
                }
            }
          }
      }
    }

    if (pMostRecentValidAnchor)
    {
      //Now, we have to see if this points to a
      // fragment in the current SMIL presentation and,
      // if so, make sure that the begin time of the
      // associated element has been resolved.  If not,
      // we need to resolve it, per the SMIL 2.0 spec,
      // but only if on an activation of this hyperlink,
      // i.e., if bResolveTargetBeginIfFragment==TRUE:
      const char* pFragment = pMostRecentValidAnchor->m_href.GetBuffer(2);
      HX_ASSERT(pFragment);
      //Only do the following if this is a fragment:
      if(pFragment  &&  '#' == *pFragment)
      {
          BOOL bFragFoundAndResolved = TRUE;
          UINT32 ulFragmentOffset = m_pSmilParser->getFragmentOffset(
                &(pFragment[1]), 
                /* This is passed by ref:*/
                bFragFoundAndResolved, bResolveBeginOfFragmentTarget,
                m_ulCurrentTime); 
          //Fixes PR 22655:
          //Ignore if not found or not yet resolved:
          if (!bFragFoundAndResolved)
          {
            return NULL;
          }
      }
      return pMostRecentValidAnchor;
    }

    return NULL;
}

// /Returns NULL if element doesn't have either an alt or a
// longdesc value, otherwise returns the element with the pID:
CSmilElement*
CSmilDocumentRenderer::findIfCurElementHasAltOrLongdesc(const char* pID,
      UINT16 ulXPos, UINT16 ulYPos)
{
    CSmilElement* pRetElement = NULL;
    CHXSimpleList::Iterator i = m_pPlayToAssocList->Begin();
    // /We need to return the one who's on top in this region in case there
    // are multiple media playing to this region:
    for(; i != m_pPlayToAssocList->End(); ++i)
    {
      SMILPlayToAssoc* pAssoc = (SMILPlayToAssoc*)(*i);
      if(strcmp((const char*)pAssoc->m_playTo, pID) == 0  &&
            pAssoc->m_uGroupIndex == m_uCurrentGroupIndex)
      {
          if(pAssoc->m_ulDelay == (UINT32)-1)
          {
            // not resolved yet
            continue; // /Check for other media playing to this region.
          }
          // /Optimization: helps speed up the decision as to whether or not
          // this one should even be considered:
          if (pAssoc->m_ulDuration + pAssoc->m_ulDelay <= m_ulCurrentTime  &&
                pAssoc->m_bRemoveSite)
          {
            continue; // /Check for other media playing to this region.
          }

          // /First, see if this (x,y) point is over the media in this
          // region:
          CSmilBasicRegion* pRegion = getRegionByID(pAssoc->m_playTo);
          if (pRegion)
          {
            HXxRect mediaRect;
            mediaRect.left = 0;
            mediaRect.top = 0;
            if(pRegion->m_bMediaSizeSet)
            {
                mediaRect.right = pRegion->m_mediaSize.cx;
                mediaRect.bottom = pRegion->m_mediaSize.cy;
            }
            else
            {
                mediaRect.right = pRegion->m_rect.right;
                mediaRect.bottom = pRegion->m_rect.bottom;
            }
            UINT32 ulWidth =
                  mediaRect.right - mediaRect.left;
            UINT32 ulHeight =
                  mediaRect.bottom - mediaRect.top;

            if(ulXPos > ulWidth  ||  ulYPos > ulHeight)
            {
                // /It's not in bounds, so keep looking:
                continue;
            }
          }


          // see if this site is current (visible) now;
          // if it isn't then no element can be active
          // in that site:
          if (pAssoc->m_pSiteInfoList)
          {
            CHXSimpleList::Iterator j = pAssoc->m_pSiteInfoList->Begin();

            for (; j != pAssoc->m_pSiteInfoList->End(); ++j)
            {
                BOOL bCurrentSite = TRUE; // if no site info, then
                                    // the site is always current
                SMILSiteInfo* pSiteInfo = (SMILSiteInfo*)(*j);

                if(pSiteInfo->m_bRemoveSite)
                {
                  // /Live sources that have 0 dur, meaning they play
                  // forever, should have MAX_ULONG32, not 0, used as
                  // their dur when calculating whether or not they're
                  // visible at the current time:
                  ULONG32 ulDur = pAssoc->m_ulDuration;
                  if (pAssoc->m_bLiveSource  &&  0 == ulDur)
                  {
                      ulDur = (ULONG32)(-1);
                  }
                  bCurrentSite =
                         m_ulCurrentTime >= pSiteInfo->m_ulDelay  &&
                         m_ulCurrentTime <= pSiteInfo->m_ulDelay+ulDur;
                }
                else
                {
                  bCurrentSite = m_ulCurrentTime >= pSiteInfo->m_ulDelay;
                }
          
                if(bCurrentSite)
                {
                  CSmilElement* pElement = m_pSmilParser->findElement(
                        pAssoc->m_id);
                  // /Now, see if element has alt tag:
                  if (pElement)
                  {
                      if (pElement->m_alt.GetLength() > 0  ||
                            pElement->m_longdesc.GetLength() > 0)
                      {
                        // /Found it, so assign it as temp winner:
                        pRetElement = pElement;
                        // /Fixes PR 53282 by continuing (instead of
                        // quitting & returning the first one found):
                        // this one is on top of all others in this
                        // region so far; resolveZOrder() says it has
                        // sorted them so that the highest z-ordered
                        // one is the last in the list, so all we
                        // have to do is continue looking but keep
                        // this one as the temporary winner until
                        // another, later one is found:
                        continue;
                      }
                  }
                }
            }
          }
      }
    }

    return pRetElement;
}



STDMETHODIMP CSmilDocumentRenderer::HandleMouseMove(
                            void*       pWindow,
                            const char* pID, // Region ID
                                    const char* pMediaID,
                            UINT16      uXPos,
                            UINT16      uYPos,
                            UINT32  kRMAEvent,
                            REF(BOOL)   bHandleSetCursor)
{
    // since IHXStatusMessage::SetStatus() effectively
    // results in a mouse move (and I don't want to loop
    // forever) don't do anything if this mouse move event
    // has the same xy as the previous mouse move event

    bHandleSetCursor = FALSE;

    if(uXPos == m_usOldXPos &&
      uYPos == m_usOldYPos)
    {
      // /We don't want to claim HXR_OK in case media itself needs this
      // mouse event:
      return HXR_CANCELLED;
    }

    m_usOldXPos = uXPos;
    m_usOldYPos = uYPos;

    HX_RESULT rc = HXR_FAIL;

    BOOL bIsMouseLeaveEvent = (HX_MOUSE_LEAVE == kRMAEvent);

    if (pMediaID)
    {
      HX_RESULT rslt = HXR_FAILED;
      switch (kRMAEvent)
      {
          case HX_MOUSE_MOVE:
          {
          }
          break;

          case HX_MOUSE_ENTER:
          {
            rslt = m_pSmilParser->tryToResolveBeginEndEvents(
                  "inBoundsEvent", pMediaID, m_ulCurrentTime+1);
          }
          break;

          case HX_MOUSE_LEAVE:
          {
            HX_RESULT rslt = m_pSmilParser->tryToResolveBeginEndEvents(
                  "outOfBoundsEvent", pMediaID, m_ulCurrentTime);
          }
          break;

          default:
          {
            HX_ASSERT(kRMAEvent  &&  "unexpected mouse event");
            break;
          }
      }

      if (SUCCEEDED(rslt)  &&
            // /Fixes PR 56052: don't do anything after end of
            // presentation otherwise layout
            !m_bSMILPresentationHasEnded)
      {
          handleElements();
      }
    }


#if defined(_WINDOWS)
    HCURSOR hCurrentCursor = GetCursor();
#endif

    CSmilAAnchorElement* pAnchor = NULL;
    HX_ASSERT(pMediaID);
    if (pMediaID  &&  !isMediaPausedAndDisabled(pMediaID)  &&
          // /For mouse leave event, don't find hyperlink:
          !bIsMouseLeaveEvent)
    {
      pAnchor = findHyperlinkElement(pID, pMediaID, uXPos, uYPos);
    }
    // /Fixes PR 56503: if any element will begin or end when pID is clicked
    // (i.e., if any has begin|end="pID.activateEvent[+|-n]") then we should
    // show the hand cursor:
    BOOL bHasActivateEventListener = FALSE;
    if (!pAnchor  &&  HX_MOUSE_MOVE == kRMAEvent  &&  pMediaID  &&
          // /Fixes case where presentation has stopped; we don't want
          // to show the hand cursor for activateEvents because they
          // won't do anything when presentation is done:
          !m_bSMILPresentationHasEnded)
    {
      bHasActivateEventListener = m_pSmilParser->hasActivateEventListener(
            pMediaID, m_uCurrentGroupIndex);
    }
    if(pAnchor  ||  bHasActivateEventListener)
    {
#if defined(_WINDOWS)
      if(hCurrentCursor != m_hHyperlinkCursor)
      {
          m_bNeedToSetHyperlinkCursor = TRUE;
      }
#elif defined(_MACINTOSH)
        if (m_hHyperlinkCursor)
        {
          m_bResetCursor = TRUE;
          ::SetCursor(*m_hHyperlinkCursor);
        }
#endif
#if defined(_UNIX)  &&  (!(defined(_BEOS)))  &&  defined(USE_XWINDOWS)
      if (m_pDisplay && m_Window && m_hHyperlinkCursor && m_hCurrentCursor != m_hHyperlinkCursor)
      {
          XLockDisplay(m_pDisplay);
          XDefineCursor(m_pDisplay, m_Window, m_hHyperlinkCursor);
          XUnlockDisplay(m_pDisplay);
          m_hCurrentCursor = m_hHyperlinkCursor;
      }
#endif

      if (m_pStatusMessage  &&  pAnchor)
      {
          // /Override displaying of URL if alt can be shown instead:
          if (pAnchor->m_alt.GetLength() > 0)
          {
            m_pStatusMessage->SetStatus (pAnchor->m_alt);
          }
          else
          {
            m_pStatusMessage->SetStatus (pAnchor->m_href);
          }
          m_bStatusMessageSet = TRUE;
      }
      rc = HXR_OK;      // handled
    }
    else
    {
      BOOL bOKToResetStatusMessage = TRUE;
      // /There's no anchor displayed in the status bar, so look to see
      // if this element has an alt tag value (or longdesc) and, if so,
      // display it:
      CSmilElement* pElemAltOrLongdesc =
            findIfCurElementHasAltOrLongdesc(pID, uXPos, uYPos);
      if (pElemAltOrLongdesc)
      {
          if (m_pStatusMessage)
          {
            if (pElemAltOrLongdesc->m_alt.GetLength() > 0)
            {
                m_pStatusMessage->SetStatus(
                      pElemAltOrLongdesc->m_alt);
                m_bStatusMessageSet = TRUE;
                bOKToResetStatusMessage = FALSE;
            }
          }
      }

#if defined(_WINDOWS)
      if(hCurrentCursor == m_hHyperlinkCursor)
      {
          //We need to change it back -- we just moved off of a hyperlink:
          m_bNeedToSetHyperlinkCursor = FALSE;
          // /Do opposite logic for for mouse Leave event:
          if (bIsMouseLeaveEvent)
          {
            // /We need to force a cursor update because
            // handleSetCursor() doesn't get called when we move off of
            // linked media and onto an area where there is no media:
            bHandleSetCursor = TRUE;
          }

      }
#elif defined(_MACINTOSH)
      if (m_bResetCursor)
      {
          m_bResetCursor = FALSE;
          InitCursor();
      }
#elif defined(_UNIX)  &&  (!(defined(_BEOS)))  &&  defined(USE_XWINDOWS)
      if (m_pDisplay && m_Window && m_hCurrentCursor == m_hHyperlinkCursor)
      {
          XLockDisplay(m_pDisplay);
          XUndefineCursor(m_pDisplay, m_Window);
          XUnlockDisplay(m_pDisplay);
          m_hCurrentCursor = 0;
      }
#endif
      if (m_pStatusMessage &&  bOKToResetStatusMessage  &&
          m_bStatusMessageSet)
      {
          m_pStatusMessage->SetStatus(NULL);
          m_bStatusMessageSet = FALSE;
      }
    }

    return rc;
}

STDMETHODIMP_(BOOL) CSmilDocumentRenderer::HandleSetCursor()
{
    BOOL rc = FALSE;

#ifdef _WINDOWS
    if(m_bNeedToSetHyperlinkCursor)
    {
      // We don't want to overwrite our pre hyperlink cursor
      HCURSOR hTemp = SetCursor(m_hHyperlinkCursor);
      if (hTemp != m_hHyperlinkCursor)
      {
          m_hPreHyperlinkCursor = hTemp;
            
      }
      rc = TRUE;
    }
    else if (m_hPreHyperlinkCursor)
    {
      SetCursor(m_hPreHyperlinkCursor);
      m_hPreHyperlinkCursor = NULL;
      rc = TRUE;
    }
#endif

    return rc;
}


STDMETHODIMP CSmilDocumentRenderer::HandleLButtonUp(const char* pRegionID,
                                                    const char* pMediaID,
                                                    UINT16      uXPos,
                                                    UINT16      uYPos,
                                                    REF(BOOL)   rbHandled)
{
#if defined(_UNIX)  &&  (!(defined(_BEOS)))  &&  defined(USE_XWINDOWS)
//Clear the previously set cursors before leaving this window
    if(m_Window)
    {
       XLockDisplay(m_pDisplay);
        XUndefineCursor(m_pDisplay, m_Window);
       XUnlockDisplay(m_pDisplay);
    }
#endif

    // /Note: "activateEvent" is equivalent to a click event on
    // the primary mouse button or when a series of keystrokes
    // activates the visual element.  We got here because of a
    // mouse click, so we need to fire a "focusInEvent" if the
    // element didn't already have the "focus":
/*          // /XXXEH- TODO: add and manage variable that keeps track of 
    // which element has the focus, if any, and fire it if this
    // element does not have it when clicked:
    if (!hasSMIL2_20000731Draft_Focus((const char*)pPlayToAssoc->m_playTo))
    {
      rslt = m_pSmilParser->tryToResolveBeginEndEvents(
            "focusInEvent",
            (const char*)pPlayToAssoc->m_id,m_ulCurrentTime);
    }
*/

    HX_RESULT rc = HXR_OK;
    rbHandled = FALSE;
    CSmilAAnchorElement* pAnchor = NULL;
    if (!isMediaPausedAndDisabled(pMediaID))
    {
      BOOL bResolveTargetBeginIfFragment = TRUE;
      pAnchor = findHyperlinkElement(pRegionID, pMediaID, uXPos, uYPos,
            bResolveTargetBeginIfFragment);
        rc = handleNamedEvent(pRegionID, pMediaID, "activateEvent");
    }

    HX_RESULT rcHLT = HXR_OK;
    
    if (pAnchor)
    {
      rcHLT = handleHyperlinkTraversal(pAnchor);
        if (SUCCEEDED(rcHLT))
        {
            rbHandled = TRUE;
        }
    }

    rc = (HXR_OK == rc)? rcHLT : rc;

    return rc;
}


HX_RESULT
CSmilDocumentRenderer::handleHyperlinkTraversal(CSmilAAnchorElement* pAnchor,
                                                BOOL bCalledFromCallback)
{
    HX_RESULT rc = HXR_OK;
    HX_RESULT rsltOfRaisingActivateEvent = HXR_FAIL;
    if(pAnchor && pAnchor->m_href.GetLength() > 0)
    {
      if(m_pParent)
      {
          IHXPlayer* pPlayer = m_pParent->getPlayer();
          if(pPlayer)
          {
            const char* pTarget = "_player";

            const char* pSendTo = NULL;
            if (pAnchor->m_sendTo.GetLength())
            {
                pSendTo = (const char*)pAnchor->m_sendTo;
            }

            BOOL bIsOnLoadActuation =
                  (0 == strcmp(pAnchor->m_actuate, "onLoad"));

            BOOL bForceCallbackForNonExternalLinks =
                  !bCalledFromCallback  &&  !pAnchor->m_bExternal;
            if ((bIsOnLoadActuation  &&
                  ("pause"==pAnchor->m_show  ||
                  "replace"==pAnchor->m_show))  ||
                  // /Always schedule callback if on Mac and
                  // PR84836 conditions exist:
                  // /Also, to help fix PR 91817, force callback for non-
                  // external links in case this is from accesskey press
                  // or some other link-activating event on main thread:
                  bForceCallbackForNonExternalLinks)
            {
                    if (!bCalledFromCallback)
                    {
                        if (!m_pHyperlinkCallback)
                        {
                            m_pHyperlinkCallback = new CHyperlinkCallback(this);
                            m_pHyperlinkCallback->AddRef();
                  }

                  if (!(m_pHyperlinkCallback->IsCallbackPending()))
                  {
                      m_pHyperlinkCallback->SetAnchor(pAnchor);

                      m_pHyperlinkCallback->SetCallbackPending(TRUE);
                      CallbackHandle hand = m_pScheduler->RelativeEnter(
                            m_pHyperlinkCallback, 0);
                      m_pHyperlinkCallback->SetCallbackHandle(hand);
                        }
                  else
                  {
                      // /Helps fix problem brought on by fix for PR 67170
                      // and PR 67536: allow for multiple simultaneous
                      // hurls at the same time.  This will only happen
                      // right after a seek as long as findNextPendingOn..
                      // only returns more than one anchor after a seek:
                      HX_RESULT pnrslt =
                        m_pHyperlinkCallback->AddAdditionalAnchor(pAnchor);
                      HX_ASSERT (HXR_OK == pnrslt);
                  }

                        return HXR_OK;
                    }
                }

            if (pAnchor->m_bExternal)
            {
                if (pAnchor->m_target.GetLength() > 0)
                {
                  // /Viper now has control over the "external" browser
                  // that is integrated into the Viper player UI.
                  // Sending the SMIL-file-specifed link's target value
                  // instead of just "_browser" fixes PR 56073:
                  pTarget = (const char*)pAnchor->m_target;
                }
                else // /Use old-school value:
                {
                  pTarget = "_browser";
                }
            }

            // /This is possibly used in at least two places, below:
            const char* pszEventName = "activateEvent";

            // /In order to decide what to do when pAnchor->m_target is
            // set, we need to figure out if that string matches a region
            // in the current presentation's layout.  If so, we need to
            // play the URL in that region, else we need to play it to
            // an external player (or to a browser if external="true"):
            BOOL bNeedToTargetANamedPlayerInstance = FALSE;
            if (pAnchor->m_target.GetLength() > 0)
            {
                HX_ASSERT("replace(ignored)"==pAnchor->m_show);

                if (!pAnchor->m_bTargetIsARegion  &&  !pAnchor->m_bExternal)
                {
                  // /Setting this and using it, below, fixes PR 55404:
                  bNeedToTargetANamedPlayerInstance = TRUE;
                }
                // /Helps fix PR 52891: pAnchor targets a region, so
                // activate the element that's been set up to play when
                // this link gets activated:
                else if (pAnchor->m_pNode  &&
                      // /Fixes PR 57845: above if() could have failed
                      // for other reasons, so make sure this is TRUE:
                      pAnchor->m_bTargetIsARegion)
                {
                  HX_RESULT rslt = HXR_OK;
                  rslt = m_pSmilParser->tryToResolveBeginEndEvents(
                        pszEventName, // /"activateEvent"
                        (const char*)pAnchor->m_pNode->m_id,
                        m_ulCurrentTime);
                  if SUCCEEDED(rslt)
                  {
                      handleElements();
                  }
                  goto cleanup;
                }
            }

            // /This fixes PR 77583 so that a, area, anchor all raise
            // activateEvents so that other elements can begin/end on
            // them, but only if the link is going to an external source,
            // i.e., not replacing the current presentation.  Some members
            // of the W3C SYMM Working Group that defined SMIL 2.0 said
            // that our implementation was faulty for not raising
            // activateEvents on anchors, but the spec isn't specific
            // about whether or not you need to raise this.   It's useful,
            // however, so just do it:
            if (pAnchor->m_bExternal  ||  pAnchor->m_show != "replace")
            {
                rsltOfRaisingActivateEvent =
                      m_pSmilParser->tryToResolveBeginEndEvents(
                      pszEventName, // /"activateEvent"
                      (const char*)pAnchor->m_pNode->m_id,
                      m_ulCurrentTime);
            }


            if(pAnchor->m_href[0] == '#')
            {
                pPlayer->AddRef();

                // relative URL, just seek...
                CHXString fragID = pAnchor->m_href.Mid(1);
                seekTo(fragID);

                pPlayer->Release();
            }
            else
            {
                IHXHyperNavigate* pHyper = 0;

                /* Calculate source player's play state noting that
                 * show's value can override the m_sourcePlaystate val:
                 * The logic comes out to:
                 * if (show=="pause"  ||  show="replace")
                 *   := pause 
                 * else
                 *   := m_sourcePlaystate
                 */
                SMILLinkPlaystate sourcePlaystate =
                      SMILLinkPlaystatePause;
                if ( ("pause"==pAnchor->m_show  ||
                      "replace"==pAnchor->m_show)  &&
                      !pAnchor->m_bExternal)
                {
                  sourcePlaystate = SMILLinkPlaystatePause;
                }
                else
                {
                  sourcePlaystate = pAnchor->m_sourcePlaystate;
                }

                m_bDestPlaystateIsPause = (SMILLinkPlaystatePause ==
                       pAnchor->m_destinationPlaystate);

                BOOL bIsOldSMIL1ShowEqualsPause =
                      ("pause" == pAnchor->m_show);

                // /If the destination player is different from the
                // source (current) player, and if the source player
                // is going to keep playing, then we need to set its
                // relative audio volume level:
                if (SMILLinkPlaystatePlay == sourcePlaystate  ||
                      // /Helps fix PR 55120: even if src player pauses,
                      // we may need to set dest player's audio level:
                      SMILLinkPlaystatePause == sourcePlaystate)
                {
                  // /Re-wrote this to implement destinationLevel on new
                  // or existing LPPlayer; helps fix PR 55120 & PR 55791:
                  if (pAnchor->m_ulSourceLevel_pct != 100  ||
                        pAnchor->m_ulDestinationLevel_pct != 100)
                  {
                      UINT16 uCurVol = 50;
                      IHXAudioPlayer* pAudioPlayer = NULL;
                      if (HXR_OK== pPlayer->QueryInterface(IID_IHXAudioPlayer,
                            (void **)&pAudioPlayer))
                      {
#if 0 /*XXXEH- need TLC work in order to enable this: */
                        // /XXXH- as soon as I can figure out how to
                        // get at the player (LPP) that gets created
                        // by the command:openWindow() call that'll
                        // happen, below, we'll be able to use
                        // pAudioPlayer->GetAudioVolume() instead, on
                        // both it and the current (source) player.
                        // With those IHXVolume objects that are
                        // returned for the respective players, we'll
                        // be able to set each players' volume level
                        // independently, & without affecting any other
                        // players' volumes in the process.
                        IHXVolume* pSourceAudioPlayerVolume =
                              pAudioPlayer->GetAudioVolume();
                        if (pSourceAudioPlayerVolume)
                        {
                            uCurVol = pSourceAudioPlayerVolume->GetVolume();
                            {
                              // /Same, so just set shared-by-all-
                              // players volume level to new val:
                              double fNewSourceVol = (double)(
                                    (float)uCurVol * (double(float(
                                    pAnchor->m_ulSourceLevel_pct)) /
                                    100.0) );
                              pSourceAudioVPlayerVolume->SetVolume(0);
                            }
                        }
                        HX_ASSERT(pSourceAudioPlayerVolume);
                        HX_RELEASE(pSourceAudioPlayerVolume);
#else
                        IHXVolume* pSourceVolume =
                              pAudioPlayer->GetDeviceVolume();
                        if (pSourceVolume)
                        {
                            uCurVol = pSourceVolume->GetVolume();
                            if (pAnchor->m_ulSourceLevel_pct  ==
                                  pAnchor->m_ulDestinationLevel_pct)
                            {
                              // /Same, so just set shared-by-all-
                              // players volume level to new val:
                              double fNewSourceVol = (double)(
                                    (float)uCurVol * (double(float(
                                    pAnchor->m_ulSourceLevel_pct)) /
                                    100.0) );
                              pSourceVolume->SetVolume(
                                    UINT16(ULONG32(
                                    (float)fNewSourceVol)));
                            }
                            else if (100 ==
                                  pAnchor->m_ulDestinationLevel_pct)
                            {
                              // /Dest. level==100: just adjust
                              // current player's tracks' soundLevels
                              // to sourceLevel.  Fixed PR 55791:
                              AdjustSoundLevelsOfAllCurrentTracks(
                                    (UINT16)m_uCurrentGroupIndex,
                                    pAnchor->m_ulSourceLevel_pct, NULL);
                            }
                            else
                            {
                              // /Dest. level (d) != current player's
                              // audio level (s), so set soundLevel to
                              // (s*(100/d)) for all source player
                              // tracks, then set IHXAudioPlayer
                              // volume to d:
                              double fS = double((float)
                                    pAnchor->m_ulSourceLevel_pct);
                              double fD = double((float)
                                    pAnchor->m_ulDestinationLevel_pct);
                              // /Avoid divide/0:
                              if (fD < XXXEH_MIN_DEST_AUDIOPLAYER_VOL)
                              {
                                  fD = XXXEH_MIN_DEST_AUDIOPLAYER_VOL;
                              }
                              UINT32 ulNewSourceLevel_pct =
                                    UINT32(float(fS * (100.0 / fD)));
                              // /Avoid too-loud source player trcks:
                              if (ulNewSourceLevel_pct <
                                    XXXEH_MIN_DEST_AUDIOPLAYER_VOL)
                              {
                                  ulNewSourceLevel_pct =
                                        XXXEH_MIN_DEST_AUDIOPLAYER_VOL;
                              }
                              // /Adjust current player's tracks:
                              AdjustSoundLevelsOfAllCurrentTracks(
                                    (UINT16)m_uCurrentGroupIndex,
                                    ulNewSourceLevel_pct, NULL);
                              // /Then adjust shared players'
                              // volume to get correct dest player
                              // volume (fixes PR 55791 case 3):
                                        double dNewVolumeOfAllPlayers  =
                                            ((double) fD) * ((double) uCurVol) / 100.0;
                                        UINT32 ulNewVolumeOfAllPlayers =
                                            (UINT32) dNewVolumeOfAllPlayers;
                              pSourceVolume->SetVolume(ulNewVolumeOfAllPlayers);
                            }
                        }
                        HX_ASSERT(pSourceVolume);
                        HX_RELEASE(pSourceVolume);
#endif
                      }
                      HX_ASSERT(pAudioPlayer);
                      HX_RELEASE(pAudioPlayer);
                  }
                }
                // /Changing to "if()" from "else if()", here, fixes
                // PR 85007: sourcePlaystate="pause" code was being
                // ignored due to new soundLevel code, above, that
                // co-opted the prior conditional of the if() statement
                // without allowing this original "pause" handling-code to
                // run:
                if (SMILLinkPlaystatePause == sourcePlaystate)
                {
                  // /If external or new player, then just pause the
                  // current player, otherwise save our spot and launch
                  // the new URL in the current player, then wait until
                  // that presentation stops and restart the saved one
                  // at the saved spot:
                  if (pAnchor->m_bExternal  ||  "new"==pAnchor->m_show  ||
                        bIsOldSMIL1ShowEqualsPause  ||
                        bNeedToTargetANamedPlayerInstance)
                  {
                      pPlayer->Pause();
                  }
                  // /Else save where we are so we can retart where we
                  // left off when the new presentation finishes:
                  else
                  {
                      if (!m_pPlayerResumeStack)
                      {
                        m_pPlayerResumeStack = new CHXStack;
                      }
                      if (m_pPlayerResumeStack)
                      {
                        CSmilPlayerResumePos* pResumePos =
                              new CSmilPlayerResumePos(
                              m_uCurrentGroupIndex,
                              m_ulCurrentTime,
                              sourcePlaystate);
                        m_pPlayerResumeStack->Push(pResumePos);
                      }
                  }
                }
                // /sourcePlaystate == stop, so stop it:
                else if (SMILLinkPlaystateStop == sourcePlaystate)
                {
                  // /m_show must be "new" (not "pause" or "replace"):
                  HX_ASSERT("new"==pAnchor->m_show);

                  pPlayer->Stop();
                }
                else
                {
                  // /sourcePlaystate had better be play, already
                  // handled above in prior if():
                  HX_ASSERT(SMILLinkPlaystatePlay == sourcePlaystate);
                }

                CHXString urlString;
                convertURL((const char*) m_pParent->getURLRoot(),
                               (const char*) m_pParent->getURLPrefix(),
                               (const char*) pAnchor->m_href,
                               urlString);

                BOOL bIsMailtoURL = !(strncmp("mailto:", urlString, 7));
                if (bIsMailtoURL)
                {
                  pTarget = "_browser";
                }

                BOOL bIsCommandOpenWindowURL = FALSE;

                // /Now, if show="new", we need to create a new player
                // and open the URL in it:
                // This is also true of the old SMIL 1.0 show="pause":
#if defined(XXXEH_USE_OPENWINDOW_METHOD)
                BOOL bIsCommandURL = !(strncmp("command:", urlString, 8));
                if (bIsCommandURL)
                {
                  const char* pCommandAction = ((const char*)urlString)+8;
                  // /Look for "command:[s*]openwindow" where [s*] is 0
                  // or more white-space characters:
                  while (isspace(*pCommandAction))
                  {
                      pCommandAction++;
                  }
                  if (*pCommandAction)
                  {
                      bIsCommandOpenWindowURL = !(strnicmp("openWindow",
                        pCommandAction, 10));
                  }
                }
                if (!bIsCommandURL  &&
                      (!strncmp("new", pAnchor->m_show,3)  ||
                      bIsOldSMIL1ShowEqualsPause  ||
                      bNeedToTargetANamedPlayerInstance) )
#else
                if (!strncmp("new", pAnchor->m_show,3)  ||
                      bIsOldSMIL1ShowEqualsPause  ||
                      bNeedToTargetANamedPlayerInstance)
#endif
                {
                  // /If show is 'new' then either fire off a new
                  // player or send it to the browser, depending on
                  // what the value of m_bExternal is:
                  if (pAnchor->m_bExternal)
                  {
                      // /If it's external, then go ahead and open in
                      // the browser and let it possibly send the URL
                      // back to the current player; the SMIL2 Linking
                      // Module Draft is silent on what to do here (as
                      // of 2001-02-01):
                      if(HXR_OK == pPlayer->QueryInterface(
                            IID_IHXHyperNavigate, (void**)&pHyper))
                      {
                                // XXXMEH - use updated method which checks
                                // for <rn:param> children
                                GoToURLWithParamCheck(urlString, pTarget, pSendTo,
                                                      pHyper, pAnchor, m_pContext,
                                          !bIsOnLoadActuation);
//                                pHyper->GoToURL(urlString, pTarget);
                                pHyper->Release();
                      }
                  }
                  else // /Create new player and open URL in it:
                  {
#if defined(XXXEH_USE_OPENWINDOW_METHOD)
                      // /Have the TLC create a new player for us via
                      // "command:openWindow(windowName,URL,behavior)":
                      CHXString pOpenWindowURL = "command:openWindow(";
                      // /Now, add the "target", if specified:
                      if (pAnchor->m_target.GetLength() > 0)
                      {
                        pOpenWindowURL += pAnchor->m_target;
                      }
                      // /Otherwise, we need to make up a unique name:
                      else
                      {
                        pOpenWindowURL += pAnchor->m_pNode->m_id;
                        char* pTmp = new char[64];
                        ULONG32 ulCurTime = HX_GET_TICKCOUNT();
                        itoa(ulCurTime, pTmp, 16);
                        pOpenWindowURL += pTmp;
                        delete [] pTmp;
                      }
                      pOpenWindowURL += ",";
                      pOpenWindowURL += urlString;
                      // /Set the source (current) player's behavior:
                      switch (sourcePlaystate)
                      {
                        case SMILLinkPlaystatePlay:
                        {
                            pOpenWindowURL += ",behavior=continue";
                        }
                        break;

                        case SMILLinkPlaystatePause:
                        {
                            pOpenWindowURL += ",behavior=pause";
                        }
                        break;

                        case SMILLinkPlaystateStop:
                        {
                            pOpenWindowURL += ",behavior=stop";
                        }
                        break;

                        default:
                        {
                            HX_ASSERT(0&&"bad sourcePlaystate");
                        }
                        break;
                      }
                      pOpenWindowURL += ")";
                      if(HXR_OK == pPlayer->QueryInterface(
                            IID_IHXHyperNavigate, (void**)&pHyper))
                      {
                                // XXXMEH - use updated method which checks
                                // for <rn:param> children
                                GoToURLWithParamCheck(pOpenWindowURL, pTarget, 
                              // /Ignore sendTo if external="false":
                              NULL,
                                        pHyper, pAnchor, m_pContext,
                              !bIsOnLoadActuation);
//                      pHyper->GoToURL(pOpenWindowURL, pTarget);
                        pHyper->Release();
                      }
#else
#pragma message(" Non-command:openWindow() new-player creation not finished")
                      IHXClientEngine* pClientEngine =
                            m_pParent->getClientEngine();
                      if (pClientEngine)
                      {
                        IHXPlayer* pNewPlayer = NULL;
                        if (HXR_OK == pClientEngine->CreatePlayer(
                              pNewPlayer)  &&  pNewPlayer)
                        {
                            pNewPlayer->SetClientContext(m_pContext);
                            // /XXXEH- delete player when done!
                            HX_RESULT openrslt = pNewPlayer->OpenURL(
                                  (const char*)urlString);
                            if (HXR_OK == openrslt)
                            {
                              // /If playstate is pause, then we
                              // don't want to begin the player:
                              if (SMILLinkPlaystatePause !=
                                   pAnchor->m_destinationPlaystate) 
                              {
                                  pNewPlayer->Begin();
                              }
                              return HXR_OK; 
                            }
                        }
                      }
#endif /* (else ndef) XXXEH_USE_OPENWINDOW_METHOD.*/
                  }
                }
                else if (pAnchor->m_bExternal  ||
                      // /This fixes PR 76406, as was done below to
                      // fix PR 62276: if it's *any* kind of "command:.."
                      // URL, send it through IRMAHypernav:
                      bIsCommandURL  ||
                      // /Fixes PR 78480: if it's a "mailto:..." link
                      // it needs to go through IMRAHypernav:
                      bIsMailtoURL  ||
                      // /This fixes PR 62276 by making sure
                      // command:openwindow ULRs go through IRMAHypernav
                      // and pPlayer->OpenURL().  This could be construed
                      // as being contrary to the SMIL 2.0 spec by
                      // effectively overriding show="replace", but it is
                      // a proprietary URL and thus can be handled in a
                      // proprietary way, i.e, won't break interoperability:
                      bIsCommandOpenWindowURL)
                {
                  if(HXR_OK == pPlayer->QueryInterface(
                        IID_IHXHyperNavigate, (void**)&pHyper))
                  {
                            // XXXMEH - use updated method which checks
                            // for <rn:param> children
                            GoToURLWithParamCheck(urlString, pTarget, pSendTo,
                                                  pHyper, pAnchor, m_pContext,
                                      !bIsOnLoadActuation);
//                    pHyper->GoToURL(urlString, pTarget);
                      HX_RELEASE(pHyper);
                  }
                }
                else // /Open in same window:
                {
                  // /Fixes PR 67630: OpenURL() call resulted in player
                  // destroying our parent CSmilRenderer which in turn
                  // destroyed us, and subsequent HandleSetCursor() call
                  // was causing hang as "this" has been deleted.  Fix
                  // is to AddRef the parent before calling OpenURL:
                  m_pParent->AddRef();

                  // /This fixes the subsequent problem (hang) in PR 91817
                  // When multiple links are queued to hurl simultaneously,
                  // just hurl the first replace-presentation-in-main-
                  // player one we come to and delete all the rest:
                  if (m_pHyperlinkCallback)
                  {
                      m_pHyperlinkCallback->ClearExtraAnchorList();
                  }
                  
                  HX_RESULT openrslt = HXR_FAIL;

                  BOOL bHandledByPlayer2 = FALSE;
                  IHXCommonClassFactory* pFactory=
                        m_pParent->getFactory();
                  IHXPlayer2* pPlayer2 = NULL;
                  if (pFactory  &&  HXR_OK == 
                        pPlayer->QueryInterface(IID_IHXPlayer2,
                        (void**)&pPlayer2))
                  {
                      IHXRequest* pRequest = NULL;
                      pFactory->CreateInstance(CLSID_IHXRequest,
                            (void**) &pRequest);
                      if (pRequest)
                      {
                        IHXValues* pValues = 0;
                        if(HXR_OK == pFactory->CreateInstance(
                              CLSID_IHXValues, (void**)&pValues))
                        {
                            // /Fixes open-in-same-window version of
                            // PR 84160: call OpenRequest() (with
                            // AutoActivated prop) instead of OpenURL:
                            pValues->SetPropertyULONG32("AutoActivated",
                                  (ULONG32)(bIsOnLoadActuation));

                            pRequest->SetRequestHeaders(pValues);
                            pRequest->SetURL((const char*)urlString);
                            bHandledByPlayer2 = TRUE;
                            openrslt = pPlayer2->OpenRequest(pRequest);

                            HX_RELEASE(pValues);
                        }
                        HX_RELEASE(pRequest);
                      }
                      HX_RELEASE(pPlayer2);
                  }
                  // /No IHXPlayer2 or other prob; just call OpenURL():
                  if (!bHandledByPlayer2)
                  {
                      openrslt = pPlayer->OpenURL((const char*)urlString);
                  }
                  if (HXR_OK == openrslt)
                  {
                      // /First, set cursor back to pre-hyperlink one
                      // so that the new presentation doesn't have the
                      // link "hand" cursor on it at first:
#if defined(_WINDOWS)
                      m_bNeedToSetHyperlinkCursor = FALSE;
#endif
                      HandleSetCursor();

                      pPlayer->Begin();
                      // /NOTE: if m_bDestPlaystateIsPause, then we'll
                      // pause the player after
                      // IHXClientAdviseSink::OnBuffering() has been
                      // called with 100% done.

                  }
                  // /Remove ref added prior to OpenURL(), above:
                  m_pParent->Release();
                }
            }
          }
      }
    }

cleanup:
    if SUCCEEDED(rsltOfRaisingActivateEvent)
    {
      handleElements();
    }

    return rc;
}

 
STDMETHODIMP CSmilDocumentRenderer::HandleCharEvent(UINT16 uChar)
{ 
    HX_RESULT rc = HXR_FAIL;

    // /XXXEH- TODO: fix this after interop.  We should probably treat this 
    // separate from events and have a "tryToResolveCharPressEvents()" func. 
    // Also, handle wide characters (right???): 
    char* pEventName = new char[32];
    sprintf(pEventName,"accesskey(%c)", uChar); /* Flawfinder: ignore */
// /#define XXXEHODGE_DEBUG_OUTPUT_HANDLECHAREVENT
#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG_OUTPUT_HANDLECHAREVENT)
{FILE* f1 = ::fopen("c:\\smil2.txt", "a+"); ::fprintf(f1, "\nHandleCharEvent(%u), currentTime: %lu\n", uChar, m_ulCurrentTime);::fclose(f1);}
#endif
    rc = m_pSmilParser->tryToResolveBeginEndEvents(pEventName,
          // /Fixes PR 69849 (and needed for PR 58290): the new optimized
          // code that uses maps instead of lists didn't take into account
          // the fact that accesskey events have no associated event-element
          // ID since they come from the keyboard, so we now use this string
          //(which is an invalid XML id) as the fake ID:
          ACCESSKEY_EVENT_FAKE_ID_STRING,
          m_ulCurrentTime);
    if SUCCEEDED(rc)
    {
#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG_OUTPUT_HANDLECHAREVENT)
{FILE* f1 = ::fopen("c:\\smil2.txt", "a+"); ::fprintf(f1, "\n    HandleCharEvent(%u): calling tryToResolveBeginEndEvents() returned HXR_OK at time: %lu\n", uChar, m_ulCurrentTime);::fclose(f1);}
#endif
      handleElements();
    }

    // /Next, let's see if we have an anchor|a|area tag awaiting an
    // accesskey event.  Go through all currently-playing media and see if
    // any of their anchors are awaiting a keypress:
    if (m_pPlayToAssocList)
    {
        CHXSimpleList::Iterator indx = m_pPlayToAssocList->Begin();
        for(; indx != m_pPlayToAssocList->End(); ++indx)
        {
          SMILPlayToAssoc* pAssoc = (SMILPlayToAssoc*)(*indx);
          if(pAssoc->m_uGroupIndex == m_uCurrentGroupIndex &&
             pAssoc->m_pHyperlinks)
          {
              if(pAssoc->m_ulDelay == (UINT32)-1)
              {
                // not resolved yet
                break;
              }
              CHXSimpleList::Iterator indxA = pAssoc->m_pHyperlinks->Begin();
              for(; indxA != pAssoc->m_pHyperlinks->End(); ++indxA)
              {
                CSmilAAnchorElement* pAnchor = 
                      (CSmilAAnchorElement*)(*indxA);
                // /XXXEH- TODO: handle wide chars here:
                if (pAnchor->m_accesskey[0] == uChar)
                {
                    HXxRect regionRect;
                    regionRect.left = 0;
                    regionRect.top = 0;
                    regionRect.right = (INT32)ANCHOR_POS_DONTCARE;
                    regionRect.bottom = (INT32)ANCHOR_POS_DONTCARE;

                    ULONG32 ulTime = m_ulCurrentTime;
                    if (m_bSMILPresentationHasEnded)
                    {
                      // /1/2 sec grace for last time sync to come
                      // after end of presentation:
                      ulTime -= (ulTime>500? 500: ulTime);
                    }

                    if (pAnchor->isCurrentLink(ulTime,
                          ANCHOR_POS_DONTCARE, ANCHOR_POS_DONTCARE,
                          regionRect,
                          // /XXXEH- need to maybe deal with resized region
                          // if multiple hotspots (links w/coords) exist on
                          // same media:
                          FALSE, 1.0, 1.0))
                    {
                      // /BE VERY CAREFUL HERE!!!: the following might
                      // cause a replacement of this renderer instance by
                      // launching a new presentation, so we need to be
                      // sure not to do anything but return after this:
                      rc = handleHyperlinkTraversal(pAnchor);
                      return rc;
                    }
                }
              }
          }
        }
    }

    return rc;
}


HX_RESULT 
CSmilDocumentRenderer::handleNamedEvent(const char* pRegionID,
                                        const char* pMediaID,
                              const char* pEventName)
{ 
    HX_RESULT rc = HXR_OK;

    if (m_pSmilParser)
    {
        HX_RESULT rslt = HXR_OK;
        rslt = m_pSmilParser->tryToResolveBeginEndEvents(pEventName,
                                                         pMediaID,
                                                         m_ulCurrentTime);
        if (SUCCEEDED(rslt))
        {
          handleElements();
        }
    }

    return rc;
}

STDMETHODIMP CSmilDocumentRenderer::HandleGotFocus(const char* pRegionID,
                                                   const char* pMediaID)
{
    return handleNamedEvent(pRegionID, pMediaID, "focusInEvent");
}

STDMETHODIMP CSmilDocumentRenderer::HandleLostFocus(const char* pRegionID,
                                                    const char* pMediaID)
{
    return handleNamedEvent(pRegionID, pMediaID, "focusOutEvent");
}


HX_RESULT
CSmilDocumentRenderer::detachSite(IHXSite* pSite)
{
    HX_RESULT rc = HXR_OK;

    // remove site from site info list

    LISTPOSITION pos = m_pSiteInfoList->GetHeadPosition();
    while(pos)
    {
      SMILSiteInfo* pSiteInfo = 
          (SMILSiteInfo*)m_pSiteInfoList->GetAt(pos);
      if(pSiteInfo->m_pRendererSite == pSite)
      {
          CSmilBasicRegion* pRegion = getRegionByID(pSiteInfo->m_regionID);
          if(pRegion)
          {
            pRegion->m_pSite->DestroyChild(pSite);
          }
          m_pSiteInfoList->RemoveAt(pos);

          // at this time go through the PlayToAssocList list and
          // change any that reference the soon to be removed 
          // site info.

          if (m_pPlayToAssocList)
          {
            CHXSimpleList::Iterator i = m_pPlayToAssocList->Begin();
            for (; i!=m_pPlayToAssocList->End(); ++i)
            {
                SMILPlayToAssoc* pPlayToAssoc = (SMILPlayToAssoc*)(*i);
                
                LISTPOSITION pos = pPlayToAssoc->m_pSiteInfoList->Find(pSiteInfo);
                pPlayToAssoc->m_pSiteInfoList->RemoveAt(pos);
            }
          }
          
          HX_DELETE(pSiteInfo);
          break;
      }
      m_pSiteInfoList->GetNext(pos);
    }

    return rc;
}

HX_RESULT 
CSmilDocumentRenderer::doSourceUpdate(SMILPlayToAssoc* pPlayToAssoc,
                              CSmilSourceUpdate* pUpdateElement)
{
    HX_RESULT         rc = HXR_OK;
    BOOL        bUpdated = FALSE;
    const char*       pID = (const char*)pUpdateElement->m_srcID;    
    CSmilElement*   pThisElement = m_pSmilParser->findElement(pID);

    if (pUpdateElement->m_updateTag == UpdateDuration)
    {
      if(pThisElement->m_bBeginOffsetSet  &&
            // /Helps fix PR 79699 et al:
            !pUpdateElement->m_bDurationIsPureOfDelay)
      {         
          if((INT32)pUpdateElement->m_ulUpdatedDuration > pThisElement->m_lBeginOffset)
          {
            pPlayToAssoc->m_ulDuration = pUpdateElement->m_ulUpdatedDuration -
            pThisElement->m_lBeginOffset;
          }
          else
          {
            pPlayToAssoc->m_ulDuration = 0;
          }
      }
      else
      {
          pPlayToAssoc->m_ulDuration = pUpdateElement->m_ulUpdatedDuration;
      }
      bUpdated = TRUE;
    }
    else if (pUpdateElement->m_updateTag == UpdateDelay)
    {
      pPlayToAssoc->m_ulDelay = pUpdateElement->m_ulUpdatedDelay;
      bUpdated = TRUE;
    }

    if (bUpdated)
    {
      CHXMapLongToObj::Iterator j = pPlayToAssoc->m_sourceMap.Begin();    
      CHXSimpleList* pRendererInfoList = (CHXSimpleList*)(*j);
      SMILSourceInfo* pSourceInfo = (SMILSourceInfo*)pRendererInfoList->GetHead();
      IHXLayoutStream* pLayoutStream = NULL;
      
      if(HXR_OK == 
         pSourceInfo->m_pStream->QueryInterface(IID_IHXLayoutStream, 
                                        (void**)&pLayoutStream))
      {
          IHXValues* pStreamProps = NULL;
          if(HXR_OK == pLayoutStream->GetProperties(pStreamProps))
          {
            if (pUpdateElement->m_updateTag == UpdateDuration)
            {
#if defined(_DEBUG)  &&  defined(XXXEH_DEBUG_HANDLESOURCE_AND_TRACKDURATIONSET)
ULONG32 ulPriorDuration = pPlayToAssoc->m_ulDuration;
BOOL bPriorDuration = TRUE;
if (HXR_OK != pStreamProps->GetPropertyULONG32("duration", ulPriorDuration))
{
    bPriorDuration = FALSE;
}
{FILE* f1 = ::fopen("c:\\smil2AddDuration.txt", "a+");
::fprintf(f1, "$$$ CSmilDocumentRenderer::doSourceUpdate(pID=%s, "
     "pPlayToAssoc->m_ulDuration=%lu, pThisElement->m_ulDuration=%lu,"
     "prior stream duration=%lu%s\n", (const char*)pID,
     pPlayToAssoc->m_ulDuration, pThisElement->m_ulDuration, ulPriorDuration,
     bPriorDuration?"":"(NO PRIOR DUR)");  ::fclose(f1);}
#endif
                pStreamProps->SetPropertyULONG32("duration",
                        pPlayToAssoc->m_ulDuration);
            }
            else if (pUpdateElement->m_updateTag == UpdateDelay)
            {
#if defined(_DEBUG)  &&  defined(XXXEH_DEBUG_HANDLESOURCE_AND_TRACKDURATIONSET)
ULONG32 ulPriorDelay = pPlayToAssoc->m_ulDelay;
BOOL bPriorDelay = TRUE;
if (HXR_OK != pStreamProps->GetPropertyULONG32("delay",     ulPriorDelay))
{
    bPriorDelay = FALSE;
}
{FILE* f1 = ::fopen("c:\\smil2AddDuration.txt", "a+");
::fprintf(f1, "$$$ CSmilDocumentRenderer::doSourceUpdate(pID=%s, "
     "pPlayToAssoc->m_ulDelay=%lu, pThisElement->m_ulDelay=%lu,"
     "prior stream delay=%lu%s\n", (const char*)pID,
     pPlayToAssoc->m_ulDelay, pThisElement->m_ulDelay, ulPriorDelay,
     bPriorDelay?"":"(NO PRIOR DELAY)");  ::fclose(f1);}
#endif
                pStreamProps->SetPropertyULONG32("delay",
                      pPlayToAssoc->m_ulDelay);
            }

            pLayoutStream->SetProperties(pStreamProps);
            HX_RELEASE(pStreamProps);
          }
          HX_RELEASE(pLayoutStream);
      }
      updateSiteEvents(pPlayToAssoc->m_uGroupIndex);
    }

    return rc;
}

HX_RESULT
CSmilDocumentRenderer::updateStreamTiming(const char* pElementID,
                                UINT32 ulDuration)
{
    HX_RESULT rc = HXR_OK;

    CSmilElement* pElement = m_pSmilParser->findElement(pElementID);

    if(pElement)
    {
      // find the SMILPlayToAssoc associated with this element ID
      SMILPlayToAssoc* pPlayToAssoc = NULL;
      if(m_pPlayToAssocList)
      {
          CHXSimpleList::Iterator i = m_pPlayToAssocList->Begin();
          for (; i!=m_pPlayToAssocList->End(); ++i)
          {
            SMILPlayToAssoc* pThisAssoc = (SMILPlayToAssoc*)(*i);
            if(pThisAssoc->m_id == (const char*)pElement->m_pNode->m_id  &&
                  // /Added this while re-fixing PR 69639; be sure to
                  // ignore already-stopped track & keep looking in
                  // case element restart made new track w/same id:
                  !pThisAssoc->m_bTrackStopped)
            {
                pPlayToAssoc = pThisAssoc;
                break;
            }
          }
      }
      if(pPlayToAssoc && 
         pPlayToAssoc->m_sourceMap.GetCount() > 0)
      {
          pPlayToAssoc->m_ulDuration = ulDuration;
          CHXMapLongToObj::Iterator j = pPlayToAssoc->m_sourceMap.Begin();
      
          CHXSimpleList* pRendererInfoList = (CHXSimpleList*)(*j);
          SMILSourceInfo* pSourceInfo = (SMILSourceInfo*)pRendererInfoList->GetHead();
          IHXLayoutStream* pLayoutStream = NULL;
          if(HXR_OK == 
            pSourceInfo->m_pStream->QueryInterface(IID_IHXLayoutStream, 
                                        (void**)&pLayoutStream))
          {
            IHXValues* pStreamProps = NULL;
            if(HXR_OK == pLayoutStream->GetProperties(pStreamProps))
            {
#if defined(_DEBUG)  &&  defined(XXXEH_DEBUG_HANDLESOURCE_AND_TRACKDURATIONSET)
{FILE* f1 = ::fopen("c:\\smil2AddDuration.txt", "a+");
::fprintf(f1, "^v^ CSmilDocumentRenderer::updateStreamTiming(%s, duration=%lu),"
        "pPlayToAssoc->m_ulDuration=%lu, pElement->m_ulDuration=%lu\n",
    (const char*)pElementID, ulDuration, pPlayToAssoc->m_ulDuration, pElement->m_ulDuration);
::fclose(f1);}
#endif
                pStreamProps->SetPropertyULONG32("duration", ulDuration);
                pLayoutStream->SetProperties(pStreamProps);
                pStreamProps->Release();

                // XXX HP THIS IS NO-OP!!
                // update delay for subsequent elements
                //updateStreamDelay(pPlayToAssoc->m_uGroupIndex,
                //                  pPlayToAssoc->m_ulDelay);
            }
            pLayoutStream->Release();
          }
          updateSiteEvents(pPlayToAssoc->m_uGroupIndex);
      }
    }

    return rc;
}

HX_RESULT
CSmilDocumentRenderer::updateStreamDelay(UINT16 uGroupIndex,
                               UINT32 ulInitialDelay)
{
    HX_RESULT rc = HXR_OK;

    if(m_pPlayToAssocList)
    {
      CHXSimpleList::Iterator i = m_pPlayToAssocList->Begin();
      for (; i!=m_pPlayToAssocList->End(); ++i)
      {
          SMILPlayToAssoc* pPlayToAssoc = (SMILPlayToAssoc*)(*i);
          if(pPlayToAssoc->m_uGroupIndex == uGroupIndex &&
            pPlayToAssoc->m_ulDelay > ulInitialDelay)
          {
            UINT32 ulNewDelay = pPlayToAssoc->m_ulDelay;
            pPlayToAssoc->m_ulDelay = ulNewDelay;
            if(pPlayToAssoc->m_sourceMap.GetCount() > 0)
            {
                CHXMapLongToObj::Iterator j = pPlayToAssoc->m_sourceMap.Begin();

                CHXSimpleList* pRendererList = (CHXSimpleList*)(*j);

                CHXSimpleList::Iterator k = pRendererList->Begin();
                for (; k != pRendererList->End(); ++k)
                {
                  SMILSourceInfo* pSMILSourceInfo = (SMILSourceInfo*)(*k);
                  IHXLayoutStream* pLayoutStream = NULL;
                  if(HXR_OK == 
                      pSMILSourceInfo->m_pStream->QueryInterface(
                      IID_IHXLayoutStream,
                      (void**)&pLayoutStream))
                  {
                      IHXValues* pStreamProps = NULL;
                      if(HXR_OK == pLayoutStream->GetProperties(
                        pStreamProps))
                      {
                        pStreamProps->SetPropertyULONG32("delay", 
                            pPlayToAssoc->m_ulDelay);
                        pLayoutStream->SetProperties(pStreamProps);
                        pStreamProps->Release();
                      }
                      pLayoutStream->Release();
                  }
                }
            }
          }
      }
    }
    return rc;
}

HX_RESULT CSmilDocumentRenderer::updateSiteEvents(UINT16 uGroupIndex)
{
    HX_RESULT rc = HXR_OK;

    if(m_pPlayToAssocList)
    {
#if defined(XXXMEH_OLD_UPDATE_SITE_EVENTS)
        // Save the animation events so we 
        // can re-add them later
        // XXXMEH - TODO - if an animation beging is tied
        // to the begin of a media element whose previously-scheduled
        // begin time changes, then we need to be able change 
        // the animate event.
        CHXSimpleList cTmp;
        if (m_pEventList)
        {
            LISTPOSITION pos = m_pEventList->GetHeadPosition();
            while (pos)
            {
                CSmilLayoutEvent* pEvent =
                    (CSmilLayoutEvent*) m_pEventList->GetNext(pos);
                if (pEvent && pEvent->m_type == CSmilLayoutEvent::eAnimate)
                {
                    // Cast this to an animate event
                    CSmilAnimateEvent* pAnim = (CSmilAnimateEvent*) pEvent;
                    // Create a copy
                    CSmilAnimateEvent* pNew = new CSmilAnimateEvent(*pAnim);
                    if (pNew)
                    {
                        // Add this to the temporary list
                        cTmp.AddTail((void*) pNew);
                    }
                }
            }
        }
#endif

        // blow away current events
        // XXXMEH - 06/27/2001 Why do we want to do this? I'm going to 
        // comment this out now and simply do a *check* below to 
        // see if the show or hide time for the event changed. If
        // it didn't, then I'm going to leave the event alone.
#if defined(XXXMEH_OLD_UPDATE_SITE_EVENTS)
        removeGroupEvents(uGroupIndex);
#endif

        removeActiveTransitions();

        LISTPOSITION ptPos = m_pPlayToAssocList->GetHeadPosition();
        while (ptPos)
        {
            SMILPlayToAssoc* pPlayToAssoc = (SMILPlayToAssoc*) m_pPlayToAssocList->GetNext(ptPos);
            if (pPlayToAssoc &&
                pPlayToAssoc->m_uGroupIndex == uGroupIndex &&
                pPlayToAssoc->m_pSiteInfoList)
            {
                LISTPOSITION siPos = pPlayToAssoc->m_pSiteInfoList->GetHeadPosition();
                while (siPos)
                {
                    SMILSiteInfo* pSiteInfo = (SMILSiteInfo*) pPlayToAssoc->m_pSiteInfoList->GetNext(siPos);
                    if (pSiteInfo)
                    {
                        IHXSite* pRegionSite = NULL;
                        CSmilBasicRegion* pRegion = getRegionByID(pSiteInfo->m_regionID);
                        if(pRegion)
                        {
                            pRegionSite = pRegion->m_pSite;
                        }

                        // Start time is delay
                        // End time is delay plus duration
                        UINT32 ulStartTime = pPlayToAssoc->m_ulDelay;
                        UINT32 ulEndTime   = pPlayToAssoc->m_ulDelay + pPlayToAssoc->m_ulDuration;

                        // Assign the playTo start and end times to the siteinfo
                        // delay and duration
                        pSiteInfo->m_ulDelay    = ulStartTime;
                        pSiteInfo->m_ulDuration = ulEndTime;

                        // Get the currently scheduled show event for this media
                        CSmilShowSiteEvent* pCurShowEvent = getShowHideEvent(pPlayToAssoc->m_id,
                                                                             pSiteInfo->m_regionID,
                                                                             TRUE);
                        if (pCurShowEvent)
                        {
                            // Is the currently scheduled show event time different
                            // from the current start time? If so, then we need to
                            // update the start time.
                            if (ulStartTime != pCurShowEvent->m_ulEventTime)
                            {
                        // /Note: we can get here when x in seq gets
                        // duration extended during playback & next-
                        // in-seq gets further delayed as a consequence.

                                // First remove the event from the list
                                removeEvent(pCurShowEvent);
                                // Update the event time
                                pCurShowEvent->m_ulEventTime = ulStartTime;
                                // Add the event back in
                                insertEvent(pCurShowEvent);
                            }
                        }

                        // Get the currently scheduled hide event for this media
                        CSmilShowSiteEvent* pCurHideEvent = getShowHideEvent(pPlayToAssoc->m_id,
                                                                             pSiteInfo->m_regionID,
                                                                             FALSE);
                        if (pCurHideEvent)
                        {
                            // Get the remove time for this media
                            UINT32 ulRemoveTime = 0;
                            HX_RESULT rv = m_pSmilParser->computeRemoveTime((const char*) pPlayToAssoc->m_id,
                                                                            ulRemoveTime);
                            if (FAILED(rv))
                            {
                                // We couldn't currently compute the remove
                                // time, so we set it way out, assuming it will
                                // get pulled back later
                                ulRemoveTime = WAY_IN_THE_FUTURE;
                            }
                            // Is the currently scheduled show event time different
                            // from the current start time? If so, then we need to
                            // update the start time.
                            if (ulRemoveTime != pCurHideEvent->m_ulEventTime)
                            {
                                // First remove the event from the list
                                removeEvent(pCurHideEvent);
                                // Update the event time
                                pCurHideEvent->m_ulEventTime = ulRemoveTime;
                                // Get the this event's group duration
                                UINT32 ulGroupDur = 0;
                                if (getGroupDuration(pCurHideEvent->m_uGroupIndex, ulGroupDur))
                                {
                                    // Get the fill behavior
                                    FillType eFill = getMediaFillBehavior((const char*) pCurHideEvent->getMediaID());
                                    // We got the group duration.
                                    // Is our new hide time scheduled for the
                                    // group duration?
                                    if (pCurHideEvent->m_ulEventTime == ulGroupDur &&
                                        eFill                        != FillRemove)
                                    {
                                        // Yes, it is, so set the flag that
                                        // says to ignore the hide
                                        pCurHideEvent->m_bIgnorEvent = TRUE;
                                    }
                                    else
                                    {
                                        // No, the hide is NOT scheduled for the group
                                        // duration, so clear the flag that
                                        // says to ignore the hide
                                        pCurHideEvent->m_bIgnorEvent = FALSE;
                                    }
                                }
                                // Add the event back in
                                insertEvent(pCurHideEvent);
                                // Set the event time into the element
                                setElementRemoveTime(pPlayToAssoc->m_id, ulRemoveTime);
                            }
                        }
#if defined(HELIX_FEATURE_SMIL2_TRANSITIONS)
                        // Do we have a transIn?
                        if (pPlayToAssoc->m_beginTransition.GetLength() > 0)
                        {
                            CSmilTransitionInfo* pTransInfo = getTransition(pPlayToAssoc->m_beginTransition);
                            if (pTransInfo)
                            {
                                if (pSiteInfo->m_ulDelay < m_ulCurrentTime)
                                {
                                    CSmilTransitionEvent* pTransEvent =
                                        new CSmilTransitionEvent(pPlayToAssoc->m_ulDelay,
                                                                 pPlayToAssoc,
                                                                 pSiteInfo,
                                                                 TRUE,
                                                                 this);
                                    if (pTransEvent)
                                    {
                                        insertEvent(pTransEvent);
                                    }
                                }
                                else if (m_ulCurrentTime <
                                         pSiteInfo->m_ulDelay + pTransInfo->m_pTrans->m_ulDuration)
                                {
                                    // don't schedule the transition... but build a state
                                    // and start the transition...
                                    startTransition(pSiteInfo->m_ulDelay,
                                                    pTransInfo,
                                                    pSiteInfo,
                                                    pPlayToAssoc,
                                                    NULL,
                                                    m_ulCurrentTime,
                                                    TRUE);
                                }
                            }
                        }
                        // Do we have a transOut?
                        if (pPlayToAssoc->m_endTransition.GetLength() > 0)
                        {
                            CSmilTransitionInfo* pTransInfo = getTransition(pPlayToAssoc->m_endTransition);
                            if (pTransInfo)
                            {
                                // if the end of the clip minus the duration of the 
                                // transition is greater than the current time...
                                if (pSiteInfo->m_ulDuration - pTransInfo->m_pTrans->m_ulDuration > m_ulCurrentTime)
                                {
                                    HX_ASSERT(pSiteInfo->m_ulDelay + pSiteInfo->m_ulDuration < m_ulCurrentTime);
                                    // simply schedule a transition event to occur when the
                                    // current time reaches the end of the clip minus the 
                                    // duration of the transition.
                                    CSmilTransitionEvent* pTransEvent =
                                        new CSmilTransitionEvent(pSiteInfo->m_ulDuration -
                                                                 pTransInfo->m_pTrans->m_ulDuration,
                                                                 pPlayToAssoc,
                                                                 pSiteInfo,
                                                                 FALSE,
                                                                 this);
                                    if (pTransEvent)
                                    {
                                        insertEvent(pTransEvent);
                                    }
                                }
                                else if (pSiteInfo->m_ulDuration < m_ulCurrentTime)
                                {
                                    startTransition(pSiteInfo->m_ulDelay +
                                                    pSiteInfo->m_ulDuration, 
                                                    pTransInfo,
                                                    pSiteInfo,
                                                    pPlayToAssoc,
                                                    NULL,
                                                    m_ulCurrentTime,
                                                    FALSE);
                                }
                            }
                        }
#endif /* #if defined(HELIX_FEATURE_SMIL2_TRANSITIONS) */
                    } // if (pSiteInfo)
                }
            }
        }
#if defined(XXXMEH_OLD_UPDATE_SITE_EVENTS)
        // Now add back in the animate events
        if (cTmp.GetCount() > 0)
        {
            LISTPOSITION pos = cTmp.GetHeadPosition();
            while (pos)
            {
                CSmilAnimateEvent* pAnim = (CSmilAnimateEvent*) cTmp.GetNext(pos);
                if (pAnim)
                {
                    insertEvent(pAnim);
                }
            }
            cTmp.RemoveAll();
        }
#endif
    }
    return rc;
}


#if defined(HELIX_FEATURE_SMIL2_TRANSITIONS)

HX_RESULT CSmilDocumentRenderer::checkSitesHideTime(SMILSiteInfo*        pOtherSiteInfo,
                                                    SMILSiteInfo*        pSiteInfo,
                                                    CSmilTransitionInfo* pTransInfo,
                                                    REF(IHXSite*)       pSiteToTransition,
                                                    BOOL                 bBeginTransition)
{
    HX_RESULT ret = HXR_OK;

    if (pOtherSiteInfo->m_pRegionSite   == pSiteInfo->m_pRegionSite &&
        pOtherSiteInfo->m_pRendererSite != pSiteInfo->m_pRendererSite)
    {
        pSiteToTransition = pSiteInfo->m_pRendererSite;
        if (bBeginTransition && pOtherSiteInfo->m_ulDuration == pSiteInfo->m_ulDelay)
        {
            // find the site with the closest but still lowest 
            // z-order that and the source must start when we end...
            // we need to move back the hide event for this site
            // by our the duration
            INT32 otherZOrder = getSiteZIndex(pOtherSiteInfo->m_pRendererSite);
            INT32 ourZOrder   = getSiteZIndex(pSiteInfo->m_pRendererSite);
            if (otherZOrder < ourZOrder)
            {
                pSiteToTransition = pSiteInfo->m_pRendererSite;
                // we want to transition from/to this site...
                // -- move back its hide event... 
                // (change the hide event to only hide the site too.)
                ret = moveHideEventForSiteBack(pOtherSiteInfo, 
                    pOtherSiteInfo->m_ulDuration,
                    pTransInfo->m_pTrans->m_ulDuration, TRUE);
            }
        }
    }
    else if (bBeginTransition &&
             pOtherSiteInfo->m_ulDuration == pSiteInfo->m_ulDelay &&
             SitesOverlap(pOtherSiteInfo->m_pRegionSite, pSiteInfo->m_pRegionSite))
    {
        ret = moveHideEventForSiteBack(pOtherSiteInfo, 
            pOtherSiteInfo->m_ulDuration,
            pTransInfo->m_pTrans->m_ulDuration, FALSE);
    }
    return ret;
}

CSmilTransitionInfo* CSmilDocumentRenderer::getTransition(const char* pID)
{
    CSmilTransitionInfo* pTransition = NULL;
    if(m_pRegionMap)
    {
      m_pTransitionMap->Lookup(pID, (void*&)pTransition);
    }
    return pTransition;
}

HX_RESULT CSmilDocumentRenderer::makeTransitionValues(CSmilTransitionInfo* pInfo,
                                                      BOOL                 bTransIn,
                                                      REF(IHXValues*)     rpValues)
{
    HX_RESULT retVal = HXR_OK;

    if (pInfo && pInfo->m_pTrans && m_pContext)
    {
        // Get an IHXCommonClassFactory interface
        IHXCommonClassFactory* pFact = NULL;
        retVal = m_pContext->QueryInterface(IID_IHXCommonClassFactory, (void**) &pFact);
        if (SUCCEEDED(retVal))
        {
            // Create an IHXValues
            HX_RELEASE(rpValues);
            retVal = pFact->CreateInstance(CLSID_IHXValues, (void**) &rpValues);
            if (SUCCEEDED(retVal))
            {
                addStringProperty(rpValues, m_pContext, "type",
                                  (const char*) pInfo->m_pTrans->m_Type);
                addStringProperty(rpValues, m_pContext, "subtype",
                                  (const char*) pInfo->m_pTrans->m_SubType);
                addStringProperty(rpValues, m_pContext, "reverse",
                                  (pInfo->m_pTrans->m_eDirection == TransitionDirectionReverse ? "1" : "0"));
                addStringProperty(rpValues, m_pContext, "TranIn",
                                  (bTransIn ? "1" : "0"));
                char        szTmp[16]; /* Flawfinder: ignore */
                const char* pszTmp = (const char*) &szTmp[0];
                sprintf(szTmp, "%lu", pInfo->m_pTrans->m_ulVertRepeat); /* Flawfinder: ignore */
                addStringProperty(rpValues, m_pContext, "VerticalRepeat",   pszTmp);
                sprintf(szTmp, "%lu", pInfo->m_pTrans->m_ulHorzRepeat); /* Flawfinder: ignore */
                addStringProperty(rpValues, m_pContext, "HorizontalRepeat", pszTmp);
                sprintf(szTmp, "%lu", pInfo->m_pTrans->m_ulBorderWidth); /* Flawfinder: ignore */
                addStringProperty(rpValues, m_pContext, "BorderWidth",      pszTmp);
                sprintf(szTmp, "%lu", pInfo->m_pTrans->m_ulBorderColor); /* Flawfinder: ignore */
                addStringProperty(rpValues, m_pContext, "BorderColor",      pszTmp);
                addStringProperty(rpValues, m_pContext, "BlendBorder",
                                  (pInfo->m_pTrans->m_bBlendBorder ? "1" : "0"));
                sprintf(szTmp, "%lu", pInfo->m_pTrans->m_ulFadeColor); /* Flawfinder: ignore */
                addStringProperty(rpValues, m_pContext, "FadeColor",      pszTmp);
            }
        }
        HX_RELEASE(pFact);
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

HX_RESULT CSmilDocumentRenderer::startTransition(UINT32               ulStartTime,
                                                 CSmilTransitionInfo* pTransInfo,
                                                 SMILSiteInfo*        pSiteInfo,
                                                 SMILPlayToAssoc*     pAssoc,
                                                 IHXSite*            pSite,
                                                 UINT32               ulActualTime,
                                                 BOOL                 bBeginTransition)
{
    HX_RESULT retVal = HXR_OK;

    if (pTransInfo && pTransInfo->m_pTrans && pSiteInfo && pAssoc)
    {
        // Get our transition id
        CHXString cOurID;
        UINT32    ulOurDuration = pTransInfo->m_pTrans->m_ulDuration;
        if (pTransInfo->m_pTrans->m_pNode)
        {
            cOurID = pTransInfo->m_pTrans->m_pNode->m_id;
        }
        // Make sure the duration of the site is greater
        // than the duration of the transition
        if (pSiteInfo->m_ulDuration >= pTransInfo->m_pTrans->m_ulDuration)
        {
            // Get the site which will be doing the transition. If we are
            // not a coordinated transition, then we will do the transition
            // always on the renderer site. If we are a coordinated 
            // transition, then we will do the transition on the parent
            // region site.
            //
            // Initially we set the renderer site to be the site
            // we will transition on
            IHXSite* pSiteToTransition = pSiteInfo->m_pRendererSite;
            if (pSiteToTransition)
            {
                // Create a SMILTransitionState struct
                SMILTransitionState* pState = new SMILTransitionState;
                if (pState)
                {
                    // Null out the struct's memory
                    memset((void*) pState, 0, sizeof(SMILTransitionState));
                    // Init the values
                    pState->m_bBegin         = bBeginTransition;
                    pState->m_ulEndPercent   = (UINT32) (pTransInfo->m_pTrans->m_dEndProgress   * 1000.0);
                    pState->m_ulStartPercent = (UINT32) (pTransInfo->m_pTrans->m_dStartProgress * 1000.0);
                    pState->m_ulDuration     = pTransInfo->m_pTrans->m_ulDuration;
                    pState->m_ulEndTime      = ulStartTime + pTransInfo->m_pTrans->m_ulDuration;
                    pState->m_uGroupIndex    = pAssoc->m_uGroupIndex;
                    pState->m_id             = pTransInfo->m_pTrans->m_pNode->m_id;
                    // Now initialize the IHXSiteTransition interface
                    retVal = pSiteToTransition->QueryInterface(IID_IHXSiteTransition,
                                                               (void**) &pState->m_pSiteTransition);
                    if (SUCCEEDED(retVal))
                    {
                        IHXValues* pValues = NULL;
                        retVal = makeTransitionValues(pTransInfo, bBeginTransition, pValues);
                        if (SUCCEEDED(retVal))
                        {
                            MLOG_TRANS(m_pErrorMessages,
                                       "%lu\tIRMASiteTransition::Initialize(0x%08x) m_ulCurrentTime=%lu\n",
                                       HX_GET_BETTERTICKCOUNT(), m_ulCurrentTime);
                            retVal = pState->m_pSiteTransition->Initialize(pValues);
                            if (SUCCEEDED(retVal))
                            {
                                if (!m_pActiveTransitions)
                                {
                                    m_pActiveTransitions = new CHXSimpleList();
                                }
                                if (m_pActiveTransitions)
                                {
                                    // Add to the list
                                    LISTPOSITION pos = m_pActiveTransitions->AddTail((void*) pState);
                                    // Do the transition the first time
                                    doTransition(pos, ulActualTime);
                                }
                                else
                                {
                                    retVal = HXR_OUTOFMEMORY;
                                }
                            }
                        }
                        HX_RELEASE(pValues);
                    }
                }
                else
                {
                    retVal = HXR_OUTOFMEMORY;
                }
            }
        }
    }
    else
    {
        HX_ASSERT(FALSE);
        retVal = HXR_FAIL;
    }

    return retVal;
}

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

BOOL CSmilDocumentRenderer::SitesOverlap(IHXSite* pCanidate, IHXSite* pSite)
{
/*          calculate if our region overlaps with this region.
            check to see if the site has a lower z-order, than our region.
            if these two tests pass and the region is visible:
                search for a hide event on this current region & site,
                pushing back the hide to the end of our duration if one is
            found.
*/
    BOOL bRet = FALSE;

    HXxPoint pntCan;
    HXxPoint pntSite;
    pCanidate->GetPosition(pntCan);
    pSite->GetPosition(pntSite);
    if (pntSite.x == pntCan.x &&
      pntSite.y == pntCan.y)
    {
      HXxSize sizeCan;
      HXxSize sizeSite;
      pCanidate->GetSize(sizeCan);
      pCanidate->GetSize(sizeSite);
      if (sizeCan.cx == sizeSite.cx &&
          sizeCan.cy == sizeSite.cy)
      {
          // check to make sure canidate is behind us in the z-order.
            INT32 canZ  = getSiteZIndex(pCanidate);
            INT32 siteZ = getSiteZIndex(pSite);
            if (canZ < siteZ)
            {
                bRet = TRUE;
            }
      }
    }
    return bRet;
}

HX_RESULT
CSmilDocumentRenderer::moveHideEventForSiteBack(SMILSiteInfo* pInfo, UINT32 ulEndTime,
                                    UINT32 len, 
                                    BOOL bJustRenderer)
{
    LISTPOSITION lPos = NULL;

    if (m_ulEventListPosition)
    {
      lPos = m_ulEventListPosition;
    }
    else
    {
      lPos = m_pEventList->GetHeadPosition();
    }

    while (lPos)
    {
      CSmilLayoutEvent* pThisEvent = (CSmilLayoutEvent*)
          m_pEventList->GetAt(lPos);

      // we can consider all events because we are starting at
      // the m_ulEventListPosition, therefore, we should start
      // after all events have been executed...
      // if Events are set to happen at the same time, Transitions
      // always are at the top of the list, therefore we can catch
      // hide events that are set to happen right after us, even
      // though the current time is past the event time.
      // pThisEvent->m_ulEventTime >= m_ulCurrentTime &&

      if (pThisEvent->getRegionSite() == pInfo->m_pRegionSite &&
          pThisEvent->getRendererSite() == pInfo->m_pRendererSite &&
          pThisEvent->m_ulEventTime == ulEndTime)
      {
          if (pThisEvent->m_type == CSmilLayoutEvent::eHideSite)
          {
            m_pEventList->RemoveAt(lPos);
            pThisEvent->m_ulEventTime += len;
            pThisEvent->m_bOnlyHideSite = bJustRenderer;
            
            LISTPOSITION pos = m_ulEventListPosition;
            insertEvent(pThisEvent);
            m_ulEventListPosition = pos;
            break;
          }
          else if (pThisEvent->m_type == CSmilLayoutEvent::eShowSite)
          {
            break;
          }
      }
      m_pEventList->GetNext(lPos);
    }
    return HXR_OK;
}

HX_RESULT 
CSmilDocumentRenderer::doTransition(LISTPOSITION pos, UINT32 ulTime)
{
    HX_RESULT ret = HXR_OK;
    if (m_pActiveTransitions)
    {
      if (pos)
      {
          SMILTransitionState* pState = (SMILTransitionState*)
            m_pActiveTransitions->GetAt(pos);
          if (pState)
          {
            INT32 percent = (((INT32)ulTime - (INT32)pState->m_ulEndTime 
                + (INT32)pState->m_ulDuration)*
                ((INT32)pState->m_ulEndPercent - (INT32)pState->m_ulStartPercent)/
                (INT32)pState->m_ulDuration) + (INT32)pState->m_ulStartPercent;

            if (percent < (INT32)pState->m_ulStartPercent)
            {
                percent = pState->m_ulStartPercent;
            }
            else if (percent > (INT32)pState->m_ulEndPercent)
            {
                percent = pState->m_ulEndPercent;
            }
                MLOG_TRANS(m_pErrorMessages, "\tIRMATransition::SetPercentage(%ld)\n", percent);
            pState->m_pSiteTransition->SetPercentage(percent);
          }
          else
          {
            ret = HXR_FAIL;
          }
      }
    }
    else
    {
      ret = HXR_FAIL;
    }
    return ret;
}

HX_RESULT
CSmilDocumentRenderer::seekTo(const char* pElementID)
{
    HX_RESULT rc = HXR_OK;

    CSmilElement* pElement = m_pSmilParser->findElement(pElementID);

    if(pElement)
    {
      HX_VECTOR_DELETE(m_pFragment);
      m_pFragment = new_string(pElementID);

      IHXPlayer* pPlayer = m_pParent->getPlayer();
      IHXGroupManager* pMgr = 0;

      if(HXR_OK == pPlayer->QueryInterface(IID_IHXGroupManager, (void**)&pMgr))
      {
          UINT16 uFragmentGroup = m_pSmilParser->getFragmentGroup(m_pFragment);
          if(uFragmentGroup != m_uCurrentGroupIndex)
          {
            m_bSettingFragment = TRUE;
            m_nFragmentTracks = 0;
            pMgr->SetCurrentGroup(uFragmentGroup);
          }
          else
          {
            BOOL bFragFoundAndResolved =TRUE;
            UINT32 ulFragmentOffset = 
                m_pSmilParser->getFragmentOffset(m_pFragment,
                      /* This is passed by reference: */
                      bFragFoundAndResolved);
            //If getFragmentOffset() failed to find the fragment or
            // found it but it did not yet have a resolved begin time,
            // we used to seek to 0 (the returned value), but now we
            // just don't do a seek:
            // (Note: this problem was found while fixing PR 22655.)
            if(bFragFoundAndResolved)
            {
                pPlayer->Seek(ulFragmentOffset);
                pPlayer->Begin();
            }
          }
          pMgr->Release();
      }
    }
    else
    {
      rc = HXR_FAIL;
    }
    return rc;
}



void
CSmilDocumentRenderer::resizeRegion(const char* pRegionName,
                            HXxSize* pNewSize)
{
    CSmilBasicRegion* pRegion = getRegionByID(pRegionName);
    if(pRegion)
    {
      pRegion->m_rect.right = pRegion->m_rect.left + pNewSize->cx;
      pRegion->m_rect.bottom = pRegion->m_rect.top + pNewSize->cy;
    }
}

void
CSmilDocumentRenderer::repositionRegion(const char* pRegionName,
                              HXxPoint* pNewPosition)
{
    CSmilBasicRegion* pRegion = getRegionByID(pRegionName);
    if(pRegion)
    {
      HXxSize size;
      size.cx = HXxRECT_WIDTH(pRegion->m_rect);
      size.cy = HXxRECT_HEIGHT(pRegion->m_rect);

      pRegion->m_rect.left = pNewPosition->x;
      pRegion->m_rect.top = pNewPosition->y;
      pRegion->m_rect.right = pRegion->m_rect.left + size.cx;
      pRegion->m_rect.bottom = pRegion->m_rect.top + size.cy;
    }
}

void CSmilDocumentRenderer::setMediaNativeSize(const char* pszID, HXxSize cSize)
{
    if (pszID &&
        cSize.cx > 0 && cSize.cy > 0)
    {
        if (!m_bLayoutElementPresent ||
            (m_bLayoutElementPresent && m_bEmptyLayout))
        {
            // Get the source for this renderer. Make sure
            // this source is not trying to manipulate the 
            // subregion width and height
            CSmilSource* pSource = getSource(pszID);
            if (pSource &&
                pSource->m_Rect.m_eWidthType  == CSS2TypeAuto &&
                pSource->m_Rect.m_eHeightType == CSS2TypeAuto)
            {
                // Set the width and height of the subregion
                // to be the media width and height
                pSource->m_Rect.m_eWidthType  = CSS2TypeLength;
                pSource->m_Rect.m_dWidth      = (double) cSize.cx;
                pSource->m_Rect.m_eHeightType = CSS2TypeLength;
                pSource->m_Rect.m_dHeight     = (double) cSize.cy;
                // If we have not set the "top" attribute, then
                // we will explicitly set it here.
                if (pSource->m_Rect.m_eTopType == CSS2TypeAuto)
                {
                    // Get the group index for this media
                    UINT16           usGroup = 0;
                    SMILPlayToAssoc* pThisPT = getPlayToAssocByMedia(pszID);
                    if (pThisPT)
                    {
                        usGroup = pThisPT->m_uGroupIndex;
                    }
                    // Find the maximum x and y for all media in this
                    // group playing to the default region
                    double dMaxX = 0.0;
                    double dMaxY = 0.0;
                    if (m_pPlayToAssocList)
                    {
                        LISTPOSITION pos = m_pPlayToAssocList->GetHeadPosition();
                        while (pos)
                        {
                            SMILPlayToAssoc* pPT = (SMILPlayToAssoc*) m_pPlayToAssocList->GetNext(pos);
                            if (pPT &&
                                pPT->m_id != pszID &&
                                pPT->m_uGroupIndex == usGroup &&
                                pPT->m_playTo == "jgje4u5kd845prhd94")
                            {
                                // This media is playing to the default region 
                                // and is not the current media. Get the source for
                                // this media
                                CSmilSource* pSrc = getSource(pPT->m_id);
                                if (pSrc)
                                {
                                    if (pSrc->m_Rect.m_eTopType    == CSS2TypeLength &&
                                        pSrc->m_Rect.m_eHeightType == CSS2TypeLength)
                                    {
                                        double dY = pSrc->m_Rect.m_dTop +
                                                    pSrc->m_Rect.m_dHeight;
                                        if (dY > dMaxY)
                                        {
                                            dMaxY = dY;
                                        }
                                    }
                                    if (pSrc->m_Rect.m_eLeftType  == CSS2TypeLength &&
                                        pSrc->m_Rect.m_eWidthType == CSS2TypeLength)
                                    {
                                        double dX = pSrc->m_Rect.m_dLeft +
                                                    pSrc->m_Rect.m_dWidth;
                                        if (dX > dMaxX)
                                        {
                                            dMaxX = dX;
                                        }
                                    }
                                }
                            }
                        }
                    }
                    // Set the subregion left of this media
                    pSource->m_Rect.m_eLeftType = CSS2TypeLength;
                    pSource->m_Rect.m_dLeft     = 0.0;
                    // Set the subregion top of this media. If we have
                    // an empty layout, then we put everything at the top
                    // left. If we have NO layout, then we stack the
                    // media up.
                    pSource->m_Rect.m_eTopType = CSS2TypeLength;
                    pSource->m_Rect.m_dTop     = (m_bEmptyLayout ? 0.0 : dMaxY);
                    // We need to update the site watcher's copy
                    // of the subregion rect. So first we have to get
                    // the site watcher
                    CSmilSiteWatcher* pWatcher = getRendererSiteWatcher(pszID);
                    if (pWatcher)
                    {
                        // Now update the site watcher's subregion rect
                        pWatcher->SetSubRegionRect(pSource->m_Rect);
                    }
                    // Update the x max to include this media
                    double dX = pSource->m_Rect.m_dLeft + pSource->m_Rect.m_dWidth;
                    if (dX > dMaxX)
                    {
                        dMaxX = dX;
                    }
                    // Update the y max to include this media
                    double dY = pSource->m_Rect.m_dTop + pSource->m_Rect.m_dHeight;
                    if (dY > dMaxY)
                    {
                        dMaxY = dY;
                    }
                    // Now may need to update the width and height of the root-layout
                    INT32   lMaxX     = (INT32) (dMaxX + 0.5);
                    INT32   lMaxY     = (INT32) (dMaxY + 0.5);
                    HXxSize cRootSize = {lMaxX, lMaxY};
                    if (m_pRootLayout &&
                        m_pRootLayout->m_pSite &&
                        (lMaxX != HXxRECT_WIDTH(m_pRootLayout->m_Rect) ||
                         lMaxY != HXxRECT_HEIGHT(m_pRootLayout->m_Rect)))
                    {
                        m_bDoNotZoom = TRUE;
                        m_pRootLayout->m_pSite->SetSize(cRootSize);
                        m_bDoNotZoom = FALSE;
                        // In the root-layout SetSize() above, we are 
                        // adjusting the size of the root-layout to account
                        // for the additional media which has no layout specified.
                        // We make sure above to disable zooming, since this 
                        // SetSize() doesn't come from resizing the player but
                        // rather from our direct manipulation. Here, we also
                        // need to reset the root-layout's "original size" so
                        // that when we do actually zoom (by resizing the
                        // player) that we know the correct original size
                        m_pRootLayout->m_OriginalSize = cRootSize;
                    }
                }
            }
        }
    }
}

void CSmilDocumentRenderer::computeRegPoint(HXxRect       cRegionRect,
                                            RegPoint      cRegPointInfo,
                                            REF(HXxPoint) rcRegPoint)
{
    // Compute regPoint.x
    if (cRegPointInfo.m_eLeftType  == CSS2TypeAuto &&
        cRegPointInfo.m_eRightType == CSS2TypeAuto)
    {
        // Both are auto - we'll default to the regionRect left
        rcRegPoint.x = cRegionRect.left;
    }
    else if (cRegPointInfo.m_eLeftType  != CSS2TypeAuto)
    {
        // Take left
        INT32 lLeft = 0;
        if (cRegPointInfo.m_eLeftType == CSS2TypePercentage)
        {
            lLeft = (INT32) (cRegPointInfo.m_dLeft * HXxRECT_WIDTH(cRegionRect) / 100.0 + 0.5);
        }
        else
        {
            lLeft = (INT32) (cRegPointInfo.m_dLeft + 0.5);
        }
        rcRegPoint.x = cRegionRect.left + lLeft;
    }
    else if (cRegPointInfo.m_eLeftType  == CSS2TypeAuto &&
             cRegPointInfo.m_eRightType != CSS2TypeAuto)
    {
        // Take right
        INT32 lRight = 0;
        if (cRegPointInfo.m_eRightType == CSS2TypePercentage)
        {
            lRight = (INT32) (cRegPointInfo.m_dRight * HXxRECT_WIDTH(cRegionRect) / 100.0 + 0.5);
        }
        else
        {
            lRight = (INT32) (cRegPointInfo.m_dRight + 0.5);
        }
        rcRegPoint.x  = cRegionRect.right - lRight;
    }
    // Compute regPoint.y
    if (cRegPointInfo.m_eTopType    == CSS2TypeAuto &&
        cRegPointInfo.m_eBottomType == CSS2TypeAuto)
    {
        // Both are auto - we'll default to the regionRect top
        rcRegPoint.y = cRegionRect.top;
    }
    else if (cRegPointInfo.m_eTopType  != CSS2TypeAuto)
    {
        // Take top
        INT32 lTop = 0;
        if (cRegPointInfo.m_eTopType == CSS2TypePercentage)
        {
            lTop  = (INT32) (cRegPointInfo.m_dTop * HXxRECT_HEIGHT(cRegionRect) / 100.0 + 0.5);
        }
        else
        {
            lTop  = (INT32) (cRegPointInfo.m_dTop + 0.5);
        }
        rcRegPoint.y = cRegionRect.top + lTop;
    }
    else if (cRegPointInfo.m_eTopType    == CSS2TypeAuto &&
             cRegPointInfo.m_eBottomType != CSS2TypeAuto)
    {
        // Take bottom
        INT32 lBottom = 0;
        if (cRegPointInfo.m_eBottomType == CSS2TypePercentage)
        {
            lBottom = (INT32) (cRegPointInfo.m_dBottom * HXxRECT_HEIGHT(cRegionRect) / 100.0 + 0.5);
        }
        else
        {
            lBottom = (INT32) (cRegPointInfo.m_dBottom + 0.5);
        }
        rcRegPoint.y = cRegionRect.bottom - lBottom;
    }
}

HX_RESULT CSmilDocumentRenderer::computeMediaLayout(HXxRect      cRegionRect,
                                                    Fit          eFit,
                                                    HXxPoint     cRegPoint,
                                                    RegAlign     eRegAlign,
                                                    HXxSize      cMediaSize,
                                                    REF(HXxRect) rcMediaRect,
                                                    BOOL         bMediaIsBrush)
{
    HX_RESULT retVal = HXR_OK;

    if (cRegionRect.right  > cRegionRect.left &&
        cRegionRect.bottom > cRegionRect.top  &&
        cMediaSize.cx > 0 && cMediaSize.cy > 0)
    {
        // If the media is a brush, then we always have 
        // fit="fill" behavior
        if (bMediaIsBrush)
        {
            eFit = FitFill;
        }
        // Switch based on fit
        switch (eFit)
        {
            case FitFill:
                {
                    switch (eRegAlign)
                    {
                        case RegAlignTopLeft:
                            {
                                rcMediaRect.left   = cRegPoint.x;
                                rcMediaRect.top    = cRegPoint.y;
                                rcMediaRect.right  = cRegionRect.right;
                                rcMediaRect.bottom = cRegionRect.bottom;
                            }
                            break;
                        case RegAlignTopMid:
                            {
                                rcMediaRect.top    = cRegPoint.y;
                                rcMediaRect.bottom = cRegionRect.bottom;
                                computeFillHiddenScaling(eFit, cRegPoint.x,      cMediaSize.cx,
                                                         cRegionRect.left, cRegionRect.right,
                                                         rcMediaRect.left, rcMediaRect.right);
                            }
                            break;
                        case RegAlignTopRight:
                            {
                                rcMediaRect.left   = cRegionRect.left;
                                rcMediaRect.top    = cRegPoint.y;
                                rcMediaRect.right  = cRegPoint.x;
                                rcMediaRect.bottom = cRegionRect.bottom;
                            }
                            break;
                        case RegAlignMidLeft:
                            {
                                rcMediaRect.left   = cRegPoint.x;
                                rcMediaRect.right  = cRegionRect.right;
                                computeFillHiddenScaling(eFit, cRegPoint.y, cMediaSize.cy,
                                                         cRegionRect.top, cRegionRect.bottom,
                                                         rcMediaRect.top, rcMediaRect.bottom);
                            }
                            break;
                        case RegAlignCenter:
                            {
                                computeFillHiddenScaling(eFit, cRegPoint.x, cMediaSize.cx,
                                                         cRegionRect.left, cRegionRect.right,
                                                         rcMediaRect.left, rcMediaRect.right);
                                computeFillHiddenScaling(eFit, cRegPoint.y, cMediaSize.cy,
                                                         cRegionRect.top, cRegionRect.bottom,
                                                         rcMediaRect.top, rcMediaRect.bottom);
                            }
                            break;
                        case RegAlignMidRight:
                            {
                                rcMediaRect.left  = cRegionRect.left;
                                rcMediaRect.right = cRegPoint.x;
                                computeFillHiddenScaling(eFit, cRegPoint.y, cMediaSize.cy,
                                                         cRegionRect.top, cRegionRect.bottom,
                                                         rcMediaRect.top, rcMediaRect.bottom);
                            }
                            break;
                        case RegAlignBottomLeft:
                            {
                                rcMediaRect.left   = cRegPoint.x;
                                rcMediaRect.top    = cRegionRect.top;
                                rcMediaRect.right  = cRegionRect.right;
                                rcMediaRect.bottom = cRegPoint.y;
                            }
                            break;
                        case RegAlignBottomMid:
                            {
                                rcMediaRect.top    = cRegionRect.top;
                                rcMediaRect.bottom = cRegPoint.y;
                                computeFillHiddenScaling(eFit, cRegPoint.x,  cMediaSize.cx,
                                                         cRegionRect.left, cRegionRect.right,
                                                         rcMediaRect.left, rcMediaRect.right);
                            }
                            break;
                        case RegAlignBottomRight:
                            {
                                rcMediaRect.left   = cRegionRect.left;
                                rcMediaRect.right  = cRegPoint.x;
                                rcMediaRect.top    = cRegionRect.top;
                                rcMediaRect.bottom = cRegPoint.y;
                            }
                            break;
                    }
                }
                break;

            case FitScroll:
            case FitHidden:
                {
                    switch (eRegAlign)
                    {
                        case RegAlignTopLeft:
                            {
                                rcMediaRect.left   = cRegPoint.x;
                                rcMediaRect.top    = cRegPoint.y;
                                rcMediaRect.right  = cRegPoint.x + cMediaSize.cx;
                                rcMediaRect.bottom = cRegPoint.y + cMediaSize.cy;
                            }
                            break;
                        case RegAlignTopMid:
                            {
                                rcMediaRect.top    = cRegPoint.y;
                                rcMediaRect.bottom = cRegPoint.y + cMediaSize.cy;
                                computeFillHiddenScaling(eFit, cRegPoint.x, cMediaSize.cx,
                                                         cRegionRect.left, cRegionRect.right,
                                                         rcMediaRect.left, rcMediaRect.right);
                            }
                            break;
                        case RegAlignTopRight:
                            {
                                rcMediaRect.left   = cRegPoint.x - cMediaSize.cx;
                                rcMediaRect.top    = cRegPoint.y;
                                rcMediaRect.right  = cRegPoint.x;
                                rcMediaRect.bottom = cRegPoint.y + cMediaSize.cy;
                            }
                            break;
                        case RegAlignMidLeft:
                            {
                                rcMediaRect.left  = cRegPoint.x;
                                rcMediaRect.right = cRegPoint.x + cMediaSize.cx;
                                computeFillHiddenScaling(eFit, cRegPoint.y, cMediaSize.cy,
                                                         cRegionRect.top, cRegionRect.bottom,
                                                         rcMediaRect.top, rcMediaRect.bottom);
                            }
                            break;
                        case RegAlignCenter:
                            {
                                computeFillHiddenScaling(eFit, cRegPoint.x, cMediaSize.cx,
                                                         cRegionRect.left, cRegionRect.right,
                                                         rcMediaRect.left, rcMediaRect.right);
                                computeFillHiddenScaling(eFit, cRegPoint.y, cMediaSize.cy,
                                                         cRegionRect.top, cRegionRect.bottom,
                                                         rcMediaRect.top, rcMediaRect.bottom);
                            }
                            break;
                        case RegAlignMidRight:
                            {
                                rcMediaRect.left  = cRegPoint.x - cMediaSize.cx;
                                rcMediaRect.right = cRegPoint.x;
                                computeFillHiddenScaling(eFit, cRegPoint.y, cMediaSize.cy,
                                                         cRegionRect.top, cRegionRect.bottom,
                                                         rcMediaRect.top, rcMediaRect.bottom);
                            }
                            break;
                        case RegAlignBottomLeft:
                            {
                                rcMediaRect.left   = cRegPoint.x;
                                rcMediaRect.top    = cRegPoint.y - cMediaSize.cy;
                                rcMediaRect.right  = cRegPoint.x + cMediaSize.cx;
                                rcMediaRect.bottom = cRegPoint.y;
                            }
                            break;
                        case RegAlignBottomMid:
                            {
                                rcMediaRect.top    = cRegPoint.y - cMediaSize.cy;
                                rcMediaRect.bottom = cRegPoint.y;
                                computeFillHiddenScaling(eFit, cRegPoint.x, cMediaSize.cx,
                                                         cRegionRect.left, cRegionRect.right,
                                                         rcMediaRect.left, rcMediaRect.right);
                            }
                            break;
                        case RegAlignBottomRight:
                            {
                                rcMediaRect.left   = cRegPoint.x - cMediaSize.cx;
                                rcMediaRect.top    = cRegPoint.y - cMediaSize.cy;
                                rcMediaRect.right  = cRegPoint.x;
                                rcMediaRect.bottom = cRegPoint.y;
                            }
                            break;
                    }
                }
                break;

            case FitMeet:
            case FitSlice:
                {
                    INT32 d       = 0; // dummy variable
                    BOOL  bIsMeet = (eFit == FitMeet ? TRUE : FALSE);
                    switch (eRegAlign)
                    {
                        case RegAlignTopLeft:
                            {
                                // left and top are fixed
                                rcMediaRect.left = cRegPoint.x;
                                rcMediaRect.top  = cRegPoint.y;
                                // compute the rest of the media rect
                                computeMeetSliceScaling(bIsMeet, 2,
                                                        cRegPoint.x,
                                                        cRegPoint.x + cMediaSize.cx,
                                                        cRegionRect.right,
                                                        cRegPoint.y,
                                                        cRegPoint.y + cMediaSize.cy,
                                                        cRegionRect.bottom,
                                                        0, 0, 0, 0, 0, 0,
                                                        rcMediaRect.right,
                                                        rcMediaRect.bottom,
                                                        d, d);
                            }
                            break;
                        case RegAlignTopMid:
                            {
                                // top is fixed
                                rcMediaRect.top = cRegPoint.y;
                                // Compute the left and right of the original media
                                INT32 lMediaLeft  = 0;
                                INT32 lMediaRight = 0;
                                computeHalfBounds(cRegPoint.x, cMediaSize.cx,
                                                  lMediaLeft,  lMediaRight);
                                // Compute the rest of the media rect
                                computeMeetSliceScaling(bIsMeet, 3,
                                                        cRegPoint.x,
                                                        lMediaLeft,
                                                        cRegionRect.left,
                                                        cRegPoint.x,
                                                        lMediaRight,
                                                        cRegionRect.right,
                                                        cRegPoint.y,
                                                        cRegPoint.y + cMediaSize.cy,
                                                        cRegionRect.bottom,
                                                        0, 0, 0,
                                                        rcMediaRect.left,
                                                        rcMediaRect.right,
                                                        rcMediaRect.bottom,
                                                        d);
                            }
                            break;
                        case RegAlignTopRight:
                            {
                                // right and top are fixed
                                rcMediaRect.top   = cRegPoint.y;
                                rcMediaRect.right = cRegPoint.x;
                                // compute the rest of the media rect
                                computeMeetSliceScaling(bIsMeet, 2,
                                                        cRegPoint.x,
                                                        cRegPoint.x - cMediaSize.cx,
                                                        cRegionRect.left,
                                                        cRegPoint.y,
                                                        cRegPoint.y + cMediaSize.cy,
                                                        cRegionRect.bottom,
                                                        0, 0, 0, 0, 0, 0,
                                                        rcMediaRect.left,
                                                        rcMediaRect.bottom,
                                                        d, d);
                            }
                            break;
                        case RegAlignMidLeft:
                            {
                                // left is fixed
                                rcMediaRect.left = cRegPoint.x;
                                // Compute the top and bottom of the original media
                                INT32 lMediaTop    = 0;
                                INT32 lMediaBottom = 0;
                                computeHalfBounds(cRegPoint.y, cMediaSize.cy,
                                                  lMediaTop,  lMediaBottom);
                                // compute the rest of the media rect
                                computeMeetSliceScaling(bIsMeet, 3,
                                                        cRegPoint.y,
                                                        lMediaTop,
                                                        cRegionRect.top,
                                                        cRegPoint.x,
                                                        cRegPoint.x + cMediaSize.cx,
                                                        cRegionRect.right,
                                                        cRegPoint.y,
                                                        lMediaBottom,
                                                        cRegionRect.bottom,
                                                        0, 0, 0,
                                                        rcMediaRect.top,
                                                        rcMediaRect.right,
                                                        rcMediaRect.bottom,
                                                        d);
                            }
                            break;
                        case RegAlignCenter:
                            {
                                // Compute the left and right of the original media
                                INT32 lMediaLeft  = 0;
                                INT32 lMediaRight = 0;
                                computeHalfBounds(cRegPoint.x, cMediaSize.cx,
                                                  lMediaLeft,  lMediaRight);
                                // Compute the top and bottom of the original media
                                INT32 lMediaTop    = 0;
                                INT32 lMediaBottom = 0;
                                computeHalfBounds(cRegPoint.y, cMediaSize.cy,
                                                  lMediaTop,  lMediaBottom);
                                // Compute the media rect
                                computeMeetSliceScaling(bIsMeet, 4,
                                                        cRegPoint.x,
                                                        lMediaLeft,
                                                        cRegionRect.left,
                                                        cRegPoint.y,
                                                        lMediaTop,
                                                        cRegionRect.top,
                                                        cRegPoint.x,
                                                        lMediaRight,
                                                        cRegionRect.right,
                                                        cRegPoint.y,
                                                        lMediaBottom,
                                                        cRegionRect.bottom,
                                                        rcMediaRect.left,
                                                        rcMediaRect.top,
                                                        rcMediaRect.right,
                                                        rcMediaRect.bottom);
                            }
                            break;
                        case RegAlignMidRight:
                            {
                                // right is fixed
                                rcMediaRect.right = cRegPoint.x;
                                // Compute the top and bottom of the original media
                                INT32 lMediaTop    = 0;
                                INT32 lMediaBottom = 0;
                                computeHalfBounds(cRegPoint.y, cMediaSize.cy,
                                                  lMediaTop,  lMediaBottom);
                                // Compute the rest of the media rect
                                computeMeetSliceScaling(bIsMeet, 3,
                                                        cRegPoint.x,
                                                        cRegPoint.x - cMediaSize.cx,
                                                        cRegionRect.left,
                                                        cRegPoint.y,
                                                        lMediaTop,
                                                        cRegionRect.top,
                                                        cRegPoint.y,
                                                        lMediaBottom,
                                                        cRegionRect.bottom,
                                                        0, 0, 0,
                                                        rcMediaRect.left,
                                                        rcMediaRect.top,
                                                        rcMediaRect.bottom,
                                                        d);
                            }
                            break;
                        case RegAlignBottomLeft:
                            {
                                // left and bottom are fixed
                                rcMediaRect.left   = cRegPoint.x;
                                rcMediaRect.bottom = cRegPoint.y;
                                // Compute the rest of the media rect
                                computeMeetSliceScaling(bIsMeet, 2,
                                                        cRegPoint.y,
                                                        cRegPoint.y - cMediaSize.cy,
                                                        cRegionRect.top,
                                                        cRegPoint.x,
                                                        cRegPoint.x + cMediaSize.cx,
                                                        cRegionRect.right,
                                                        0, 0, 0, 0, 0, 0,
                                                        rcMediaRect.top,
                                                        rcMediaRect.right,
                                                        d, d);
                            }
                            break;
                        case RegAlignBottomMid:
                            {
                                // bottom is fixed
                                rcMediaRect.bottom = cRegPoint.y;
                                // Compute the left and right of the original media
                                INT32 lMediaLeft  = 0;
                                INT32 lMediaRight = 0;
                                computeHalfBounds(cRegPoint.x, cMediaSize.cx,
                                                  lMediaLeft,  lMediaRight);
                                // Compute the rest of the media rect
                                computeMeetSliceScaling(bIsMeet, 3,
                                                        cRegPoint.x,
                                                        lMediaLeft,
                                                        cRegionRect.left,
                                                        cRegPoint.y,
                                                        cRegPoint.y - cMediaSize.cy,
                                                        cRegionRect.top,
                                                        cRegPoint.x,
                                                        lMediaRight,
                                                        cRegionRect.right,
                                                        0, 0, 0,
                                                        rcMediaRect.left,
                                                        rcMediaRect.top,
                                                        rcMediaRect.right,
                                                        d);
                            }
                            break;
                        case RegAlignBottomRight:
                            {
                                // right and bottom are fixed
                                rcMediaRect.right  = cRegPoint.x;
                                rcMediaRect.bottom = cRegPoint.y;
                                // Compute the rest of the media rect
                                computeMeetSliceScaling(bIsMeet, 2,
                                                        cRegPoint.x,
                                                        cRegPoint.x - cMediaSize.cx,
                                                        cRegionRect.left,
                                                        cRegPoint.y,
                                                        cRegPoint.y - cMediaSize.cy,
                                                        cRegionRect.top,
                                                        0, 0, 0, 0, 0, 0,
                                                        rcMediaRect.left,
                                                        rcMediaRect.top,
                                                        d, d);
                            }
                            break;
                    }
                }
                break;
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

void CSmilDocumentRenderer::computeHalfBounds(INT32 lMid, INT32 lSpan,
                                              REF(INT32) rlMin, REF(INT32) rlMax)
{
    INT32 lHalfSpan = lSpan >> 1;
    rlMin           = lMid - lHalfSpan;
    rlMax           = lMid + (lSpan & 1 ? lHalfSpan + 1 : lHalfSpan);
}

void CSmilDocumentRenderer::computeFillHiddenScaling(Fit eFit, INT32 lRegPt, INT32 lMediaDim,
                                                     INT32 lRegionLo, INT32 lRegionHi,
                                                     REF(INT32) rlMediaLo,
                                                     REF(INT32) rlMediaHi)
{
    // Get the min and max bounds of the media
    INT32 lMediaLo = 0;
    INT32 lMediaHi = 0;
    computeHalfBounds(lRegPt, lMediaDim, lMediaLo, lMediaHi);
    // Compute the rect
    if (eFit == FitFill)
    {
        double dLoDiffOld  = lMediaLo  - lRegPt;
        double dLoDiffNew  = lRegionLo - lRegPt;
        double dHiDiffOld  = lMediaHi  - lRegPt;
        double dHiDiffNew  = lRegionHi - lRegPt;
        double dLoScale    = (dLoDiffOld != 0.0 ? dLoDiffNew / dLoDiffOld : 1.0);
        double dHiScale    = (dHiDiffOld != 0.0 ? dHiDiffNew / dHiDiffOld : 1.0);
        dLoScale           = (dLoScale > 0.0 ? dLoScale : -dLoScale);
        dHiScale           = (dHiScale > 0.0 ? dHiScale : -dHiScale);
        if (dLoScale != 0.0 && dLoScale < dHiScale)
        {
            rlMediaLo           = lRegionLo;
            INT32 lScaledHiDiff = (INT32) (dHiDiffOld * dLoScale + 0.5);
            rlMediaHi           = lRegPt + lScaledHiDiff;
        }
        else
        {
            INT32 lScaledLoDiff = (INT32) (dLoDiffOld * dHiScale + 0.5);
            rlMediaLo           = lRegPt + lScaledLoDiff;
            rlMediaHi           = lRegionHi;
        }
    }
    else if (eFit == FitHidden ||
             eFit == FitScroll)
    {
        rlMediaLo = lMediaLo;
        rlMediaHi = lMediaHi;
    }
}

void CSmilDocumentRenderer::computeMeetSliceScaling(BOOL   bIsMeet, UINT32 ulNumDim,
                                                    INT32 lA1, INT32 lA2, INT32 lA3,
                                                    INT32 lB1, INT32 lB2, INT32 lB3,
                                                    INT32 lC1, INT32 lC2, INT32 lC3,
                                                    INT32 lD1, INT32 lD2, INT32 lD3,
                                                    REF(INT32) rlA, REF(INT32) rlB,
                                                    REF(INT32) rlC, REF(INT32) rlD)
{
    // Make sure there's not more than 4 dimensions
    if (ulNumDim > 4) ulNumDim = 4;
    // Compute the scales
    INT32  lBase[4]    = {lA1, lB1, lC1, lD1};
    INT32  lOld[4]     = {lA2, lB2, lC2, lD2};
    INT32  lNew[4]     = {lA3, lB3, lC3, lD3};
    double dOldDiff[4] = {0.0, 0.0, 0.0, 0.0};
    double dScale[4]   = {0.0, 0.0, 0.0, 0.0};
    UINT32 i           = 0;
    double dMinMax     = (bIsMeet ? 10000000.0 : 0.0);
    for (i = 0; i < ulNumDim; i++)
    {
        // Compute the old diff
        dOldDiff[i] = lOld[i] - lBase[i];
        // Compute the new diff
        double dNewDiff = lNew[i] - lBase[i];
        // Compute the scale
        dScale[i] = (dOldDiff[i] != 0 ? dNewDiff / dOldDiff[i] : 1.0);
        // Make sure the scale is not negative
        dScale[i] = (dScale[i] > 0.0 ? dScale[i] : -dScale[i]);
        // Find the min or max
        if ((bIsMeet  && dScale[i] < dMinMax) ||
            (!bIsMeet && dScale[i] > dMinMax))
        {
            dMinMax = dScale[i];
        }
    }
    // So now we have either the minimum or maximum scale,
    // so now we need to adjust all of the scales to this scale
    INT32 lOut[4] = {0, 0, 0, 0};
    for (i = 0; i < ulNumDim; i++)
    {
        if (dScale[i] == dMinMax)
        {
            lOut[i] = lNew[i];
        }
        else
        {
            INT32 lNewDiff = (INT32) (dOldDiff[i] * dMinMax + 0.5);
            lOut[i]        = lBase[i] + lNewDiff;
        }
    }
    // Assign to the out parameters
    rlA = lOut[0];
    rlB = lOut[1];
    rlC = lOut[2];
    rlD = lOut[3];
}

HX_RESULT CSmilDocumentRenderer::processMediaRegionOverrides(const char* pszRegionID,
                                                             const char* pszMediaID,
                                                             BOOL        bMediaStart,
                                                             UINT32      ulTime)
{
    HX_RESULT retVal = HXR_OK;

    if (pszRegionID && pszMediaID)
    {
        // Get the source
        CSmilSource* pSource = getSource(pszMediaID);
        if (pSource)
        {
            // Get the region
            CSmilBasicRegion* pRegion = getRegionByID(pszRegionID);
            if (pRegion)
            {
                // Are there any media overrides?
                if (pSource->m_bBackgroundColorSpecified)
                {
                    // Get the site user
                    UINT32         ulOldColor = 0;
                    UINT32         ulNewColor = 0;
                    CSmilSiteUser* pSiteUser  = (CSmilSiteUser*) pRegion->m_pSiteUser;
                    if (pSiteUser)
                    {
                        // Are we starting or ending the media?
                        if (bMediaStart)
                        {
                            // We are ending the media - therefore, we need to
                            // set the site user's background color to the 
                            // media's background color
                            ulOldColor = pRegion->m_ulBackgroundColor;
                            ulNewColor = pSource->m_ulBackgroundColor;
                        }
                        else
                        {
                            // We are ending the media - therefore, we need to
                            // set the site user's background color to the 
                            // region's background color
                            ulOldColor = pSource->m_ulBackgroundColor;
                            ulNewColor = pRegion->m_ulBackgroundColor;
                        }
                        // Now set the background color into the site user
                        UINT32 ulCurColor = pSiteUser->GetBackgroundColor();
                        if (ulCurColor != ulNewColor)
                        {
                            pSiteUser->SetBackgroundColor(ulNewColor);
                            pSiteUser->Redraw();
                        }
                        // If the media is ending and we are resetting
                        // the background Color back, then we should
                        // note the time, so that an animation ending
                        // will not reset it right back to what it is now.
                        if (!bMediaStart)
                        {
                            pSiteUser->SetLastMediaEndOverrideTime(m_ulCurrentTime);
                        }
                    }
#if 0
                    // XXXMEH - TEST - this is a further optimization, but
                    // seems to be blocked by a site bug. So ifdef
                    // it out for now.
                    // Check if we need to adjust the "SiteNeverBlts"
                    // property of the site. If we are switching to or
                    // from totally transparent, then we may need to
                    // adjust this property
                    if (isTransparent(ulNewColor) &&
                        !isTransparent(ulOldColor))
                    {
                        setSiteProperty(pRegion->m_pSite, "SiteNeverBlts", "1");
                    }
                    else if (!isTransparent(ulNewColor) &&
                             isTransparent(ulOldColor))
                    {
                        setSiteProperty(pRegion->m_pSite, "SiteNeverBlts", "0");
                    }
#endif
                }
#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)
                // Now we need to check if this media is playing
                // to a viewport.
                if (!pRegion->m_bUnderRootLayout)
                {
                    // This media is playing to a viewport,
                    // so get the viewport.
                    CSmilBasicViewport* pPort = getViewportByDescendant(pRegion);
                    if (pPort)
                    {
                        // Check whether we need to increment or
                        // decrement the active media count.
                        if (bMediaStart)
                        {
                            // Media is starting, so increment the
                            // active media count
                            pPort->m_ulNumActiveMedia++;
                            // If this is the first media to start AND
                            // the viewport has an "open" property of
                            // whenActive, then do a show on the viewport
                            if (pPort->m_ulNumActiveMedia == 1 &&
                                pPort->m_pPort                 &&
                                pPort->m_pPort->m_eOpen == ViewportOpenWhenActive)
                            {
                                showHideViewport(pPort, TRUE);
                            }
                        }
                        else
                        {
                            // Media is ending or being hidden, so
                            // decrement the active media count, but 
                            // make sure we don't wrap around
                            if (pPort->m_ulNumActiveMedia)
                            {
                                pPort->m_ulNumActiveMedia--;
                            }
                            // If this is the last media to end AND
                            // the viewport has a "close" property of
                            // whenNotActive, then do a hide on the viewport.
                            if (pPort->m_ulNumActiveMedia == 0 &&
                                pPort->m_pPort                 &&
                                pPort->m_pPort->m_eClose == ViewportCloseWhenNotActive)
                            {
                                showHideViewport(pPort, FALSE);
                            }
                        }
                    }
                }
#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */
                // Now we need to check whether the starting or ending
                // if this media will cause a parent region to display.
                // For instance, if region B is a child of region A,
                // and region A has showBackground="whenActive", then
                // region A may NOT be visible when media in region B
                // starts. However, "whenActive" means that when media
                // is active in the region OR ANY CHILD REGIONS. Therefore,
                // region A should be made visible at the same time.
                // Conversely, if a media is ending, it may cause a parent
                // to be hidden.
                //
                // Loop up through the region's ancestors. We
                // can know if the CSmilBasicBox is a CSmilBasicRegion
                // by the fact that top-level boxes will have 
                // no parents, so if CSmilBasicBox::m_pParent == NULL,
                // then it's a top-level box and not a region.
                CSmilBasicBox* pBox = pRegion->m_pParent;
                while (pBox && pBox->m_pParent)
                {
                    // We know this is a region, so cast it
                    CSmilBasicRegion* pParReg = (CSmilBasicRegion*) pBox;
                    // Are we starting or ending this media?
                    if (bMediaStart)
                    {
                        // We are starting this media, so we need
                        // to check if this region has showBackground="whenActive".
                        // If it does, then we need to show this site.
                        if (pParReg->m_eShowBackground == ShowBackgroundWhenActive)
                        {
                            showSite(pParReg->m_pSite, TRUE);
                        }
                    }
                    else
                    {
                        // We are ending this media, so we need to
                        // check two things: a) does this region have 
                        // showBackground="whenActive"; and b) is anything
                        // currently playing in this region at this time?
                        // If (a) is yes and (b) is no, then we need to
                        // hide this site.
                        if (pParReg->m_eShowBackground == ShowBackgroundWhenActive &&
                            !isRegionActive(pParReg->m_region, (UINT16) m_uCurrentGroupIndex, ulTime))
                        {
                            showSite(pParReg->m_pSite, FALSE);
                        }
                    }
                    // Get the next ancestor up
                    pBox = pBox->m_pParent;
                }

                // We need to resolve the z-order every time media starts
                // or stops. We only need to resolve from the region's parent
                // down.
                resolveZOrder(pRegion->m_pParent, ulTime);
            }
        }
    }

    return retVal;
}

void CSmilDocumentRenderer::resolveZOrder(CSmilBasicBox* pBox, UINT32 ulTime)
{
    // Resolving z-order is really resolving z-order among our
    // children, so if we don't have any children, then there's
    // nothing to do.
    if (pBox)
    {
        // Get the site for this box. This could be a region site
        // or it could be the root-level site.
        IHXSite* pBoxSite = pBox->m_pSite;
        if (pBoxSite)
        {
            // There can be two kinds of child sites of this site - 
            // region sites and renderer sites. All the child renderer sites
            // must be lower in z-order than the child region sites.
            // So we must sort them first.
            INT32         lCurZOrder = 0;
            CHXSimpleList cTmpList;
            // Run through the site info list and pick out renderer
            // sites which are playing to this region site (potentially
            // more than one). Note that if this is a root-level
            // site, then there won't be any renderers playing to this
            // site.
            if (m_pSiteInfoList)
            {
                LISTPOSITION pos = m_pSiteInfoList->GetHeadPosition();
                while (pos)
                {
                    SMILSiteInfo* pInfo = (SMILSiteInfo*) m_pSiteInfoList->GetNext(pos);
                    if (pInfo && pInfo->m_pRegionSite == pBoxSite)
                    {
                  // /Helps fix PR 81510 (& duplicate PR 83796); if delay
                  // < resume time, use resume time for z-ordering:
                  UINT32 ulRelevantInfoDelay = pInfo->m_ulDelay;
                  if (pInfo->m_ulResumeTime > pInfo->m_ulDelay)
                  {
                      ulRelevantInfoDelay = pInfo->m_ulResumeTime;
                  }
                        // This is a renderer playing to this region, so add it
                        // to the list in the proper order (increasing by begin
                        // time, begin-time tie-breaker by lexical order)
                        BOOL         bAdded = FALSE;
                        LISTPOSITION pos2   = cTmpList.GetHeadPosition();
                        while (pos2)
                        {
                            SMILSiteInfo* pListInfo = (SMILSiteInfo*) cTmpList.GetAt(pos2);
                            if (pListInfo)
                            {
                        UINT32 ulRelevantListInfoDelay = pListInfo->m_ulDelay;
                        if (pListInfo->m_ulResumeTime > pListInfo->m_ulDelay)
                        {
                            ulRelevantListInfoDelay = pListInfo->m_ulResumeTime;
                        }
                                if (pListInfo->m_lZIndex > pInfo->m_lZIndex ||
                                    ulRelevantListInfoDelay > ulRelevantInfoDelay ||
                                    (ulRelevantListInfoDelay  == ulRelevantInfoDelay &&
                                     pListInfo->m_ulLexicalOrder >  pInfo->m_ulLexicalOrder))
                                {
                                    cTmpList.InsertBefore(pos2, (void*) pInfo);
                                    bAdded = TRUE;
                                    break;
                                }
                            }
                            cTmpList.GetNext(pos2);
                        }
                        if (!bAdded)
                        {
                            cTmpList.AddTail((void*) pInfo);
                        }
                    }
                }
                // Now we have ordered the renderer sites, we can
                // set the z-order of the renderer sites
                pos = cTmpList.GetHeadPosition();
                while (pos)
                {
                    SMILSiteInfo* pInfo = (SMILSiteInfo*) cTmpList.GetNext(pos);
                    if (pInfo)
                    {
                        setSiteZIndex(pInfo->m_pRendererSite, lCurZOrder++);
                    }
                }
                // Now we can clear the temporary list
                cTmpList.RemoveAll();
            }
            // After those, we have to order the region sites which may
            // be children of this site. We know that these must be
            // CSmilBasicRegion's.
            if (pBox->m_pChildList)
            {
                LISTPOSITION pos = pBox->m_pChildList->GetHeadPosition();
                while (pos)
                {
                    CSmilBasicRegion* pRegion =
                        (CSmilBasicRegion*) pBox->m_pChildList->GetNext(pos);
                    if (pRegion)
                    {
                        // Now insert this into the z-order list
                        BOOL         bAdded = FALSE;
                        LISTPOSITION pos2   = cTmpList.GetHeadPosition();
                        while (pos2)
                        {
                            CSmilBasicRegion* pListRegion = 
                                (CSmilBasicRegion*) cTmpList.GetAt(pos2);
                            if (pListRegion)
                            {
                                if (pListRegion->m_lZIndex > pRegion->m_lZIndex)
                                {
                                    cTmpList.InsertBefore(pos2, (void*) pRegion);
                                    bAdded = TRUE;
                                    break;
                                }
                                else if (pListRegion->m_lZIndex == pRegion->m_lZIndex)
                                {
                                    UINT32    ulListStartTime    = 0;
                                    UINT32    ulListLexicalOrder = 0;
                                    HX_RESULT lListRet           = getMostRecentInfo(pListRegion->m_pSite,
                                                                                     ulTime,
                                                                                     ulListStartTime,
                                                                                     ulListLexicalOrder);
                                    if (SUCCEEDED(lListRet))
                                    {
                                        UINT32    ulStartTime    = 0;
                                        UINT32    ulLexicalOrder = 0;
                                        HX_RESULT lRet           = getMostRecentInfo(pRegion->m_pSite,
                                                                                     ulTime,
                                                                                     ulStartTime,
                                                                                     ulLexicalOrder);
                                        if (SUCCEEDED(lRet))
                                        {
                                            if (ulListStartTime > ulStartTime ||
                                                (ulListStartTime    == ulStartTime &&
                                                 ulListLexicalOrder >  ulLexicalOrder))
                                            {
                                                cTmpList.InsertBefore(pos2, (void*) pRegion);
                                                bAdded = TRUE;
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                            cTmpList.GetNext(pos2);
                        }
                        if (!bAdded)
                        {
                            cTmpList.AddTail((void*) pRegion);
                        }
                    }
                }
                // Now that we have ordered the child region sites,
                // we can go through and continue setting the z-index.
                pos = cTmpList.GetHeadPosition();
                while (pos)
                {
                    CSmilBasicRegion* pRegion = (CSmilBasicRegion*) cTmpList.GetNext(pos);
                    if (pRegion)
                    {
                        setSiteZIndex(pRegion->m_pSite, lCurZOrder++);
                    }
                }
                // Now we can clear the temporary list
                cTmpList.RemoveAll();
                // Now we must recursively call this method on
                // all the children
                pos = pBox->m_pChildList->GetHeadPosition();
                while (pos)
                {
                    CSmilBasicBox* pChildBox = (CSmilBasicBox*) pBox->m_pChildList->GetNext(pos);
                    resolveZOrder(pChildBox, ulTime);
                }
            }
        }
    }
}

HX_RESULT CSmilDocumentRenderer::getMostRecentInfo(IHXSite* pSite, UINT32 ulTime,
                                                   REF(UINT32) rulStartTime,
                                                   REF(UINT32) rulLexicalOrder)
{
    HX_RESULT retVal = HXR_OK;

    // Clear flag which says we found anything
    BOOL   bFoundOne = FALSE;
    UINT32 ulStart   = 0;
    UINT32 ulLexical = 0;
    // Run through the site info list
    if (pSite && m_pSiteInfoList)
    {
        LISTPOSITION pos = m_pSiteInfoList->GetHeadPosition();
        while (pos)
        {
            SMILSiteInfo* pInfo = (SMILSiteInfo*) m_pSiteInfoList->GetNext(pos);
          // /Helps fix PR 81510 (& duplicate PR 83796); if delay is less than
          // resume time, use resume time as if it were start time:
          UINT32 ulRelevantDelay = pInfo->m_ulDelay;
          if (pInfo->m_ulResumeTime > pInfo->m_ulDelay)
          {
            ulRelevantDelay = pInfo->m_ulResumeTime;
          }
            if (pInfo                         &&
                pInfo->m_pRegionSite == pSite &&
                ulTime               >= ulRelevantDelay)
            {
                if (!bFoundOne)
                {
                    ulStart   = ulRelevantDelay;
                    ulLexical = pInfo->m_ulLexicalOrder;
                    bFoundOne = TRUE;
                }
                else
                {
                    if (ulRelevantDelay > ulStart ||
                        (ulRelevantDelay        == ulStart &&
                         pInfo->m_ulLexicalOrder >  ulLexical))
                    {
                        ulStart   = ulRelevantDelay;
                        ulLexical = pInfo->m_ulLexicalOrder;
                    }
                }
            }
        }
    }
    // Assign the out parameters
    if (bFoundOne)
    {
        rulStartTime    = ulStart;
        rulLexicalOrder = ulLexical;
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

void CSmilDocumentRenderer::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);
    }
}

BOOL CSmilDocumentRenderer::hasParamChildren(CSmilElement* pElement,
                                             BOOL          bServerOnly)
{
    BOOL bRet = FALSE;

    if (pElement && pElement->m_pNode)
    {
        SMILNode* pChild = pElement->m_pNode->getFirstChild();
        while (pChild)
        {
            if (pChild->m_tag == SMILParam)
            {
                if (bServerOnly)
                {
                    CSmilParamElement* pParam = (CSmilParamElement*) pChild->m_pElement;
                    if (pParam &&
                        pParam->m_eDelivery == DeliveryServer)
                    {
                        bRet = TRUE;
                        break;
                    }
                }
                else
                {
                    bRet = TRUE;
                    break;
                }
            }
            pChild = pElement->m_pNode->getNextChild();
        }
    }

    return bRet;
}

void CSmilDocumentRenderer::addParamProperties(CSmilElement* pElement,
                                               IHXValues*   pValues,
                                               IUnknown*     pContext,
                                               BOOL          bServerOnly)
{
    if (pElement && pElement->m_pNode && pValues && pContext)
    {
        SMILNode* pChild = pElement->m_pNode->getFirstChild();
        while (pChild)
        {
            if (pChild->m_tag == SMILParam && pChild->m_pElement)
            {
                CSmilParamElement* pParam = (CSmilParamElement*) pChild->m_pElement;
                if (pParam->m_pName &&
                    pParam->m_pValue &&
                    ((bServerOnly && pParam->m_eDelivery == DeliveryServer) || !bServerOnly))
                {
                    pValues->SetPropertyCString((const char*) pParam->m_pName->GetBuffer(),
                                                pParam->m_pValue);
                }
            }
            pChild = pElement->m_pNode->getNextChild();
        }
    }
}

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)

void CSmilDocumentRenderer::checkAnimChildren(CSmilElement* pElement, UINT32 ulTrackEnd)
{
    if (pElement && pElement->m_pNode)
    {
        SMILNode* pChild = pElement->m_pNode->getFirstChild();
        while (pChild)
        {
            if (pChild->m_tag == SMILAnimate       ||
                pChild->m_tag == SMILAnimateMotion ||
                pChild->m_tag == SMILAnimateColor  ||
                pChild->m_tag == SMILAnimateMotion)
            {
                CSmilAnimateElement* pAnim =
                    (CSmilAnimateElement*) pChild->m_pElement;
                if (pAnim)
                {
                    if (pAnim->m_ulDelay >= ulTrackEnd)
                    {
                        // Our parent totally prevents us from
                        // starting. Since the event is probably
                        // already scheduled, then we will simply
                        // mark this to cancel this animation
                        pAnim->m_bCancelAnimation = TRUE;
                    }
                    else if (pAnim->m_ulDelay < ulTrackEnd &&
                             pAnim->m_ulDelay + pAnim->m_ulActiveDuration > ulTrackEnd)
                    {
                        // The delay is less than the track end, but
                        // the delay plus active duration is cut off
                        // by the track end. Therefore, we will shorten
                        // the active duration to match the track end.
                        pAnim->m_ulActiveDuration = ulTrackEnd - pAnim->m_ulDelay;
                    }
                }
            }
            pChild = pElement->m_pNode->getNextChild();
        }
    }
}

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

#if defined(HELIX_FEATURE_SMIL2_TRANSITIONS)

CSmilTransitionInfo::CSmilTransitionInfo(CSmilTransition* pTrans, 
                                         IUnknown*        pContext)
{
    HX_ASSERT(pTrans);
    HX_ASSERT(pContext);
    m_pTrans   = pTrans;
    m_pContext = pContext;
    if (m_pContext)
    {
        // AddRef the context
        m_pContext->AddRef();
    }
}

CSmilTransitionInfo::~CSmilTransitionInfo()
{
    HX_RELEASE(m_pContext);
}

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

// /IHXClientAdviseSink Interface Methods:

STDMETHODIMP
CSmilDocumentRenderer::OnPosLength(UINT32   ulPosition,
                           UINT32     ulLength)
{
    MLOG_TIMESYNC(m_pErrorMessages, "OnPosLength(%lu,%lu) tick=%lu\n",
                  ulPosition, ulLength, HX_GET_BETTERTICKCOUNT());
    // /Note: this *should* have been done in OnBuffering() when 100%
    // was complete, but the renderers don't get a chance to draw until
    // after OnBuffering(100%) is done, apparently:
    if (ulPosition > 0  &&  m_bDestPlaystateIsPause)
    {
      // /Done buffering, and we've had a chance to draw the first "frame"
      // of each media stream's data, so let's pause it if
      // "destinationPlaystate" was "pause":
      IHXPlayer* pPlayer = m_pParent->getPlayer();
      if(pPlayer)
      {
          pPlayer->Pause();
      }
      m_bDestPlaystateIsPause = FALSE;
    }
    // The ulLength argument is the current duration
    // of the current group. Save this value
    m_ulCurGroupDuration = ulLength;

    return HXR_OK;
}


STDMETHODIMP
CSmilDocumentRenderer::OnPresentationOpened()
{
    return HXR_NOTIMPL;
}


STDMETHODIMP
CSmilDocumentRenderer::OnPresentationClosed()
{
// /   HX_ASSERT(0=="XXXEH: implementThis!");
    // /When we get this notification, we need to decide whether or not
    // the caller of this is the player we were playing in or a new
    // player that opened (and may mean we're in a paused state):
    return HXR_NOTIMPL;
}


STDMETHODIMP
CSmilDocumentRenderer::OnStatisticsChanged(void)
{
    return HXR_NOTIMPL;
}


STDMETHODIMP
CSmilDocumentRenderer::OnPreSeek(UINT32   ulOldTime, UINT32 ulNewTime)
{
    return HXR_NOTIMPL;
}


STDMETHODIMP
CSmilDocumentRenderer::OnPostSeek(UINT32 ulOldTime, UINT32 ulNewTime)
{
    return HXR_NOTIMPL;
}


STDMETHODIMP
CSmilDocumentRenderer::OnStop(void)
{
    return HXR_NOTIMPL;
}


STDMETHODIMP
CSmilDocumentRenderer::OnPause(UINT32 ulTime)
{
    return HXR_NOTIMPL;
}


// Called by client engine to inform that a begin OR resume has just occured:
STDMETHODIMP
CSmilDocumentRenderer::OnBegin(UINT32 ulTime)
{
    // /First, if we have a fragment offset in the URL that we're playing,
    // now is the time to do the seek of the player since it isn't ready
    // to be seeked until this point:
    IHXPlayer* pPlayer = m_pParent->getPlayer();
    HX_ASSERT(pPlayer);
    //If getFragmentOffset() found the fragment and it had
    // a resolved begin time (plus offset) of zero,
    // we used to not seek to 0 even though that's a valid
    // value.
    // (Note: this problem was found while fixing PR 22655.)
    if(pPlayer  &&  m_bFragFoundAndResolved)
    {
      pPlayer->Seek(m_ulFragmentTimeOffset);
    }

    return HXR_OK;
}


// Called by client engine to inform the client that buffering
// of data is occuring. The render is informed of the reason for
// the buffering (start-up of stream, seek has occured, network
// congestion, etc.), as well as percentage complete of the 
// buffering process.
STDMETHODIMP
CSmilDocumentRenderer::OnBuffering(UINT32 ulFlags, UINT16 unPercentComplete)
{
    // /Note: deciding on whether or not to pause *should* have been
    // done here when 100% was complete, but the renderers don't get
    // a chance to draw until after OnBuffering(100%) is done, apparently,
    // so the code to handle this is in OnPosLength().
    return HXR_NOTIMPL;
}


/****************************************************************************
 *  IHXClientAdviseSink::OnContacting                       ref:  hxclsnk.h
 *
 *  Called by client engine to inform the client is contacting hosts(s).
 */
STDMETHODIMP
CSmilDocumentRenderer::OnContacting(const char* pHostName)
{
    return HXR_NOTIMPL;
}

HX_RESULT
CSmilDocumentRenderer::GetElementProperties(UINT16          uGroupID, 
                                  UINT16        uTrackID, 
                                  REF(IHXValues*)     pProperties)
{
    HX_RESULT           rc = HXR_OK;
    CSmilElement* pElement = NULL;

    pProperties = NULL;

    SMILPlayToAssoc* pPlayToAssoc = getPlayToAssoc(uGroupID, uTrackID);
    if (pPlayToAssoc)
    {
      pElement = m_pSmilParser->findElement(pPlayToAssoc->m_id);
      if (pElement)
      {
          rc = pElement->GetElementProperties(pProperties);
      }
    }

    return rc;
}

HX_RESULT          
CSmilDocumentRenderer::GetElementStatus(UINT16            uGroupID,
                                UINT16              uTrackID,
                                UINT32              ulCurrentTime,
                                REF(IHXValues*)    pStatus)
{
    HX_RESULT           rc = HXR_OK;
    CHXSimpleList*      pSiteInfoList = NULL;
    CSmilElement* pElement = NULL;
    CSmilShowSiteEvent* pCurHideEvent = NULL;
    SMILPlayToAssoc*    pPlayToAssoc = getPlayToAssoc(uGroupID, uTrackID);

    pStatus = NULL;

    if (pPlayToAssoc && pPlayToAssoc->m_pSiteInfoList)
    {
      pSiteInfoList = pPlayToAssoc->m_pSiteInfoList;
      
        LISTPOSITION pos = pSiteInfoList->GetHeadPosition();
        while (pos)
        {
            SMILSiteInfo* pSiteInfo = (SMILSiteInfo*)pSiteInfoList->GetNext(pos);
            if (pSiteInfo)
            {
            pCurHideEvent = getShowHideEvent(pPlayToAssoc->m_id,
                                                 pSiteInfo->m_regionID,
                                                 FALSE);

            if (pCurHideEvent && pCurHideEvent->m_ulEventTime > ulCurrentTime)
            {
                pStatus = new CHXHeader();
                pStatus->AddRef();

                pStatus->SetPropertyULONG32("Show", 1);
                break;
            }
          }
      }
    }

    return rc;
}

HX_RESULT
CSmilDocumentRenderer::AttachElementLayout(UINT16         uGroupID,
                                 UINT16       uTrackID,
                                 IHXRenderer*    pRenderer,
                                 IHXStream*         pStream,
                                 IHXValues*         pProps)
{
    HX_RESULT           rc = HXR_OK;    
    UINT32        ulDelay = 0;
    UINT32        ulDuration = 0;
    IHXLayoutStream*    pLayoutStream = NULL;
    CSmilBasicRegion*   pRegion = NULL;
    CHXSimpleList*      pRendererList = NULL;
    SMILSourceInfo*     pSourceInfo = NULL;
    SMILPlayToAssoc*    pPlayToAssoc = NULL;

    // setup the Root layout if it has not
    // this could happen when the child SMIL gets initialized 
    // ahead of its parent when switching groups
    if (m_bSitesDetached)
    {
      m_bSitesDetached = FALSE;
      rc = setupRootLayout(FALSE);
      HX_ASSERT(HXR_OK == rc);
    }

    pPlayToAssoc = getPlayToAssoc(uGroupID, uTrackID);
    HX_ASSERT(pPlayToAssoc);

    pRegion = getRegionByID(pPlayToAssoc->m_playTo);    
    if (!pRegion)
    {
      // We didn't find a <region> by id and we 
      // didn't find a <region> by the "regionName"
      // indirection, so we are going to have to 
      // fall back to the "default" region.
      pRegion = setupDefaultLayout();
      if (pRegion)
      {
          // Change the parameters
          pPlayToAssoc->m_playTo     = pRegion->m_region;
          pPlayToAssoc->m_regionName = pRegion->m_region;
      }
    }

    // vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
    // /XXXEH- To do: the code inside this if() and the code after the else is
    // the same code (reordered a bit) as found inside RendererInitialized(),
    // above.  To do: Combine into a function noting that the following also
    // calls HandleAttachElementLayout(), not done in RendererInitialized():
    if (pStream && pProps)
    {
      pSourceInfo = new SMILSourceInfo;
      pSourceInfo->m_pStream = pStream;
      if(pSourceInfo->m_pStream)
      {
          pSourceInfo->m_pStream->AddRef();
      }
      pSourceInfo->m_pRenderer = pRenderer;
      if(pSourceInfo->m_pRenderer)
      {
          pSourceInfo->m_pRenderer->AddRef();
      }
    
      pProps->GetPropertyULONG32("Delay", ulDelay);
      pProps->GetPropertyULONG32("Duration", ulDuration);

      pSourceInfo->m_ulDelay = ulDelay;
      pSourceInfo->m_ulDuration = ulDuration - ulDelay;

      pRendererList = (CHXSimpleList*) pPlayToAssoc->m_sourceMap[0];
      HX_ASSERT(pRendererList);
      // /Check for NULL as happens in PR 120999 when jumping back
      // and forth between clips in a clip list:
      if (pRendererList)
      {
          pRendererList->AddTail(pSourceInfo);
      }
    }
    else
    {
      pRendererList = (CHXSimpleList*)pPlayToAssoc->m_sourceMap[0];
      HX_ASSERT(pRendererList->GetCount() == 1);

      pSourceInfo = (SMILSourceInfo*)pRendererList->GetHead();
    }

    char cTemp[20]; /* Flawfinder: ignore */
    ::sprintf(cTemp,"%#010lx",(ULONG32)(void*)pRenderer); /* Flawfinder: ignore */
    pSourceInfo->m_tunerName   = (const char*) cTemp;
    ::sprintf(cTemp,"%#010lx",(ULONG32)(void*)pRenderer+1); /* Flawfinder: ignore */
    pSourceInfo->m_childTunerName = (const char*)cTemp;

    const char* pChildTuner = pSourceInfo->m_childTunerName;

    // get to the site manager and set an event hook
    IHXEventHookMgr* pHookMgr = NULL;
    if(HXR_OK ==
      m_pSiteMgr->QueryInterface(IID_IHXEventHookMgr, 
                        (void**)&pHookMgr))
    {
      CSmilEventHook* pChildEventHook = NULL;

      // create event hook for playto
      pChildEventHook = new CSmilEventHook((CSmilEventHookResponse*) this,
                                   pPlayToAssoc->m_playTo, pChildTuner, FALSE,
                                   (const char*) pPlayToAssoc->m_id);
      pChildEventHook->AddRef();

      pHookMgr->AddHook(pChildEventHook, pChildTuner, 0);

      pSourceInfo->m_pRendererEventHook = pChildEventHook;

      pHookMgr->Release();
    }
    else
    {
      pSourceInfo->m_pRendererEventHook = NULL;
    }

    pPlayToAssoc->m_tunerName = pSourceInfo->m_tunerName;
    pPlayToAssoc->m_childTunerName = pSourceInfo->m_childTunerName;
    pPlayToAssoc->m_pRendererEventHook = pSourceInfo->m_pRendererEventHook;

    // add hyperlinks
    CSmilElement* pElement = m_pSmilParser->findElement(
      pPlayToAssoc->m_id);
    if(pElement && pElement->m_pHyperlinks)
    {
      CHXSimpleList::Iterator i = 
            pElement->m_pHyperlinks->Begin();
      for(; i != pElement->m_pHyperlinks->End(); ++i)
      {
          CSmilAAnchorElement* pAnchor = 
            (CSmilAAnchorElement*)(*i);
          //[SMIL 1.0 Compliance] Fixes PR 26473:
          // We want to add in LIFO fashion so inner
          // (nested) anchors will be found first in
          // CSmilDocumentRenderer::findHyperlinkElement(),
          // below.  In other words, we're giving an
          // effective higher link "z-order" to the
          // decendants of other links.  This used to
          // call AddTail():
          pPlayToAssoc->m_pHyperlinks->AddHead(pAnchor);
      }
    }

    IHXValues* pValues = 0;
    IHXBuffer* pPlayToBuffer = 0;
    IHXBuffer* pRegionName = 0;
    IHXCommonClassFactory* pFactory = m_pParent->getFactory();
    if ((HXR_OK == pFactory->CreateInstance(CLSID_IHXValues, (void**)&pValues)) &&
      (HXR_OK == pFactory->CreateInstance(CLSID_IHXBuffer, (void**)&pPlayToBuffer)) &&
      (HXR_OK == pFactory->CreateInstance(CLSID_IHXBuffer, (void**)&pRegionName)))
    {           
      pPlayToBuffer->Set((BYTE*)pChildTuner, strlen(pChildTuner)+1);
      pValues->SetPropertyCString("playto", pPlayToBuffer);

      if(pPlayToAssoc->m_regionName.GetLength() > 0)
      {
          const char* pName = pPlayToAssoc->m_regionName;
          pRegionName->Set((BYTE*)pName, strlen(pName)+1);
          pValues->SetPropertyCString("region", pRegionName);
      }
    }
    HX_RELEASE(pPlayToBuffer);
    HX_RELEASE(pRegionName);

    if (pStream &&
      HXR_OK == pStream->QueryInterface(IID_IHXLayoutStream, (void**)&pLayoutStream))
    {
      pLayoutStream->SetProperties(pValues);
    }
    HX_RELEASE(pLayoutStream);

    if(!pRegion->m_bImplicitRegion)
    {
      addSiteForRenderer(pPlayToAssoc, pSourceInfo, pRenderer, FALSE);
    
      if (!pStream)
      {
          m_pParent->HandleAttachElementLayout((IUnknown*)pRenderer, pValues);
      }
    }
    HX_RELEASE(pValues);
    // /XXXEH- end of "To do: the code inside this...".
    // /^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    return rc;
}

HX_RESULT
CSmilDocumentRenderer::DetachElementLayout(IUnknown* pLSG)
{
    HX_RESULT     rc = HXR_OK;
    return rc;
}

BOOL CSmilDocumentRenderer::isRegionActive(const char* pszRegID, UINT16 usGroup, UINT32 ulTime)
{
    BOOL bRet = FALSE;

    if(m_pPlayToAssocList && pszRegID)
    {
        LISTPOSITION pos = m_pPlayToAssocList->GetHeadPosition();
        while (pos)
        {
            SMILPlayToAssoc* pAssoc =
                (SMILPlayToAssoc*) m_pPlayToAssocList->GetNext(pos);
            if (pAssoc &&
                pAssoc->m_uGroupIndex == usGroup &&
                pAssoc->m_playTo      == pszRegID &&
                ulTime                >= pAssoc->m_ulDelay &&
                ulTime                <  pAssoc->m_ulDelay + pAssoc->m_ulDuration)
            {
                bRet = TRUE;
                break;
            }
        }
    }

    return bRet;
}

#if defined(BE_SITE_SUPPLIER_20010205)
// IHXSiteSupplier Interface Methods

/****************************************************************************
 *  IHXSiteSupplier::SitesNeeded                              ref:  hxwin.h
 *
 */
STDMETHODIMP
CSmilDocumentRenderer::SitesNeeded(UINT32 uRequestID, IHXValues* pProps)
{ 
    HX_RESULT pnrslt = HXR_OK; 

    HX_ASSERT(pProps); 
    if (!pProps) 
    { 
      pnrslt = HXR_INVALID_PARAMETER; 
    } 
    else 
    { 
      IHXSite* pChildSite = NULL;

      SMILPlayToAssoc* pPlayToAssoc = NULL;
      BOOL bNoRegion = TRUE;

      // See if we need to layout this renderer
      // First check to see if we have a region by id
      CSmilBasicRegion* pRegion =
            getRegionByID((const char*)m_XXXEH_HACK_regionId);
      if(pRegion)
      {
          bNoRegion = FALSE;
      }
      else
      {
          // We didn't find it by id, so try finding a region by regionName:
          pRegion = getFirstRegionByName(m_XXXEH_HACK_regionId);
          if (pRegion)
          {
            bNoRegion = FALSE;
          }
          else
          {
            // Create a CSmilBasicRegion
            pRegion = new CSmilBasicRegion(NULL);
            if (pRegion)
            {
                // Set the playto
                pRegion->m_region = m_XXXEH_HACK_regionId;
                // Set this as an implicit region
                pRegion->m_bImplicitRegion = TRUE;
                // Set width and height as unspecified
                pRegion->m_bWidthUnspecified  = TRUE;
                pRegion->m_bHeightUnspecified = TRUE;
                // Add it to the map
                (*m_pRegionMap)[m_XXXEH_HACK_regionId] = pRegion;
            }
          }
      }

      pPlayToAssoc = pRegion? getPlayToAssoc(pRegion->m_region) : NULL;
      HX_ASSERT(pPlayToAssoc);
      if (!pPlayToAssoc)
      {
          pnrslt = HXR_UNEXPECTED;
      }
      else
      {
          const char* pPlayTo = pPlayToAssoc->m_playTo;

          IHXSite* pSite = pRegion->m_pSite;

          // Create a child site within the region:
          pSite->CreateChild(pChildSite);
            // Put this site into this region's list
            // of child renderer sites
            pRegion->addRendererSiteChild(pChildSite);

          // /Create a site watcher for this site
          CSmilSiteWatcher* pSiteWatch = new CSmilSiteWatcher(this,
                pRegion->m_region, TRUE /* <==is a child site */,
                pPlayToAssoc->m_id);
          if (pSiteWatch)
          {
            // AddRef the watcher
            pSiteWatch->AddRef();
            // Get the top-level box
            CSmilBasicBox* pTopBox = getTopLevelBox(pRegion);
            if (pTopBox  &&  pTopBox->m_eResizeBehavior == ResizeZoom  &&
                  !m_bDoNotZoom)
            {
                // Initialize the watcher with the current zoom factor;
                // is important if the player starts up in some zoom mode
                // other than original size.
                pSiteWatch->SetZoomScaleFactors(pRegion->m_dZoomScaleFactorX,
                                          pRegion->m_dZoomScaleFactorY);
            }
            // Attach the watcher to the renderer site
            HX_RESULT attchWatchRetVal = pChildSite->AttachWatcher(pSiteWatch);
            if (SUCCEEDED(attchWatchRetVal))
            {
                // Create the site watcher map if necessary
                if (!m_pSiteWatcherMap)
                {
                  m_pSiteWatcherMap = new CHXMapPtrToPtr();
                }
                if (m_pSiteWatcherMap)
                {
                  // Add the site watcher to the site watcher map
                  m_pSiteWatcherMap->SetAt((void*) pChildSite,
                                     (void*) pSiteWatch);
                }
            }
          }

          HXxSize cCurSize;
          pSite->GetSize(cCurSize);
          cCurSize.cx-=40;
          cCurSize.cy-=40;
          pChildSite->SetSize(cCurSize);


          IHXSite2*  pSite2 = NULL;
          if (pChildSite  &&  pChildSite->QueryInterface(IID_IHXSite2,
                (void**)&pSite2) == HXR_OK)
          {
            pSite2->ShowSite(TRUE);
            pSite2->Release();
          }

          HRESULT     hresTemp      = HXR_OK;
          IHXValues*        pSiteProps    = NULL;
          IHXBuffer*        pValue  = NULL;
          UINT32      style   = 0;

          HX_VERIFY(HXR_OK == pChildSite->QueryInterface(IID_IHXValues,(void**)&pSiteProps));
          HX_ASSERT(pSiteProps);

          CSmilElement* pElement =
                m_pSmilParser->findElement(pPlayToAssoc->m_id);
          HX_ASSERT(pElement);

          // /Set the "sensitivity" (to mouse events) of the renderer site:
          if (pPlayToAssoc->m_id.GetLength() > 0  && m_pSmilParser)
          {
            ULONG32 ulSensitivityStrLen = 0;
            if (pElement &&
                  pElement->m_sensitivityToMouseEvents.GetLength() > 0)
            {
                setSiteProperty(pChildSite, "sensitivity",
                      (const char*)pElement->m_sensitivityToMouseEvents);
            }
          }

          hresTemp = pProps->GetPropertyCString("playto",pValue);
          if (HXR_OK == hresTemp)
          {
            pSiteProps->SetPropertyCString("channel",pValue);
            pValue->Release();
          }
          else
          {
            hresTemp = pProps->GetPropertyCString("name",pValue);
            if (HXR_OK == hresTemp)
            {
                pSiteProps->SetPropertyCString("LayoutGroup",pValue);
                pValue->Release();
            }
          }

          pSiteProps->Release();

          // /Use child player site mgr.  It works; don't ask:
          IHXSiteManager* pSiteMgrOfIndependentPlayer = NULL;
          if (m_pIndependentPlayer  &&
                HXR_OK == m_pIndependentPlayer->
                QueryInterface(IID_IHXSiteManager,
                (void**)&pSiteMgrOfIndependentPlayer))
          {
            // /Properties are set so now it's OK to add the site:
            pSiteMgrOfIndependentPlayer->AddSite(pChildSite);

            if (!m_pChildSiteInfoMap)
            {
                m_pChildSiteInfoMap = new CHXMapLongToObj;
            }
            if (m_pChildSiteInfoMap)
            {
                pChildSite->AddRef();
                pSite->AddRef();
                pSiteMgrOfIndependentPlayer->AddRef();
                SMILChildSiteInfo* pChildSiteInfo = new SMILChildSiteInfo(
                      pChildSite, pSite, pSiteMgrOfIndependentPlayer);
                m_pChildSiteInfoMap->SetAt(uRequestID, pChildSiteInfo);
            }
          }
      }
    }

    return pnrslt;
}

/****************************************************************************
 *  IHXSiteSupplier::SitesNotNeeded                           ref:  hxwin.h
 *
 */
STDMETHODIMP 
CSmilDocumentRenderer::SitesNotNeeded(UINT32 uRequestID)
{
    SMILChildSiteInfo* pChildSiteInfo = NULL;

    // Locate the site
    if (!m_pChildSiteInfoMap  ||
          !m_pChildSiteInfoMap->Lookup(uRequestID, (void*& )pChildSiteInfo)  ||
          !pChildSiteInfo  ||  !pChildSiteInfo->m_pChildSite)
    {
      HX_ASSERT(m_pChildSiteInfoMap  &&  pChildSiteInfo);
      HX_ASSERT(pChildSiteInfo->m_pChildSite);
      return HXR_INVALID_PARAMETER;
    }

    if (m_pSiteWatcherMap)
    {
        void* pVoid = NULL;
        if (m_pSiteWatcherMap->Lookup((void*)pChildSiteInfo->m_pChildSite, pVoid)  &&  pVoid)
        {
            // Get the CSmilSiteWatcher pointer
            CSmilSiteWatcher* pWatcher = (CSmilSiteWatcher*)pVoid;
            // Remove the site watcher from the site watcher map
            m_pSiteWatcherMap->RemoveKey((void*)pChildSiteInfo->m_pChildSite);
            // Release our ref on the site watcher
            HX_RELEASE(pWatcher);
        }
    }

    HX_ASSERT(pChildSiteInfo->m_pSiteMgr);
    HX_ASSERT(pChildSiteInfo->m_pParentSite);

    // Perform cleanup on child site
    pChildSiteInfo->m_pChildSite->DetachWatcher();
    pChildSiteInfo->m_pSiteMgr->RemoveSite(pChildSiteInfo->m_pChildSite);
    pChildSiteInfo->m_pParentSite->DestroyChild(pChildSiteInfo->m_pChildSite);
    pChildSiteInfo->m_pChildSite->Release();

    // Remove from map and free memory
    m_pChildSiteInfoMap->RemoveKey(uRequestID);
    delete pChildSiteInfo;

    return HXR_OK;
}

/****************************************************************************
 *  IHXSiteSupplier::BeginChangeLayout                        ref:  hxwin.h
 *
 */
STDMETHODIMP 
CSmilDocumentRenderer::BeginChangeLayout()
{
    return HXR_OK;
}


/****************************************************************************
 *  IHXSiteSupplier::DoneChangeLayout                         ref:  hxwin.h
 *
 */
STDMETHODIMP 
CSmilDocumentRenderer::DoneChangeLayout()
{
    return HXR_OK;
}
#endif /* BE_SITE_SUPPLIER_20010205 */

STDMETHODIMP CSmilDocumentRenderer::MarkerResolved(IHXBuffer* pURLStr,
                                                   IHXBuffer* pMarkerNameStr,
                                                   UINT32      ulTime,
                                                   IHXValues* pOtherMarkerParams)
{
    HX_RESULT retVal = HXR_OK;

    // Now we need to run through the playToAssoc list
    // and find any id's which correspond to this URL
    if (pURLStr && pMarkerNameStr && m_pPlayToAssocList)
    {
        LISTPOSITION pos = m_pPlayToAssocList->GetHeadPosition();
        while (pos)
        {
            SMILPlayToAssoc* pAssoc =
                (SMILPlayToAssoc*) m_pPlayToAssocList->GetNext(pos);
            if (pAssoc &&
                !strcmp((const char*) pURLStr->GetBuffer(),
                        (const char*) pAssoc->m_URL))
            {
                // Init a temp list
                CHXSimpleList cTmp;
                // First check and see if this URL comes from
                // an external media marker file.
                CExternalMediaMarkerInfo* pInfo = getExternalMediaMarkerInfo(pAssoc->m_URL);
                if (pInfo && pInfo->m_pIDList)
                {
                    LISTPOSITION pos = pInfo->m_pIDList->GetHeadPosition();
                    while (pos)
                    {
                        const char* pszID = (const char*) pInfo->m_pIDList->GetNext(pos);
                        if (pszID)
                        {
                            cTmp.AddTail((void*) pszID);
                        }
                    }
                }
                else
                {
                    cTmp.AddTail((void*) (const char*) pAssoc->m_id);
                }
                // Now run through our temporary list and 
                // resolve this marker to all id's.
                BOOL         bDoHandle = FALSE;
                LISTPOSITION tpos      = cTmp.GetHeadPosition();
                while (tpos && SUCCEEDED(retVal))
                {
                    const char* pszID = (const char*) cTmp.GetNext(tpos);
                    if (pszID)
                    {
                        BOOL bHandle = FALSE;
                        retVal = m_pSmilParser->resolveMediaMarkerTime(pszID,
                                                                       (const char*) pMarkerNameStr->GetBuffer(),
                                                                       ulTime,
                                                                       bHandle);
                        if (SUCCEEDED(retVal) && bHandle)
                        {
                            bDoHandle = TRUE;
                        }
                    }
                }
                if (SUCCEEDED(retVal) && bDoHandle)
                {
                    handleElements();
                }
            }
        }
    }

    return retVal;
}

STDMETHODIMP CSmilDocumentRenderer::EventFired(IHXBuffer* pURLStr,
                                               IHXBuffer* pFragmentStr,
                                               IHXBuffer* pEventNameStr,
                                               IHXValues* pOtherValues)
{
    HX_RESULT retVal = HXR_OK;

    if (pURLStr && pEventNameStr)
    {
        LISTPOSITION pos = m_pPlayToAssocList->GetHeadPosition();
        while (pos)
        {
            SMILPlayToAssoc* pAssoc =
                (SMILPlayToAssoc*) m_pPlayToAssocList->GetNext(pos);
            if (pAssoc &&
                !strcmp((const char*) pAssoc->m_URL,
                        (const char*) pURLStr->GetBuffer()))
            {
                retVal = handleNamedEvent((const char*) pAssoc->m_playTo,
                                          (const char*) pAssoc->m_id, 
                                          (const char*) pEventNameStr->GetBuffer());
            }
        }
    }
    else
    {
        retVal = HXR_FAIL;
    }

    return retVal;
}

STDMETHODIMP CSmilDocumentRenderer::ErrorOccurred(const UINT8   unSeverity,
                                                  const ULONG32 ulHXCode,
                                                  const ULONG32 ulUserCode,
                                                  const char*   pUserString,
                                                  const char*   pMoreInfoURL)
{
    HX_RESULT retVal = HXR_OK;

    MLOG_FLOW(m_pErrorMessages, "ErrorOccurred(%u,%lu,%lu,%s,%s)\n",
              unSeverity, ulHXCode, ulUserCode, pUserString, pMoreInfoURL);

    return retVal;
}

HX_RESULT
CSmilDocumentRenderer::InitPersistent(UINT32                ulPersistentComponentID,
                              UINT16                  uPersistentGroupID,
                              UINT16                  uPersistentTrackID,
                              IHXPersistentRenderer*  pPersistentParent)
{
    IHXStreamSource*    pPersistentSource = NULL;
    IHXStream*          pPersistentStream = NULL;

    m_ulPersistentComponentID = ulPersistentComponentID;
    m_uPersistentGroupID = uPersistentGroupID;
    m_uPersistentTrackID = uPersistentTrackID;

    m_pPersistentParentRenderer = pPersistentParent;
    HX_ADDREF(m_pPersistentParentRenderer);

    if (m_pContext &&
      HXR_OK == m_pContext->QueryInterface(IID_IHXStreamSource, (void**)&pPersistentSource))
    {
      if (HXR_OK == pPersistentSource->GetStream(0, (IUnknown*&)pPersistentStream))
      {
          if (HXR_OK != pPersistentStream->QueryInterface(IID_IHXLayoutStream, (void**)&m_pPersistentLayoutStream))
          {   
            HX_ASSERT(m_pPersistentLayoutStream);
          }
      }
      HX_RELEASE(pPersistentStream);
    }
    HX_RELEASE(pPersistentSource);
          
    return HXR_OK;
}

BOOL
CSmilDocumentRenderer::IsNestedMetaSupported(void)
{
    BOOL        bResult = TRUE;    
    UINT32      ulParentPersistentVersion = 0;
    UINT32      ulParentPersistentMajorVersion = 0;
    UINT32      ulParentPersistentType = PersistentUnknown;
    IHXValues*        pProperties = NULL;

    if (!m_pPersistentParentRenderer)
    {
      return bResult;
    }

    if (HXR_OK == m_pPersistentParentRenderer->GetPersistentProperties(pProperties))
    {
      pProperties->GetPropertyULONG32("PersistentType", ulParentPersistentType);
      pProperties->GetPropertyULONG32("PersistentVersion", ulParentPersistentVersion);

      ulParentPersistentMajorVersion = HX_GET_MAJOR_VERSION(ulParentPersistentVersion);

      switch (ulParentPersistentType)
      {
      case PersistentUnknown:
          bResult = FALSE;
          break;
      case PersistentSMIL:
          if (ulParentPersistentMajorVersion == 1)
          {
            bResult = FALSE;
          }
          break;
      default:
          break;
      }
    }
    HX_RELEASE(pProperties);
    
    return bResult;
}

void
CSmilDocumentRenderer::PersistentDurationSet(UINT32 ulDuration, 
                                   UINT32 ulDelay, 
                                   BOOL bIsLive)
{
    IHXRendererAdviseSink* pRendererAdviseSink = NULL;

    if (m_pPersistentParentRenderer &&
      HXR_OK == m_pPersistentParentRenderer->QueryInterface(IID_IHXRendererAdviseSink, (void**)&pRendererAdviseSink))
    {
      pRendererAdviseSink->TrackDurationSet(m_uPersistentGroupID,
                                    m_uPersistentTrackID,
                                    ulDuration,
                                    ulDelay,
                                    bIsLive);
    }
    HX_RELEASE(pRendererAdviseSink);

    return;
}

FillType CSmilDocumentRenderer::getMediaFillBehavior(const char* pszID)
{
    FillType eRet = FillDefault;

    if (pszID && m_pSmilParser)
    {
        CSmilElement* pElement = m_pSmilParser->findElement(pszID);
        if (pElement)
        {
            eRet = pElement->m_eActualFill;
        }
    }

    return eRet;
}

BOOL CSmilDocumentRenderer::atLeastOneActiveAnimation(UINT32 ulTime)
{
    BOOL bRet = FALSE;

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)
    if (m_pActiveAnimations &&
        m_pActiveAnimations->GetCount() > 0)
    {
        // Run through all active animations and check
        LISTPOSITION pos = m_pActiveAnimations->GetHeadPosition();
        while (pos)
        {
            CSmilAnimateInfo* pInfo =
                (CSmilAnimateInfo*) m_pActiveAnimations->GetNext(pos);
            if (pInfo &&
                pInfo->m_pSandwich &&
                pInfo->m_pSandwich->GetAttributeName() != kAttrNameSoundLevel &&
                pInfo->m_pSandwich->AtLeastOneActiveLayer(ulTime))
            {
                bRet = TRUE;
                break;
            }
        }
    }
#endif /* #if defined(HELIX_FEATURE_SMIL2_ANIMATION) */

    return bRet;
}

void CSmilDocumentRenderer::clearAllEventHandledFlags()
{
    if(m_pEventList && m_pEventList->GetCount() > 0)
    {
        LISTPOSITION pos = m_pEventList->GetHeadPosition();
        while(pos)
        {
            CSmilLayoutEvent* pEvent =
                (CSmilLayoutEvent*) m_pEventList->GetNext(pos);
            if (pEvent)
            {
                pEvent->setHandledFlag(FALSE);
            }
        }
    }
}

HX_RESULT CSmilDocumentRenderer::setRendererULONG32Property(const char* pszMediaID,
                                                            const char* pszName,
                                                            UINT32      ulValue,
                                                            REF(BOOL)   rbSetNewValue)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pszMediaID && pszName)
    {
        // Set the default
        rbSetNewValue = FALSE;
        // Get the IHXRenderer
        IHXRenderer* pRenderer = NULL;
        retVal = getRenderer(pszMediaID, pRenderer);
        if (SUCCEEDED(retVal))
        {
            // QI IHXRenderer for IHXValues
            IHXValues* pValues = NULL;
            retVal = pRenderer->QueryInterface(IID_IHXValues, (void**) &pValues);
            if (SUCCEEDED(retVal))
            {
                // Get the current value. If we can't get the value,
                // that's OK, since we are only using this current
                // value to optimize out any unnecessary setting
                // of properties
                UINT32 ulCurValue = 0;
                HX_RESULT rv = pValues->GetPropertyULONG32(pszName, ulCurValue);
                // Is the current value different from the 
                // value we're trying to set?
                if ((SUCCEEDED(rv) && ulCurValue != ulValue) || FAILED(rv))
                {
                    retVal = pValues->SetPropertyULONG32(pszName, ulValue);
                    if (SUCCEEDED(retVal))
                    {
                        rbSetNewValue = TRUE;
                    }
                }
            }
            HX_RELEASE(pValues);
        }
        HX_RELEASE(pRenderer);
    }

    return retVal;
}

HX_RESULT CSmilDocumentRenderer::setRendererCStringProperty(const char* pszMediaID,
                                                            const char* pszName,
                                                            const char* pszValue,
                                                            REF(BOOL)   rbSetNewValue)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pszMediaID && pszName && pszValue)
    {
        // Set the default
        rbSetNewValue = FALSE;
        // Get the IHXRenderer
        IHXRenderer* pRenderer = NULL;
        retVal = getRenderer(pszMediaID, pRenderer);
        if (SUCCEEDED(retVal))
        {
            // QI IHXRenderer for IHXValues
            IHXValues* pValues = NULL;
            retVal = pRenderer->QueryInterface(IID_IHXValues, (void**) &pValues);
            if (SUCCEEDED(retVal))
            {
                // Get the current value. If we can't get the value,
                // that's OK, since we are only using this current
                // value to optimize out any unnecessary setting
                // of properties
                BOOL        bSame     = FALSE;
                IHXBuffer* pCurValue = NULL;
                HX_RESULT rv = pValues->GetPropertyCString(pszName, pCurValue);
                if (SUCCEEDED(rv))
                {
                    const char* pszCurValue = (const char*) pCurValue->GetBuffer();
                    if (pszCurValue)
                    {
                        if (!strcmp(pszCurValue, pszValue))
                        {
                            bSame = TRUE;
                        }
                    }
                }
                // Release the current value buffer
                HX_RELEASE(pCurValue);
                // Is the current value different from the 
                // value we're trying to set?
                if ((SUCCEEDED(rv) && !bSame) || FAILED(rv))
                {
                    // Set the flag
                    rbSetNewValue = TRUE;
                    // Set the CString property
                    setProperty(pValues, pszName, pszValue);
                }
            }
            HX_RELEASE(pValues);
        }
        HX_RELEASE(pRenderer);
    }

    return retVal;
}

void CSmilDocumentRenderer::addRemoveMediaMarkerSink(BOOL bAdd)
{
    if (m_pContext)
    {
        // QI for IHXMediaMarkerManager
        IHXMediaMarkerManager* pMediaMarkerManager = NULL;
        m_pContext->QueryInterface(IID_IHXMediaMarkerManager,
                                   (void**) &pMediaMarkerManager);
        if (pMediaMarkerManager)
        {
            // Get our own IHXMediaMarkerSink interface
            IHXMediaMarkerSink* pSink = NULL;
            QueryInterface(IID_IHXMediaMarkerSink, (void**) &pSink);
            if (pSink)
            {
                if (bAdd)
                {
                    // Add ourselves as a sink
                    pMediaMarkerManager->AddMediaMarkerSink(pSink);
                }
                else
                {
                    // Remove ourselves as a sink
                    pMediaMarkerManager->RemoveMediaMarkerSink(pSink);
                }
            }
            HX_RELEASE(pSink);
        }
        HX_RELEASE(pMediaMarkerManager);
    }
}

void CSmilDocumentRenderer::addRemoveEventSink(BOOL bAdd)
{
    if (m_pContext)
    {
        // QI for IHXEventManager
        IHXEventManager* pEventManager = NULL;
        m_pContext->QueryInterface(IID_IHXEventManager,
                                   (void**) &pEventManager);
        if (pEventManager)
        {
            // Get our own IHXEventSink interface
            IHXEventSink* pSink = NULL;
            QueryInterface(IID_IHXEventSink, (void**) &pSink);
            if (pSink)
            {
                if (bAdd)
                {
                    // Add ourselves as a sink
                    pEventManager->AddEventSink(pSink);
                    // Set the flag
                    m_bEventSinkWasSetup = TRUE;
                }
                else
                {
                    // Remove ourselves as a sink
                    pEventManager->RemoveEventSink(pSink);
                    // Clera the flag
                    m_bEventSinkWasSetup = FALSE;
                }
            }
            HX_RELEASE(pSink);
        }
        HX_RELEASE(pEventManager);
    }
}

void CSmilDocumentRenderer::addRemoveErrorSink(BOOL bAdd)
{
    if (m_pContext)
    {
        // QI for IHXErrorSinkControl
        IHXErrorSinkControl* pErrorSinkControl = NULL;
        m_pContext->QueryInterface(IID_IHXErrorSinkControl,
                                   (void**) &pErrorSinkControl);
        if (pErrorSinkControl)
        {
            // Get our own IHXEventSink interface
            IHXErrorSink* pSink = NULL;
            QueryInterface(IID_IHXErrorSink, (void**) &pSink);
            if (pSink)
            {
                if (bAdd)
                {
                    // Add ourselves as a sink
                    pErrorSinkControl->AddErrorSink(pSink,
                                                    HXLOG_EMERG,
                                                    HXLOG_DEBUG);
                }
                else
                {
                    // Remove ourselves as a sink
                    pErrorSinkControl->RemoveErrorSink(pSink);
                }
            }
            HX_RELEASE(pSink);
        }
        HX_RELEASE(pErrorSinkControl);
    }
}

void CSmilDocumentRenderer::addEventSinkFilterRule(const char*    pszURL,
                                                   const char*    pszFragment,
                                                   const char*    pszEventName,
                                                   IHXEventSink* pSink)
{
    if (m_pContext && m_pParent &&
        (pszURL || pszFragment || pszEventName))
    {
        // QI for IHXEventManager
        IHXEventManager* pEventManager = NULL;
        m_pContext->QueryInterface(IID_IHXEventManager,
                                   (void**) &pEventManager);
        if (pEventManager)
        {
            // If we have a sink passed in, use it. If
            // not, then get our own event sink interface
            BOOL bOurSink = FALSE;
            if (!pSink)
            {
                // Set the flag so that we'll know this is 
                // our interface (and we need to release it
                // at the bottom of this method)
                bOurSink = TRUE;
                // Get our own IHXEventSink interface
                QueryInterface(IID_IHXEventSink, (void**) &pSink);
            }
            if (pSink)
            {
                // Create an IHXValues
                IHXCommonClassFactory* pFact = m_pParent->getFactory();
                if (pFact)
                {
                    IHXValues* pRule = NULL;
                    pFact->CreateInstance(CLSID_IHXValues, (void**) &pRule);
                    if (pRule)
                    {
                        // Set URL string
                        if (pszURL)
                        {
                            setProperty(pRule, FILTER_RULE_KEY_URL,
                                        pszURL);
                        }
                        // Set fragment string
                        if (pszFragment)
                        {
                            setProperty(pRule, FILTER_RULE_KEY_FRAGMENT,
                                        pszFragment);
                        }
                        // Set eventName string
                        if (pszEventName)
                        {
                            setProperty(pRule, FILTER_RULE_KEY_EVENTNAME,
                                        pszEventName);
                        }
                        // Add the rule
                        pEventManager->AddEventSinkFilterRule(pSink, pRule);
                    }
                    HX_RELEASE(pRule);
                }
            }
            // If this was our sink interface, then
            // we need to release it
            if (bOurSink)
            {
                HX_RELEASE(pSink);
            }
        }
        HX_RELEASE(pEventManager);
    }
}

HX_RESULT CSmilDocumentRenderer::getEventSink(const char*         pszID,
                                              REF(IHXEventSink*) rpEventSink)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pszID)
    {
        // Get the IHXRenderer interface
        IHXRenderer* pRenderer = NULL;
        retVal = getRenderer(pszID, pRenderer);
        if (SUCCEEDED(retVal))
        {
            // QI for IHXEventSink
            HX_RELEASE(rpEventSink);
            retVal = pRenderer->QueryInterface(IID_IHXEventSink,
                                               (void**) &rpEventSink);
        }
        HX_RELEASE(pRenderer);
    }

    return retVal;
}

HX_RESULT CSmilDocumentRenderer::addRemoveEventSink(const char* pszID,
                                                    BOOL        bAdd)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pszID && m_pContext)
    {
        // See if we can get the IHXEventSink interface
        IHXEventSink* pSink = NULL;
        retVal = getEventSink(pszID, pSink);
        if (SUCCEEDED(retVal))
        {
            // Check if the event sink is already setup
            BOOL         bSinkIsSetup = FALSE;
            LISTPOSITION pos          = NULL;
            if (m_pEventSinkList)
            {
                pos = m_pEventSinkList->GetHeadPosition();
                while (pos)
                {
                    IHXEventSink* pListSink =
                        (IHXEventSink*) m_pEventSinkList->GetAt(pos);
                    if (pListSink && pListSink == pSink)
                    {
                        bSinkIsSetup = TRUE;
                        break;
                    }
                    m_pEventSinkList->GetNext(pos);
                }
            }
            // We only have work to do if:
            // a) we are adding and it's not already setup; OR
            // b) we are removing and it's already setup.
            if (( bAdd && !bSinkIsSetup) ||
                (!bAdd &&  bSinkIsSetup))
            {
                // Get the IHXEventManager interface
                IHXEventManager* pMgr = NULL;
                retVal = m_pContext->QueryInterface(IID_IHXEventManager,
                                                    (void**) &pMgr);
                if (SUCCEEDED(retVal))
                {
                    // Are we adding or removing?
                    if (bAdd)
                    {
                        // Add the event sink to the event manager
                        retVal = pMgr->AddEventSink(pSink);
                        if (SUCCEEDED(retVal))
                        {
                            // Do we have a event sink list yet?
                            if (!m_pEventSinkList)
                            {
                                m_pEventSinkList = new CHXSimpleList();
                            }
                            if (m_pEventSinkList)
                            {
                                // AddRef the event sink before it goes on the list
                                pSink->AddRef();
                                // Add it to the list
                                m_pEventSinkList->AddTail((void*) pSink);
                            }
                            else
                            {
                                retVal = HXR_FAIL;
                            }
                        }
                    }
                    else
                    {
                        // Remove the event sink from the event manager
                        retVal = pMgr->RemoveEventSink(pSink);
                        if (SUCCEEDED(retVal))
                        {
                            // Remove it from the list
                            m_pEventSinkList->RemoveAt(pos);
                            // Release the list's ref on the sink
                            pSink->Release();
                        }
                    }
                }
                HX_RELEASE(pMgr);
            }
        }
        HX_RELEASE(pSink);
    }

    return retVal;
}

void CSmilDocumentRenderer::removeAllEventSinks()
{
    if (m_pEventSinkList && m_pContext)
    {
        IHXEventManager* pMgr = NULL;
        m_pContext->QueryInterface(IID_IHXEventManager, (void**) &pMgr);
        if (pMgr)
        {
            LISTPOSITION pos = m_pEventSinkList->GetHeadPosition();
            while (pos)
            {
                // Get the sink
                IHXEventSink* pSink =
                    (IHXEventSink*) m_pEventSinkList->GetNext(pos);
                // Remove the event sink from the event manager
                pMgr->RemoveEventSink(pSink);
                // Release the list's ref on the sink
                HX_RELEASE(pSink);
            }
            // Remove all entries from the list
            m_pEventSinkList->RemoveAll();
        }
        HX_RELEASE(pMgr);
    }
}

HX_RESULT CSmilDocumentRenderer::setupEventPipe(const char* pszSrcID,
                                                const char* pszDstID,
                                                const char* pszEventName)
{
    HX_RESULT retVal = HXR_FAIL;

    if (pszSrcID && pszDstID)
    {
        // We have to setup the destination renderer as a event sink.
        // If it's not setup yet OR the renderer doesn't support
        // IHXEventSink, then this call will fail.
        retVal = addRemoveEventSink(pszDstID, TRUE);
        if (SUCCEEDED(retVal))
        {
            // First get the playToAssoc
            SMILPlayToAssoc* pAssoc = getPlayToAssocByMedia(pszSrcID);
            if (pAssoc)
            {
                // Get the src media URL from the playToAssoc
                const char* pszURL = (const char*) pAssoc->m_URL;
                // Get the destination event sink
                IHXEventSink* pSink = NULL;
                retVal = getEventSink(pszDstID, pSink);
                if (SUCCEEDED(retVal))
                {
                    // Now we need to add a filter rule that will
                    // let this sink get events from the src media
                    addEventSinkFilterRule(pszURL, NULL, pszEventName, pSink);
                }
                HX_RELEASE(pSink);
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
    }

    return retVal;
}

void
CSmilDocumentRenderer::handlePendingScheduling(void)
{
    // /We need to call handlePendingScheduling in case
    // this endEvent is from the last-active track in which
    // case the core will not give us any more time syncs
    // so we can't do the following in onTimeSync():
    BOOL bSomeElementScheduleWasChanged = FALSE;
    HX_RESULT tmprslt =
          m_pSmilParser->handlePendingScheduling(
          m_ulCurrentTime,
          m_uCurrentGroupIndex,
          bSomeElementScheduleWasChanged,
          // /We don't need to get list of paused elements:
          NULL, NULL);
    if (SUCCEEDED(tmprslt) && bSomeElementScheduleWasChanged)
    {
      handleElements();
    }

    return;
}


HX_RESULT
CSmilDocumentRenderer::AdjustSoundLevelsOfAllCurrentTracks(
      UINT16 uCurGroupIndex,
      // /Adjust each track's sound level by this amount, so if current
      // level of track is 50% and this adjustment_pct is 60%, then new
      // sound level for that track would be 30% (= 60% of 50%):
      UINT32 ulSoundLevelAdjustment_pct,
      // /In case track is being restarted by link-region-target HURL, don't
      // mess with its soundLevel as that will be set in handleSource():
      CSmilSource* pSmilSourceDontAdjustMe)
{
    HX_RESULT pnrslt = HXR_OK;

    // /Get all currently-playing tracks and adjust their soundLevel values:
    if(m_pPlayToAssocList)
    {
      CHXSimpleList::Iterator i = m_pPlayToAssocList->Begin();
      for (; i!=m_pPlayToAssocList->End(); ++i)
      {
          SMILPlayToAssoc* pPlayToAssoc = (SMILPlayToAssoc*)(*i);
          // /XXXEH- do this for already-played-and-stopped ones in this
          // group?  I think so, in case there's a seek backwards:
          if (pPlayToAssoc->m_uGroupIndex >= uCurGroupIndex)
          {
            if (pSmilSourceDontAdjustMe  &&  pPlayToAssoc->m_id ==
                  pSmilSourceDontAdjustMe->m_pNode->m_id)
            {
                continue;
            }

            IHXTrack* pTrack = NULL;
            HX_RESULT rtvlgetTrack = getTrack(pPlayToAssoc->m_uGroupIndex,
                  pPlayToAssoc->m_uTrackIndex, pTrack);
            if (SUCCEEDED(rtvlgetTrack))
            {
                UINT16 usPriorSoundLevel_pct = pTrack->GetSoundLevel();
                UINT16 usNewSoundLevel_pct = (usPriorSoundLevel_pct *
                      (UINT16)ulSoundLevelAdjustment_pct) / 100;
                if (usNewSoundLevel_pct > (UINT16)MAX_ALLOWED_SOUNDLEVEL)
                {
                  usNewSoundLevel_pct = (UINT16)MAX_ALLOWED_SOUNDLEVEL;
                }
                pTrack->SetSoundLevel(usNewSoundLevel_pct);
            }
            HX_RELEASE(pTrack);
          }
      }
    }

    // /To handle all not-yet-added tracks, set this for adjusting their
    // soundLevel values in ::handleSource():
// /XXXEH- finish this!
 
    return pnrslt;
}

void CSmilDocumentRenderer::SendHyperlinkHints()
{
    if(m_pParent)
    {
        IHXPlayer* pPlayer = m_pParent->getPlayer();
        if(pPlayer && m_pSmilParser)
        {
            // Get the <smil> node
            SMILNode* pTopNode = m_pSmilParser->findFirstNode(SMILSmil);
            if (pTopNode)
            {
                // Now recurse down the tree
                HintHyperlinkNodeRecurse(pTopNode);
            }
        }
    }
}

void CSmilDocumentRenderer::HintHyperlinkNodeRecurse(SMILNode* pNode)
{
    if (pNode)
    {
        // Check if it's a hyperlinking node
        if (pNode->m_tag == SMILAAnchor ||
            pNode->m_tag == SMILArea    ||
            pNode->m_tag == SMILAnchor)
        {
            // Yep, this is a hyperlinking node, so hint it
            //
            // Get the element
            CSmilAAnchorElement* pAnchor = (CSmilAAnchorElement*) pNode->m_pElement;
            if (pAnchor)
            {
                // Hint this hyperlink
                HintHyperlinkNode(pAnchor);
            }
        }
        // Now recursively check our children
        if (pNode->m_pNodeList)
        {
            LISTPOSITION pos = pNode->m_pNodeList->GetHeadPosition();
            while (pos)
            {
                SMILNode* pChildNode = (SMILNode*) pNode->m_pNodeList->GetNext(pos);
                HintHyperlinkNodeRecurse(pChildNode);
            }
        }

    }
}

void CSmilDocumentRenderer::HintHyperlinkNode(CSmilAAnchorElement* pAnchor)
{
    // Only hint if we actually have an href
    if(pAnchor && pAnchor->m_href.GetLength() > 0 && m_pParent)
    {
        // Get the IHXPlayer
        IHXPlayer* pPlayer = m_pParent->getPlayer();
        if(pPlayer)
        {
            // Intialize target and sendTo
            const char* pTarget = "_player";
            if (pAnchor->m_bExternal)
            {
                if (pAnchor->m_target.GetLength() > 0)
                {
                    pTarget = (const char*) pAnchor->m_target;
                }
                else
                {
                    pTarget = "_browser";
                }
            }

            const char* pSendTo = NULL;
            if (pAnchor->m_sendTo.GetLength() > 0)
            {
                pSendTo = (const char*) pAnchor->m_sendTo;
            }

            BOOL bIsOnLoadActuation = FALSE;
            if (!strcmp(pAnchor->m_actuate, "onLoad"))
            {
                bIsOnLoadActuation = TRUE;
            }

            BOOL bNeedToTargetANamedPlayerInstance = FALSE;
            if (pAnchor->m_target.GetLength() > 0)
            {
                if (!pAnchor->m_bTargetIsARegion  &&  !pAnchor->m_bExternal)
                {
                    bNeedToTargetANamedPlayerInstance = TRUE;
                }
            }

            if(pAnchor->m_href[0] != '#')
            {
                IHXHyperNavigate* pHyper = 0;

                SMILLinkPlaystate sourcePlaystate = SMILLinkPlaystatePause;
                if (("pause"   == pAnchor->m_show  ||
                     "replace" == pAnchor->m_show)  &&
                    !pAnchor->m_bExternal)
                {
                    sourcePlaystate = SMILLinkPlaystatePause;
                }
                else
                {
                    sourcePlaystate = pAnchor->m_sourcePlaystate;
                }

                BOOL bIsOldSMIL1ShowEqualsPause = FALSE;
                if ("pause" == pAnchor->m_show)
                {
                    bIsOldSMIL1ShowEqualsPause = TRUE;
                }

                CHXString urlString;
                convertURL((const char*) m_pParent->getURLRoot(),
                           (const char*) m_pParent->getURLPrefix(),
                           (const char*) pAnchor->m_href,
                           urlString);

                BOOL bIsMailtoURL = !(strncmp("mailto:", urlString, 7));
                if (bIsMailtoURL)
                {
                    pTarget = "_browser";
                }

                BOOL bIsCommandOpenWindowURL = FALSE;

                // /Now, if show="new", we need to create a new player
                // and open the URL in it:
                // This is also true of the old SMIL 1.0 show="pause":
                BOOL bIsCommandURL = !(strncmp("command:", urlString, 8));
                if (bIsCommandURL)
                {
                    const char* pCommandAction = ((const char*)urlString)+8;
                    // /Look for "command:[s*]openwindow" where [s*] is 0
                    // or more white-space characters:
                    while (isspace(*pCommandAction))
                    {
                        pCommandAction++;
                    }
                    if (*pCommandAction)
                    {
                        bIsCommandOpenWindowURL = !(strnicmp("openWindow",
                            pCommandAction, 10));
                    }
                }
                if (!bIsCommandURL  &&
                    (!strncmp("new", pAnchor->m_show,3) ||
                     bIsOldSMIL1ShowEqualsPause         ||
                     bNeedToTargetANamedPlayerInstance))
                {
                    if (pAnchor->m_bExternal)
                    {
                        if(HXR_OK == pPlayer->QueryInterface(
                                IID_IHXHyperNavigate, (void**)&pHyper))
                        {
                            // XXXMEH - use updated method which checks
                            // for <rn:param> children
                            GoToURLWithParamCheck(urlString, pTarget, pSendTo,
                                                  pHyper, pAnchor, m_pContext,
                                                  !bIsOnLoadActuation,
                                                  TRUE); // TRUE means we're hinting
                            pHyper->Release();
                        }
                    }
                    else
                    {
                        // /Have the TLC create a new player for us via
                        // "command:openWindow(windowName,URL,behavior)":
                        CHXString pOpenWindowURL = "command:openWindow(";
                        // /Now, add the "target", if specified:
                        if (pAnchor->m_target.GetLength() > 0)
                        {
                            pOpenWindowURL += pAnchor->m_target;
                        }
                        // /Otherwise, we need to make up a unique name:
                        else
                        {
                            pOpenWindowURL += pAnchor->m_pNode->m_id;
                            char* pTmp = new char[64];
                            ULONG32 ulCurTime = HX_GET_TICKCOUNT();
                            itoa(ulCurTime, pTmp, 16);
                            pOpenWindowURL += pTmp;
                            delete [] pTmp;
                        }
                        pOpenWindowURL += ",";
                        pOpenWindowURL += urlString;
                        // /Set the source (current) player's behavior:
                        switch (sourcePlaystate)
                        {
                            case SMILLinkPlaystatePlay:
                            {
                                pOpenWindowURL += ",behavior=continue";
                            }
                            break;

                            case SMILLinkPlaystatePause:
                            {
                                pOpenWindowURL += ",behavior=pause";
                            }
                            break;

                            case SMILLinkPlaystateStop:
                            {
                                pOpenWindowURL += ",behavior=stop";
                            }
                            break;

                            default:
                            {
                                HX_ASSERT(0&&"bad sourcePlaystate");
                            }
                            break;
                        }
                        pOpenWindowURL += ")";
                        if(HXR_OK == pPlayer->QueryInterface(
                                IID_IHXHyperNavigate, (void**)&pHyper))
                        {
                            GoToURLWithParamCheck(pOpenWindowURL, pTarget, 
                                                  NULL, pHyper, pAnchor,
                                                  m_pContext, !bIsOnLoadActuation,
                                                  TRUE); // TRUE means we're hinting
                            pHyper->Release();
                        }
                    }
                }
                else if (pAnchor->m_bExternal  ||
                         bIsCommandURL         ||
                         bIsMailtoURL          ||
                         bIsCommandOpenWindowURL)
                {
                    if(HXR_OK == pPlayer->QueryInterface(
                            IID_IHXHyperNavigate, (void**)&pHyper))
                    {
                        GoToURLWithParamCheck(urlString, pTarget, pSendTo,
                                              pHyper, pAnchor, m_pContext,
                                              !bIsOnLoadActuation,
                                              TRUE); // TRUE means we're hinting
                        HX_RELEASE(pHyper);
                    }
                }
            }
        }
    }
}


void
CSmilDocumentRenderer::CleanUpSiteInfoContainers()
{    
    HX_DELETE(m_pSiteInfoByRendererMap);
    // /For PR 116482: this needs to get deleted after
    // m_pSiteInfoByRendererMap, above.  In case our close() gets called
    // before our SiteUserDetachSite() gets called, we still might have these
    // objects to delete:
    if (m_pSiteInfoList)
    {
        CHXSimpleList::Iterator i = m_pSiteInfoList->Begin();
        for(; i != m_pSiteInfoList->End(); ++i)
        {
            SMILSiteInfo* pSiteInfo = (SMILSiteInfo*)(*i);
            delete pSiteInfo;
        }
    }
    HX_DELETE(m_pSiteInfoList);
}



Generated by  Doxygen 1.6.0   Back to index