#include "priv.h"
#include "resource.h"
#include "hlframe.h"
#include "bindcb.h"
#include "winlist.h"
#include "iface.h"
#include "shdocfl.h"
#include <optary.h>
#include <mluisupp.h>

#define DM_SHELLEXECOBJECT    0x80000000


// flags for SHDVID_DOCFAMILYCHARSET communication
#define DFC_URLCHARSET  1

#ifdef FEATURE_PICS
#include "dochost.h"    /* for IID_IsPicsBrowser */
#endif

#ifdef DEBUG
extern DWORD g_dwPerf;
#endif

#define DM_HLINKTRACE DM_TRACE

#define DM_WEBCHECKDRT          0
#define JMPMSG(psz, psz2)    TraceMsg(0, "shlf TR-CDOV::%s %s", psz, psz2)
#define JMPMSG2(psz, x)        TraceMsg(0, "shlf TR-CDOV::%s %x", psz, x)
#define DOFMSG(psz)        TraceMsg(0, "shlf TR-DOF::%s", psz)
#define DOFMSG2(psz, x)        TraceMsg(0, "shlf TR-DOF::%s %x", psz, x)
#define URLMSG(psz)        TraceMsg(0, "shlf TR-DOF::%s", psz)
#define URLMSG2(psz, x)        TraceMsg(0, "shlf TR-DOF::%s %x", psz, x)
#define URLMSG3(psz, x, y)    TraceMsg(0, "shlf TR-DOF::%s %x %x", psz, x, y)
#define BSCMSG(psz, i, j)    TraceMsg(0, "shlf TR-BSC::%s %x %x", psz, i, j)
#define BSCMSG3(psz, i, j, k)    TraceMsg(0, "shlf TR-BSC::%s %x %x %x", psz, i, j, k)
#define BSCMSGS(psz, sz)    TraceMsg(0, "shlf TR-BSC::%s %s", psz, sz)
#define OIPSMSG(psz)        TraceMsg(0, "shlf TR-OIPS::%s", psz)
#define OIPSMSG3(psz, sz, p)    TraceMsg(0, "shlf TR-OIPS::%s %s,%x", psz, sz,p)
#define REFMSG0(psz)        TraceMsg(0, "shlf TR-CDOV::%s", psz)
#define REFMSG(psz, cRef)    TraceMsg(0, "shlf TR-CDOV::%s new _cRef==%d", psz, cRef)
#define REFMSG2(psz, if, cRef)    TraceMsg(0, "shlf TR-CDOV::%s(%s) new _cRef==%d", psz, if, cRef)
#define VIEWMSG(psz)        TraceMsg(0, "shlf TR CDOV::%s", psz)
#define VIEWMSG2(psz,xx)    TraceMsg(0, "shlf TR CDOV::%s %x", psz,xx)
#define CACHEMSG(psz, d)        TraceMsg(0, "shlf TR CDocObjectCtx::%s %d", psz, d)
#define HFRMMSG(psz)        TraceMsg(TF_SHDNAVIGATE, "shlf HFRM::%s", psz)
#define HFRMMSG2(psz, x, y)    TraceMsg(TF_SHDNAVIGATE, "shlf HFRM::%s %x %x", psz, x, y)
#define MNKMSG(psz, psz2)    TraceMsg(0, "shlf MNK::%s (%s)", psz, psz2)
#define SERVMSG(psz, x, y)    TraceMsg(0, "shlf SERV::%s %x %x", psz, x, y)

#define KEY_BINDCONTEXTPARAM            _T("BIND_CONTEXT_PARAM")
#define SZ_DWNBINDINFO_OBJECTPARAM      _T("__DWNBINDINFO")

BOOL g_fHlinkDLLLoaded = FALSE;        // must be per-process

STDAPI HlinkFrameNavigate(DWORD grfHLNF, IBindCtx *pbc,
                           IBindStatusCallback *pibsc,
                           IHlink* pihlNavigate,
                           IHlinkBrowseContext *pihlbc);
STDAPI HlinkFrameNavigateNHL(DWORD grfHLNF, IBindCtx *pbc,
                           IBindStatusCallback *pibsc,
                           LPCWSTR pszTargetFrame,
                           LPCWSTR pszUrl,
                           LPCWSTR pszLocation);

// IHlinkFrame members
HRESULT CIEFrameAuto::SetBrowseContext(IHlinkBrowseContext *pihlbc)
{
    if (pihlbc)
        pihlbc->AddRef();

    if (_phlbc)
    {
        if (_dwRegHLBC) 
        {
            _phlbc->Revoke(_dwRegHLBC);
            _dwRegHLBC = 0;
        }
        _phlbc->Release();
    }

    _phlbc = pihlbc;

    return NOERROR;
}

HRESULT CIEFrameAuto::GetBrowseContext(IHlinkBrowseContext **ppihlbc)
{
    TraceMsg(0, "shlf TR ::GetBrowseContext called");
    
    *ppihlbc = _phlbc;

    if (_phlbc) 
    {
        _phlbc->AddRef();
        return S_OK;
    }
    
    return E_FAIL;
}

void CIEFrameAuto::_SetPendingNavigateContext(IBindCtx *pbc, IBindStatusCallback *pibsc)
{
    if (_pbscPending) 
    {
        _pbscPending->Release();
        _pbscPending = NULL;
    }

    if (_pbcPending) 
    {
        _pbcPending->Release();
        _pbcPending = NULL;
    }

    if (pibsc) 
    {
        _pbscPending = pibsc;
        _pbscPending->AddRef();
    }

    if (pbc) 
    {
        // as long as we are cacheing the pending BindCtx, if it specifies
        // a shortcut URL we need to cache that too. (IE:98431)
        IUnknown *          pUnk = NULL;
        IHtmlLoadOptions *  pHtmlLoadOptions  = NULL;

        _pbcPending = pbc;
        _pbcPending->AddRef();

        pbc->GetObjectParam(_T("__HTMLLOADOPTIONS"), &pUnk);
        if (pUnk)
        {
            pUnk->QueryInterface(IID_IHtmlLoadOptions, (void **) &pHtmlLoadOptions);

            if (pHtmlLoadOptions)
            {
                TCHAR    achCacheFile[MAX_PATH+1];
                ULONG    cchCacheFile = ARRAYSIZE(achCacheFile)-1;

                memset(&achCacheFile, 0, (cchCacheFile+1)*sizeof(TCHAR) );
                
                // now determine if this is a shortcut-initiated load
                pHtmlLoadOptions->QueryOption(HTMLLOADOPTION_INETSHORTCUTPATH,
                                                       &achCacheFile,
                                                       &cchCacheFile);

                if (_pwszShortcutPathPending)
                    LocalFree(_pwszShortcutPathPending);

                _pwszShortcutPathPending = StrDup(achCacheFile);

                pHtmlLoadOptions->Release();
            }

            pUnk->Release();
        }

   }
}

//
//  NavigateContext is a set of parameters passed from one CIEFrameAuto
// to another.
//
void CIEFrameAuto::_ActivatePendingNavigateContext()
{
    if (_pbsc) 
    {
        _pbsc->Release();
        _pbsc = NULL;
    }

    if (_pbc) 
    {
        _pbc->Release();
        _pbc = NULL;
    }

    if (_pwszShortcutPath)
    {
        LocalFree(_pwszShortcutPath);
        _pwszShortcutPath = NULL;
    }

    if (_pbscPending) 
    {
        _pbsc = _pbscPending;
        _pbscPending = NULL;
    }

    if (_pbcPending) 
    {
        _pbc = _pbcPending;
        _pbcPending = NULL;
    }

    if (_pwszShortcutPathPending) 
    {
        _pwszShortcutPath = _pwszShortcutPathPending;
        _pwszShortcutPathPending = NULL;
    }
        
}

//  Called to guarantee a newly created HLinkFrame's window is
//  visible after the navigate.
HRESULT ShowHlinkFrameWindow(IUnknown *pUnkTargetHlinkFrame)
{
    IWebBrowserApp* pdie;
    HRESULT hres = pUnkTargetHlinkFrame->QueryInterface(IID_PPV_ARG(IWebBrowserApp, &pdie));
    if (SUCCEEDED(hres)) 
    {
        pdie->put_Visible(TRUE);
        pdie->Release();
    }
    return hres;
}

HRESULT CIEFrameAuto::_NavigateMagnum(DWORD grfHLNF, IBindCtx *pbc, IBindStatusCallback *pibsc, LPCWSTR pszTargetName, LPCWSTR pszUrl, LPCWSTR pszLocation, IHlink *pihlNavigate, IMoniker *pmkTarget)
{
    HRESULT hres = NOERROR;
    HFRMMSG2("Navigate called", grfHLNF, pihlNavigate);
    BOOL fNavigateForReal = pszUrl || (pihlNavigate && (pihlNavigate != (IHlink*)-1));

    _fSuppressHistory = _psbProxy != _psb;  // no history for search band, etc
    _fSuppressSelect = _psbProxy != _psb;   // no need to record select pidl
    if (grfHLNF != (DWORD)-1)
    {
        if (SHHLNF_WRITENOHISTORY & grfHLNF)
        {
            _fSuppressHistory = TRUE;
        }
        if (SHHLNF_NOAUTOSELECT & grfHLNF)
        {
            _fSuppressSelect = TRUE;
        }
    }

    if (pbc == NULL && pibsc == NULL && pihlNavigate == NULL && pszUrl == NULL) 
    {
        //
        //  This is a private interface so that mshtml can do navigation
        // if it is hosted by the shell.  When IHlinkBrowseContext is implemented
        // in the shell this special code can be removed and the associated
        // code in mshtml that calls Navigate with these special parameters
        // can be removed so that it just goes through the
        // IHlinkBrowseContext->SetCurrentHlink interface.
        //
        //  We also use this private mechanism to release the navigation
        // context with grfHLNF==0.
        // 

        switch (grfHLNF&~(SHHLNF_WRITENOHISTORY|SHHLNF_NOAUTOSELECT)) 
        {
        case HLNF_NAVIGATINGBACK:
            hres = _BrowseObject(PIDL_LOCALHISTORY, SBSP_SAMEBROWSER|SBSP_NAVIGATEBACK);
            break;

        case HLNF_NAVIGATINGFORWARD:
            hres = _BrowseObject(PIDL_LOCALHISTORY, SBSP_SAMEBROWSER|SBSP_NAVIGATEFORWARD);
            break;

        case 0:
            _ActivatePendingNavigateContext();
            break;

        default:
            hres = E_INVALIDARG;
            break;
        }

        return hres;
    }

#ifdef FEATURE_PICS
    /* As part of checking ratings, the PICS code will silently download the
     * root document of a site to look for rating labels in it.  If that's a
     * frameset page, Trident will create OCXs for the subframes and try to
     * navigate them, which will invoke ratings checks for them and cause
     * infinite recursion.  So here we check to see if our top-level browser
     * is really this PICS download, and if it is, we don't do any navigation.
     */
    IUnknown *punkPics;
    if (SUCCEEDED(QueryService(SID_STopLevelBrowser, IID_IsPicsBrowser, (void **)&punkPics)))
    {
        punkPics->Release();
        return S_OK;
    }
#endif

    //
    // If we've got this call while we are busy (EnableModeless is FALSE),
    // we should just bail here (instead of doing somthing and let _JumpTo
    // call fail. 
    //
    // This can happen if someone has a window.location="foobar.htm" in their unload
    // event handler.  
    if (fNavigateForReal && !(grfHLNF & HLNF_OPENINNEWWINDOW)) 
    {
        // If _pbs is NULL, it is bad news; we can't navigate.
        // An allowable reason for this condition is that someone has called CIEFrameAuto::Quit()
        // and we are in the process of shutting down.
        //
        if (_pbs == NULL)
        {
            if (_fQuitInProgress)
            {
                TraceMsg(TF_WARNING, "CIEFrameAuto::_NavigateMagnum quitting due to browser closing.");
                return S_OK;
            }
            TraceMsg(TF_WARNING, "CIEFrameAuto::_NavigateMagnum _pbs is NULL, but we are not shutting down.");
            return E_FAIL;
        }

        // If we have a _pbs but the browser says that it can't navigate now, then return S_FALSE.
        //
        else if (_pbs->CanNavigateNow() != S_OK) 
        {
            TraceMsg(TF_WARNING, "CIEFrameAuto::Navigate CanNavigateNow returns non S_OK, bail out.");
            return S_FALSE;
        }
    }

    //
    // This Navigate method is not re-entrant (because of _SetPendingNavigateContext)
    //
    if (_fBusy) 
    {
        TraceMsg(DM_WARNING, "CIEA::Navigate re-entered. Returning E_FAIL");
        return E_FAIL;
    }
    _fBusy = TRUE;

    //
    // HACK: To let Webcheck DRT go.
    //
    if (fNavigateForReal  && !(grfHLNF & HLNF_OPENINNEWWINDOW)) 
    {
        TraceMsg(DM_WEBCHECKDRT, "CIFA::Navigate calling _CancelPendingNavigation");
        VARIANT var = { 0 };
        var.vt = VT_I4;
        var.lVal = TRUE;    // synchronous

        _CancelPendingNavigation(&var);
    }

    if (pszUrl && SHRestricted2(REST_NOFILEURL, NULL, 0) && PathIsFilePath(pszUrl))
    {
        TCHAR szPath[MAX_URL_STRING];
        SHUnicodeToTChar(pszUrl, szPath, ARRAYSIZE(szPath));
        MLShellMessageBox(NULL, MAKEINTRESOURCE(IDS_SHURL_ERR_PARSE_NOTALLOWED),
                        szPath, MB_OK | MB_ICONERROR, szPath);
    
        _fBusy = FALSE;
        return E_ACCESSDENIED;
    }


    _SetPendingNavigateContext(pbc, pibsc);

#ifdef DEBUG
    g_dwPerf = GetCurrentTime();

#endif

    if (pihlNavigate == (IHlink*)-1) 
    {
        //
        // HACK: -1 means "release the navigation state".
        // CDocObjectHost::_CancelPendingNavigation is the only caller.
        // It Exec's SBCMDID_CANCELNAVIGATION which will asynchronously
        // cancel the pending navigation. Therefore, we no longer need
        // to call _CancelPendingNavigation here. (SatoNa)
        // 
        // _CancelPendingNavigation();
    }
    else if (pihlNavigate || pszUrl) 
    {
        hres = S_OK;

        if (SUCCEEDED(hres))
        {
            if ((grfHLNF & HLNF_EXTERNALNAVIGATE) && (grfHLNF & HLNF_NAVIGATINGBACK))
                GoBack();
            else if ((grfHLNF & HLNF_EXTERNALNAVIGATE) && (grfHLNF & HLNF_NAVIGATINGFORWARD))
                GoForward();
            else 
            {
                hres = _JumpTo(pbc,(LPWSTR) pszLocation, grfHLNF, pibsc, pihlNavigate, pszTargetName, pszUrl);
                if (FAILED(hres)) 
                {
                    TraceMsg(DM_ERROR, "IEAuto::Navigate _JumpTo failed %x", hres);
                }
            }
            if (pihlNavigate)
            {
                //
                //  Hopefully, we'll come up with a clean solution to
                //  solve this problem nicely. I made a proposal to NatBro/SriniK
                //  that CreateHlink will CoCreateInstance IHlink so that OLE
                //  LoadLibrary it and maintains it as an InProc server. (SatoNa)
                //
                // HACK: If we AddRef to IHlink, we need to make it sure that
                //  HLINK.DLL is stay loaded even though the DocObject InProc
                //  server (that implicitly links to HLINK.DLL) is unloaded.
                //
                if (!g_fHlinkDLLLoaded) 
                {
                    LoadLibrary(TEXT("hlink.dll"));
                    g_fHlinkDLLLoaded = TRUE;
                }
            }
        }
        else
        {
            TraceMsg(DM_ERROR, "CIEFA::Nav phl->GetMonRef failed %x", hres);
        }
    }

    _fBusy = FALSE;

    HFRMMSG2("Navigate returning", hres, 0);
    
    if (SUCCEEDED(hres) && (pihlNavigate != (IHlink*)-1)) 
    {
        if (grfHLNF & HLNF_EXTERNALNAVIGATE) 
        {
            HWND hwndFrame;
            _psb->GetWindow(&hwndFrame);
            
            if (_phlbc) 
            {
                // if we have a browse context, then we're navigating from it and
                // we should size our window to match it.
                HLBWINFO hlbwi;
                
                hlbwi.cbSize = SIZEOF(hlbwi);
                if (SUCCEEDED(_phlbc->GetBrowseWindowInfo(&hlbwi)) &&
                    (hlbwi.grfHLBWIF & HLBWIF_HASFRAMEWNDINFO)) 
                {
                    WINDOWPLACEMENT wp;
                    
                    wp.length = sizeof(WINDOWPLACEMENT);
                    GetWindowPlacement(hwndFrame, &wp);
                    wp.rcNormalPosition = hlbwi.rcFramePos;
                    wp.showCmd = (hlbwi.grfHLBWIF & HLBWIF_FRAMEWNDMAXIMIZED) 
                                    ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL;

                    // This is not broken in AOL because this
                    // is an external navigate (word has cocreateinstance()d
                    // Internet.Explorer and navigated it.
                    //
                    SetWindowPlacement(hwndFrame, &wp);
                }

                // Register the hlinkframe interface with the browse context, if it has not already
                // been registered
                if (_dwRegHLBC == 0)
                    _phlbc->Register(0, (IHlinkFrame *) this, pmkTarget, &_dwRegHLBC); 

                // add the link to browse context and
                // REVIEW: need to pass the proper friendly name
                _phlbc->OnNavigateHlink(grfHLNF, pmkTarget, pszLocation, NULL, NULL);
            }

            put_Visible(TRUE);
            
            SetForegroundWindow(hwndFrame);
        }

        //
        // According to SriniK, we need to call IHlinkSite::OnNavigationComplete
        // before returning from IHlinkFrame::Navigate with S_OK. (SatoNa)
        //
        if (pihlNavigate) 
        {
            BOOL fExternal = FALSE;
            if (_phlbc && _pbs) 
            {
                ITravelLog* ptl;
                if (SUCCEEDED(_pbs->GetTravelLog(&ptl))) 
                {
                    if (FAILED(ptl->GetTravelEntry(_pbs, 0, NULL))) 
                    {
                        TraceMsg(DM_HLINKTRACE, "CIEFA::_NavMag this is external nav. Don't call OnNavigationComplete");
                        fExternal = TRUE;
                    }
                    else if (SUCCEEDED(ptl->GetTravelEntry(_pbs, TLOG_BACKEXTERNAL, NULL))) 
                    {
                        TraceMsg(DM_HLINKTRACE, "CIEFA::_NavMag this is external for. Don't call OnNavigationComplete");
                        fExternal = TRUE;
                    }
                    ptl->Release();
                }
            }

            //
            // Don't call OnNavigationComplete if this is an external navigation.
            //
            if (!fExternal) 
            {
                IHlinkSite* pihlSite = NULL;
                DWORD dwSiteData;
                HRESULT hresT = pihlNavigate->GetHlinkSite(&pihlSite, &dwSiteData);
                if (SUCCEEDED(hresT) && pihlSite) 
                {
                    TraceMsg(DM_HLINKTRACE, "CIEFA::_NavMag calling OnNavigationComplete");
                    hresT = pihlSite->OnNavigationComplete(dwSiteData, 0, S_OK, L"");
                    if (FAILED(hresT)) 
                    {
                        TraceMsg(DM_ERROR, "CIEFA::Navigat OnNavComplete failed %x", hresT);
                    }
                    pihlSite->Release();
                }
            }
        }
    }

    return hres;
}

//
//  HACK - what we really want is a good private marshalled interface - zekel 8-AUG-97
//  to the Browser.  but for now we will overload the NavigateHack method, 
//  because it is simple and quick for ship.
//
#define HLNF_REFERRERHACK       0x40000000
HRESULT CIEFrameAuto::_ReferrerHack(LPCWSTR pszUrl)
{
    if (_pbs == NULL)    //Make sure we have a IBrowserService.
        return S_FALSE;

    LPITEMIDLIST pidl;

    if (SUCCEEDED(_pbs->IEParseDisplayName(CP_ACP, pszUrl, &pidl)))
    {
        ASSERT(pidl);
        _pbs->SetReferrer(pidl);
        ILFree(pidl);
    }

    return S_OK;
}

HRESULT CIEFrameAuto::NavigateHack(DWORD grfHLNF, IBindCtx *pbc, IBindStatusCallback *pibsc, LPCWSTR pszTargetName, LPCWSTR pszUrl, LPCWSTR pszLocation)
{
    HRESULT     hres = E_FAIL;
    IBindCtx *  pBindCtx = pbc;
    IUnknown *  pNotify = NULL;
    IUnknown *  pBindCtxParam = NULL;
    BOOL        fAsyncCalled = FALSE;
       
    // Check if we are actually a native frame build... 
    if (pbc)
    {
        hres = pbc->GetObjectParam(KEY_BINDCONTEXTPARAM, &pBindCtxParam);
    }
    
    if (SUCCEEDED(hres) && pBindCtxParam)
    {
        // NavigateHack can be called multiple times, and we only want to create the 
        // new bind context the first time. Since the ITargetNotify pointer is removed
        // after the first use, we can check that to make sure.
        // get and transfer the target notify pointer.
        hres = pbc->GetObjectParam(TARGET_NOTIFY_OBJECT_NAME, &pNotify);
        if (SUCCEEDED(hres) && pNotify)
        {
            // The call is coming from a native frame build of MSHTML. 
            // We can not use their bind context, create a new one and transfer
            // parameters.
            // The old bind context is going to be released by the creator, so do not
            // make a release call on it.
            hres = CreateAsyncBindCtxEx(NULL, 0, NULL, NULL, &pBindCtx, 0);
            if(FAILED(hres))
                goto Exit;

            fAsyncCalled = TRUE;

            // carry over the ITargetNotify2 pointer.
            hres = pBindCtx->RegisterObjectParam( TARGET_NOTIFY_OBJECT_NAME, pNotify );
            if (FAILED(hres))
                goto Exit;

            pNotify->Release();
            pNotify = NULL;

            // carry over the bind context parameter.
            hres = pBindCtx->RegisterObjectParam( KEY_BINDCONTEXTPARAM, pBindCtxParam );
            if (FAILED(hres))
                goto Exit;

            {
                IUnknown * pDwnBindInfo = NULL;

                if (SUCCEEDED(pbc->GetObjectParam(SZ_DWNBINDINFO_OBJECTPARAM, &pDwnBindInfo)) && pDwnBindInfo)
                {
                    pBindCtx->RegisterObjectParam(SZ_DWNBINDINFO_OBJECTPARAM, pDwnBindInfo);
                    pDwnBindInfo->Release();
                }
            }
        }

        pBindCtxParam->Release();
        pBindCtxParam = NULL;
    }
    
    if (IsFlagSet(grfHLNF, HLNF_REFERRERHACK))
        hres =  _ReferrerHack(pszUrl);
    else
        hres = _NavigateMagnum(grfHLNF, pBindCtx, pibsc, pszTargetName, pszUrl, pszLocation, NULL, NULL);

Exit:
    SAFERELEASE(pNotify); 
    SAFERELEASE(pBindCtxParam);

    // If the call failed anywhere, we can not be sure the new document
    // will free the object parameter that is in the bind context 
    // we have created in this function.
    if (FAILED(hres) && pBindCtx)
    {
        // we don't want to change the return code here.
        pBindCtx->RevokeObjectParam(KEY_BINDCONTEXTPARAM);
        pBindCtx->RevokeObjectParam(TARGET_NOTIFY_OBJECT_NAME);
    }
    
    if (fAsyncCalled)
        pBindCtx->Release();

    return hres;
}

// passing NULL pibsc and pbc will make be like "ReleaseNavigationState"
// passing -1 for pihlNavigate will cancel pending navigation

HRESULT CIEFrameAuto::Navigate(DWORD grfHLNF, IBindCtx *pbc,
     IBindStatusCallback *pibsc, IHlink *pihlNavigate)
{
    IMoniker* pmkTarget = NULL;
    LPOLESTR pwszDisplayName = NULL;
    LPOLESTR pwszLocation = NULL;
    LPOLESTR pwszFrameName = NULL;
    HRESULT hres = S_OK;

    if (pihlNavigate && ((IHlink *)-1) != pihlNavigate)
    {
        pihlNavigate->GetTargetFrameName(&pwszFrameName);

        //
        // Note that we are discarding "relative" portion.
        //
        hres = pihlNavigate->GetMonikerReference(HLINKGETREF_ABSOLUTE, &pmkTarget, &pwszLocation);

        HFRMMSG2("Navigate pihl->GetMonRef returned", hres, pmkTarget);

        if (SUCCEEDED(hres))
        {
            IBindCtx* pbcLocal;
    
            if (pbc) 
            {
                pbcLocal = pbc;
                pbcLocal->AddRef();
            }
            else 
            {
                hres = CreateBindCtx(0, &pbcLocal);
            }

            if (SUCCEEDED(hres))
            {
                hres = pmkTarget->GetDisplayName(pbcLocal, NULL, &pwszDisplayName);
                pbcLocal->Release();
            }
        }
    }

    if (SUCCEEDED(hres))
    {
        hres = _NavigateMagnum(grfHLNF, pbc, pibsc, pwszFrameName, pwszDisplayName, pwszLocation, pihlNavigate, pmkTarget);
    }
    if (pwszFrameName)
    {
        OleFree(pwszFrameName);
    }
    if (pwszDisplayName)
    {
        OleFree(pwszDisplayName);
    }
    if (pwszLocation)
    {
        OleFree(pwszLocation);
    }
    if (pmkTarget)
    {
        pmkTarget->Release();
    }
    return hres;
}

HRESULT CIEFrameAuto::OnNavigate(DWORD grfHLNF,
            /* [unique][in] */ IMoniker *pimkTarget,
            /* [unique][in] */ LPCWSTR pwzLocation,
            /* [unique][in] */ LPCWSTR pwzFriendlyName,
            /* [in] */ DWORD dwreserved)
{
    TraceMsg(0, "shlf TR ::OnNavigate called");
    return S_OK;
}

void CIEFrameAuto::_CancelPendingNavigation(VARIANTARG* pvar)
{
    TraceMsg(0, "shd TR _CancelPendingNavigation called");
    if (_pmsc) 
    {
        TraceMsg(0, "shd TR _CancelPendingNavigation calling _pmsc->Exec");
        _pmsc->Exec(&CGID_Explorer, SBCMDID_CANCELNAVIGATION, 0, pvar, NULL);
    }
}

// *** ITargetNotify ***

void 
CIEFrameAuto::_HandleOpenOptions( IUnknown * pUnkDestination, ITargetNotify * ptgnNotify)
{
    HRESULT             hres = S_OK;
    ITargetNotify2 *    ptgnNotify2 = NULL; 

    if (!pUnkDestination || !ptgnNotify)
        return;

    if (SUCCEEDED(ptgnNotify->QueryInterface( IID_ITargetNotify2, (void **)&ptgnNotify2)))
    {
        BSTR    bstrOptions = NULL;

        ASSERT(ptgnNotify2);

        // Apply the options only if the initator of the navigation 
        // asks for it.

        if (S_OK == ptgnNotify2->GetOptionString(&bstrOptions))
        {
            _omwin._OpenOptions.ReInitialize();

            if (bstrOptions)
            {
                _omwin._ParseOptionString(bstrOptions, ptgnNotify2);

                // We are done with the options string, release it
                SysFreeString(bstrOptions);
            }

            // Apply the options now.
            //
            IWebBrowser2 * pNewIE;

            if (SUCCEEDED(pUnkDestination->QueryInterface(IID_PPV_ARG(IWebBrowser2, &pNewIE))))
            {
                _omwin._ApplyOpenOptions(pNewIE);
                pNewIE->Release();
            }
        }

        ptgnNotify2->Release();
    
    }
}

HRESULT CIEFrameAuto::OnCreate(IUnknown *pUnkDestination, ULONG cbCookie)
{
    HRESULT             hres = S_OK;

    if (cbCookie == (ULONG)_cbCookie && _ptgnNotify)
    {
        _HandleOpenOptions( pUnkDestination, _ptgnNotify);
    
        hres = _ptgnNotify->OnCreate(pUnkDestination, cbCookie);
        SAFERELEASE(_ptgnNotify);
    }
    return hres;
}

HRESULT CIEFrameAuto::OnReuse(IUnknown *pUnkDestination)
{
    return S_OK;
}

#define NOTIFY_WAIT_TIMEOUT (60000)
//  chrisfra 10/10/96: do we need EnableModeless(FALSE)/(TRUE) around 
//  our little loop, or is the busy flag (which is set) sufficient?

HRESULT CIEFrameAuto::_WaitForNotify()
{
    if (_ptgnNotify && IsInternetExplorerApp())
    {
        DWORD dwObject, msWait, msStart = GetTickCount();

        goto DOPEEK;

        while (_ptgnNotify)
        {
            // NB We need to let the run dialog become active so we have to half handle sent
            // messages but we don't want to handle any input events or we'll swallow the
            // type-ahead.
            msWait = GetTickCount();
            if (msWait - msStart > NOTIFY_WAIT_TIMEOUT) 
                break;

            msWait = NOTIFY_WAIT_TIMEOUT - (msWait - msStart);
            dwObject = MsgWaitForMultipleObjects(0, NULL, FALSE, msWait, QS_ALLINPUT);
            // Are we done waiting?
            switch (dwObject) 
            {
            case WAIT_FAILED:
                break;
                
            case WAIT_OBJECT_0:
DOPEEK:
                // got a message, dispatch it and wait again
                MSG msg;
                while (PeekMessage(&msg, NULL,0, 0, PM_REMOVE)) 
                {
                    DispatchMessage(&msg);
                    if (_ptgnNotify == NULL || 
                        ((GetTickCount() - msStart) > NOTIFY_WAIT_TIMEOUT)) 
                        break;
                    
                }
                break;
            }
        }
    }
    return S_OK;
}

HRESULT CIEFrameAuto::_RegisterCallback(TCHAR *szFrameName, ITargetNotify *ptgnNotify)
{
    HRESULT hr = S_OK;

    SAFERELEASE(_ptgnNotify);

    _fRegistered = 0;

    if (ptgnNotify)
    {
        IDispatch *pid;
        hr = QueryInterface(IID_PPV_ARG(IDispatch, &pid));
        if (SUCCEEDED(hr))
        {
            hr = E_FAIL;
            IShellWindows *psw = WinList_GetShellWindows(TRUE);
            if (psw != NULL)
            {
                long cbCookie;
                hr = psw->Register(pid, NULL, SWC_CALLBACK, &cbCookie);
                if (SUCCEEDED(hr))
                {
                    TCHAR szCookie[25];   // big enough for "_[cbCookie]"
                    int slenCookie;
                    int slenName;
                    int slenMin;

                    _cbCookie = cbCookie;
                    _fRegistered = 1;
                    _ptgnNotify = ptgnNotify;
                    _ptgnNotify->AddRef();

                    //  prepend unique id to target -- tells created WebBrowserOC to
                    //  register the remainder (if any) as frame name and to perform
                    //  callbacks on all registered callbacks
                    wnsprintf(szCookie, ARRAYSIZE(szCookie), TEXT("_[%ld]"), cbCookie);
                    slenCookie = lstrlen(szCookie);
                    slenName = lstrlen(szFrameName);
                    slenMin =  min((int)MAX_URL_STRING-slenCookie,slenName);
                    MoveMemory(&szFrameName[slenCookie], szFrameName, CbFromCch(slenMin));
                    szFrameName[slenCookie+slenMin] = 0;
                    CopyMemory(szFrameName, szCookie, CbFromCch(slenCookie));
                }
                psw->Release();
            }
            pid->Release();
        }
    }

    return hr;
}


HRESULT CIEFrameAuto::_RevokeCallback()
{
    HRESULT hr = S_OK;

    if (_fRegistered)
    {
        IShellWindows *psw = WinList_GetShellWindows(TRUE);
        if (psw != NULL)
        {
            hr = psw->Revoke(_cbCookie);
            psw->Release();
        }
    }
    SAFERELEASE(_ptgnNotify);
    _fRegistered = 0;
    return hr;
}


//
//  HACK - what we really want is a good private marshalled interface - zekel 8-AUG-97
//  to the Browser.  but for now we will overload the NavigateHack method, 
//  because it is simple and quick for ship.
//
void CIEFrameAuto::_SetReferrer(ITargetFramePriv *ptgfp)
{
    LPITEMIDLIST pidl;
    WCHAR szUrl[MAX_URL_STRING];

    ASSERT(ptgfp);

    //Make sure we have a IBrowserService.
    if (_psb && SUCCEEDED(_pbs->GetPidl(&pidl)))
    {
        if (SUCCEEDED(_pbs->IEGetDisplayName(pidl, szUrl, SHGDN_FORPARSING)))
            ptgfp->NavigateHack(HLNF_REFERRERHACK, NULL, NULL, NULL, szUrl, NULL);

        ILFree(pidl);
    }
}


HRESULT CIEFrameAuto::_JumpTo(IBindCtx *pbc, LPWSTR pszLocation, DWORD grfHLNF, IBindStatusCallback *pibsc, IHlink *pihlNavigate, LPCWSTR pszFrameName, LPCWSTR pszUrl)
{
    LPITEMIDLIST pidl = NULL;
    HRESULT hres;
    ITargetNotify *ptgnNotify = NULL;
    IUnknown *punkNotify = NULL;
    IUnknown *punkThis = NULL;
    UINT uiCP = CP_ACP;

    // Get the current document codepage from Trident and use it for url string conversion if necessary.
    if (!(grfHLNF & HLNF_ALLOW_AUTONAVIGATE) && _pmsc)
    {
        VARIANT varOut = { 0 };
        VARIANT varIn = { 0 };

        varIn.vt = VT_I4;
        varIn.lVal = DFC_URLCHARSET; // we want the doc's url charset

        if (SUCCEEDED(_pmsc->Exec(&CGID_ShellDocView, SHDVID_DOCFAMILYCHARSET, 0, &varIn, &varOut)))
            uiCP = (UINT)varOut.lVal;
    }

    //  Note that we are simply passing the pidl to ISB::BrowseObject,
    // assuming that new shell32.dll allows us to bind to DocObject
    // documents.
    //

    DWORD flags = (grfHLNF & HLNF_OPENINNEWWINDOW) ?
        (SBSP_NEWBROWSER | SBSP_ABSOLUTE | SBSP_INITIATEDBYHLINKFRAME) :
        (SBSP_SAMEBROWSER | SBSP_ABSOLUTE | SBSP_INITIATEDBYHLINKFRAME);


    flags |= ((grfHLNF & HLNF_ALLOW_AUTONAVIGATE) ? (SBSP_ALLOW_AUTONAVIGATE) : 0);
    flags |= ((grfHLNF & SHHLNF_WRITENOHISTORY) ? (SBSP_WRITENOHISTORY) : 0);
    flags |= ((grfHLNF & SHHLNF_NOAUTOSELECT) ? (SBSP_NOAUTOSELECT) : 0);

    if (pbc && SUCCEEDED(pbc->GetObjectParam(TARGET_NOTIFY_OBJECT_NAME, &punkNotify)))
    {
        if (FAILED(punkNotify->QueryInterface(IID_PPV_ARG(ITargetNotify, &ptgnNotify))))
            ptgnNotify = NULL;
        
        punkNotify->Release();
        QueryInterface(IID_PPV_ARG(IUnknown, &punkThis));
    }

    if (grfHLNF & HLNF_CREATENOHISTORY)
        flags |= SBSP_REDIRECT;

    if (flags & SBSP_NEWBROWSER)
    {
        TCHAR *pszHeaders = NULL;
        BYTE *pPostData = NULL;
        DWORD cbPostData = 0;
        TCHAR szFrameName[MAX_URL_STRING+1];
        STGMEDIUM stgPostData = { TYMED_NULL, NULL, NULL };

        //Qfe:1478 If restricted to open in new window, return failure.
        if ((grfHLNF & HLNF_OPENINNEWWINDOW) 
            && SHIsRestricted2W(_hwnd, REST_NoOpeninNewWnd, NULL, 0))
        {
            SAFERELEASE(punkThis);
            return E_ACCESSDENIED;
        }

        szFrameName[0] = 0;

        //  Here is where if we are doing a new window we must
        //  extract frame, post etc and append to pidl.  These must
        //  be done in the following order (to match extraction code):
        //      URLID_FRAMENAME,URLID_POSTDATA,URLID_HEADERS

        if (pszFrameName)
        {
            SHUnicodeToTChar(pszFrameName, szFrameName, ARRAYSIZE(szFrameName));
        }


        if (pibsc)
        {
            GetHeadersAndPostData(pibsc,&pszHeaders,&stgPostData,&cbPostData, NULL);

            if (stgPostData.tymed == TYMED_HGLOBAL) 
            {
                pPostData = (LPBYTE) stgPostData.hGlobal;
            }
        }

        hres = _PidlFromUrlEtc(uiCP, pszUrl, pszLocation, &pidl);

        HFRMMSG2("_JumpTo _PidlFromUrlEtc returned", hres, pidl);

        if (SUCCEEDED(hres))
        {
            IUnknown* punkNewWindow = NULL;
            BOOL fCancel = FALSE;

            // The NewWindow2 event may return the window for us.
            FireEvent_NewWindow2(_GetOuter(), &punkNewWindow, &fCancel);
            if (!fCancel)
            {
                BOOL fProcessed = FALSE;

                // We might need the old NewWindow event...
                if (!punkNewWindow)
                {
                    _RegisterCallback(szFrameName, ptgnNotify);
        
                    // fire an event to indicate a new window needs to be created
                    // to allow a container to handle it itself if it wants
                    // since we may be aggregated, QI our parent

                    // Yet another Compuserve workaround (IE 60688):
                    // If the target frame name is "_blank", Compuserve will pass that name
                    // in to the Navigate call of the new window.  We would then create a new window
                    // (which would fire this event) causing a loop.  Break the recursion by sending
                    // an empty string for the frame name.
                    HWND hwnd = _GetHWND();
                    
                    if (hwnd)
                    {
                        FireEvent_NewWindow(_GetOuter(), hwnd, pidl,pszLocation,0,
                            StrCmpI(szFrameName, TEXT("_blank")) ? szFrameName : TEXT(""),  // Target frame name
                            pPostData,cbPostData,pszHeaders,&fProcessed);
                    }
                }
    
                if (!fProcessed)
                {
                    if (!punkNewWindow)
                    {
#ifdef INCLUDE_BUSTED_OC_QI
                        IUnknown* pdvb = NULL;
#endif
                        _RevokeCallback();

#ifdef INCLUDE_BUSTED_OC_QI
                        // For some unidentifiable reason the old code did NOT
                        // create a new window if we were hosted in the WebBrowserOC.
                        // mikesh/cheechew/jeremys/chrisfra don't know why this happens.
                        // Who knows what app will break if we change this...
                        // (Note: IDefViewBrowser is a CWebBrowseSB only interface)
                        //
                        // NOTE: chrisfra 3/11/97, this code breaks open a
                        // new window for a non-existent target, when in
                        // desktop component or browser band
                        fCancel = !(_psbTop && FAILED(_psbTop->QueryInterface(IID_PPV_ARG(IDefViewBrowser, &pdvb))));
                        if (pdvb)
                            pdvb->Release();
#endif
                    }
    
                    // what we really want to do is just hand this off to
                    // _psbTop->BrowseObject and let it (CWebBrowserSB or CShellBrowser)
                    // decide whether to use HlinkFrameNavigate or not, but if we
                    // do that, then we lose the grfHLNF and pihlNavigate.
                    // So put that logic here...
                    //
                    if (!fCancel)
                    {
                        hres = CreateTargetFrame(pszFrameName, &punkNewWindow);
                        if (SUCCEEDED(hres))
                        {
                            //  Notify ptgnNotify, then release and remove from bindctx
                            if (ptgnNotify)
                            {
                                _HandleOpenOptions( punkNewWindow, ptgnNotify);

                                ptgnNotify->OnCreate(punkNewWindow, GetTickCount());

                                ptgnNotify->Release();
                                ptgnNotify = NULL;

                                pbc->RevokeObjectParam(TARGET_NOTIFY_OBJECT_NAME);
                            }

                            LPHLINKFRAME phf;

                            hres = punkNewWindow->QueryInterface(IID_PPV_ARG(IHlinkFrame, &phf));
                            if (SUCCEEDED(hres))
                            {
                                ITargetFramePriv * ptgfp;

                                if (NULL == pihlNavigate)
                                {
                                    hres = punkNewWindow->QueryInterface(IID_PPV_ARG(ITargetFramePriv, &ptgfp));
                                }

                                if (SUCCEEDED(hres))
                                {
                                    if (pihlNavigate)
                                    {
                                        hres = phf->Navigate(grfHLNF & ~HLNF_OPENINNEWWINDOW, 
                                                             pbc, 
                                                             pibsc, 
                                                             pihlNavigate);
                                    }
                                    else
                                    {
                                        // HACK - see this methods comments 
                                        _SetReferrer(ptgfp);

                                        hres = ptgfp->NavigateHack(grfHLNF & ~HLNF_OPENINNEWWINDOW, 
                                                             pbc, 
                                                             pibsc,
                                                             NULL,
                                                             pszUrl,
                                                             pszLocation);
                                    }
    
                                    if (FAILED(hres)) 
                                    {
                                        TraceMsg(DM_ERROR, "CIEFA::_JumpTo marshalled IHlinkFrame::Navigate failed %x", hres);
                                    }

                                    ShowHlinkFrameWindow(punkNewWindow);
                                    if (NULL == pihlNavigate)
                                    {
                                        ptgfp->Release();
                                    }

                                    if(SUCCEEDED(hres) && pibsc)
                                    {
                                        _SetPendingNavigateContext(NULL, NULL);
                                    }

                                }
                                phf->Release();
                            }
                        }
                    }
                    else 
                    {
                        //
                        //  If NEWBROWSER is specified when there is no top level
                        // browser, we should ask IE/Shell to do browsing.
                        // We don't pass HLNF_OPENINNEWWINDOW in this case.
                        //
                        
                        //  Notify object doing navigation that we are the object implementing IWebBrowserApp
                        if (ptgnNotify) ptgnNotify->OnReuse(punkThis);
    
                        if (pihlNavigate)
                        {
                            hres = HlinkFrameNavigate(grfHLNF & ~HLNF_OPENINNEWWINDOW,
                                                        NULL, NULL, pihlNavigate, NULL);
                        }
                        else
                        {
                            hres = HlinkFrameNavigateNHL(grfHLNF & ~HLNF_OPENINNEWWINDOW,
                                       NULL, NULL, NULL, pszUrl, pszLocation);
                        }
                    }
                }
                else
                {
                    //  Oldstyle AOL or other 3rd Party, wait for registration of 
                    //  WebBrowserOC, which calls us back on _ptgnNotify
                    _WaitForNotify();
                    //  We timed out the window create, notify caller
                    if (_ptgnNotify) 
                        _ptgnNotify->OnCreate(NULL, 0);
                    _RevokeCallback();
                }
            }

            if (punkNewWindow)
                punkNewWindow->Release();

        }
        else
        {
            TraceMsg(DM_ERROR, "IEAuto::_JumpTo _PidlFromUrlEtc (1) failed %x", hres);
        }
        if (pszHeaders) 
        {
            LocalFree(pszHeaders);
            pszHeaders = NULL;
        }

        if (stgPostData.tymed != TYMED_NULL)
        {
            ReleaseStgMedium(&stgPostData);
        }

    }
    else
    {
        //  Notify object doing navigation that we are the object implementing IWebBrowserApp
        if (ptgnNotify) ptgnNotify->OnReuse(punkThis);

        hres = _PidlFromUrlEtc(uiCP, pszUrl, pszLocation, &pidl);
        if (SUCCEEDED(hres))
        {
            hres = _psb->BrowseObject(pidl, flags);
        }
        else 
        {
            TraceMsg(DM_ERROR, "IEAuto::_JumpTo _PidlFromUrlEtc (2) failed %x", hres);
        }
    }

    if (pidl)
    {
        HFRMMSG2("_JumpTo _psb->BrowseObject returned", hres, 0);
        ILFree(pidl);
    }
    
    if (ptgnNotify)
    {
        ptgnNotify->Release();
        pbc->RevokeObjectParam(TARGET_NOTIFY_OBJECT_NAME);
    }

    SAFERELEASE(punkThis);

    return hres;
}


HRESULT CIEFrameAuto::QueryService(REFGUID guidService, REFIID riid, void ** ppvObj)
{
    *ppvObj = NULL;

    // WARNING: Note that we are not following the strict semantics of
    //  ISP::QueryService. It is, however, OK because this (the fact that
    //  IHlinkFrame support IServiceProvider) is not public.

    if (IsEqualIID(guidService, SID_SOmWindow)) 
    {
        return _omwin.QueryInterface(riid, ppvObj);
    }
    else if (IsEqualIID(guidService, IID_IHlinkFrame)) 
    {
        SERVMSG("QueryService called", _pbc, _pbsc);

        if (IsEqualIID(riid, IID_IBindCtx) && _pbc) 
        {
            *ppvObj = _pbc;
            _pbc->AddRef();
        }
        else if (IsEqualIID(riid, IID_IBindStatusCallback) && _pbsc)
        {
            *ppvObj = _pbsc;
            _pbsc->AddRef();
        }         
        else
        {
            return QueryInterface(riid, ppvObj);
        }
    }
    else if (IsEqualIID(guidService, SID_PendingBindStatusCallback)) 
    {
        if (IsEqualIID(riid, IID_IBindStatusCallback) && _pbscPending)
        {
            *ppvObj = _pbscPending;
            _pbscPending->AddRef();
        }
    } 
    else if (_psp) 
    {
        return _psp->QueryService(guidService, riid, ppvObj);
    }

    return *ppvObj ? S_OK : E_FAIL;
}


HRESULT CIEFrameAuto::Exec(
    /* [unique][in] */ const GUID *pguidCmdGroup,
    /* [in] */ DWORD nCmdID,
    /* [in] */ DWORD nCmdexecopt,
    /* [unique][in] */ VARIANTARG *pvarargIn,
    /* [unique][out][in] */ VARIANTARG *pvarargOut)
{
    HRESULT hres = S_OK;

    if (pguidCmdGroup)
    {
        if (IsEqualGUID(CGID_Explorer, *pguidCmdGroup))
        {
            switch(nCmdID)
            {
            case SBCMDID_CANCELNAVIGATION:
                _CancelPendingNavigation(NULL);
                break;

            case SBCMDID_SELECTHISTPIDL:
            case SBCMDID_HISTSFOLDER:
                if (_poctFrameTop)
                    hres = _poctFrameTop->Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
                else
                    hres = S_OK;
                break;

            case SBCMDID_IESHORTCUT:
#ifdef BROWSENEWPROCESS_STRICT // "Nav in new process" has become "Launch in new process", so this is no longer needed
                // If this is an IE shortcut and browse in a new process is turned on
                // and we are explorer.exe - we should pass on the request to navigate to
                // this shortcut. The caller is expected to create a new window/process to
                // launch this shortcut
                if (IsBrowseNewProcessAndExplorer())  
                    hres = E_FAIL;
                else
#endif
                    hres = _NavIEShortcut(pvarargIn,pvarargOut);
                
                break;

            case SBCMDID_GETSHORTCUTPATH:
               if (_pwszShortcutPath && pvarargOut)
               {
                    pvarargOut->bstrVal = SysAllocString(_pwszShortcutPath);
                    if (pvarargOut->bstrVal)
                        pvarargOut->vt = VT_BSTR;        //no need to set hres=S_OK since it is inited already
                    else 
                        hres = E_OUTOFMEMORY;
               }
               else 
               {
                    if (pvarargOut)
                        pvarargOut->vt = VT_EMPTY;

                    hres = E_FAIL;
               }
               
               break;    
            default:
                hres = OLECMDERR_E_NOTSUPPORTED; 
            }
        }
        else if (IsEqualGUID(CGID_ShortCut, *pguidCmdGroup))
        {
            if (_poctFrameTop) // we must check!
                hres = _poctFrameTop->Exec(&CGID_ShortCut, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
            else
                hres = OLECMDERR_E_NOTSUPPORTED;
        } 
        else if (IsEqualGUID(CGID_ShellDocView, *pguidCmdGroup))
        {
            switch (nCmdID)
            {
                case SHDVID_DELEGATEWINDOWOM:
                    _omwin.SetDelegationPolicy(V_BOOL(pvarargIn));
                    break;
                default:
                    hres = OLECMDERR_E_NOTSUPPORTED;
            }
        }
        else if (IsEqualGUID(CGID_InternetExplorer, *pguidCmdGroup))
        // CGID_InternetExplorer are public defined in msiehost.h
        {
            switch (nCmdID)
            {
                case IECMDID_CLEAR_AUTOCOMPLETE_FOR_FORMS:
                {
                    if (pvarargIn->vt == VT_I4)
                    {
                        hres = ClearAutoSuggestForForms(V_I4(pvarargIn));
                    }
                    else
                        hres = E_INVALIDARG;
                }
                break;

                case IECMDID_SETID_AUTOCOMPLETE_FOR_FORMS:
                {
                    if ((pvarargIn->vt == VT_UI8) ||
                        (pvarargIn->vt == VT_I8))
                    {
                        hres = SetIdAutoSuggestForForms(((GUID *)(&pvarargIn->ullVal)), _omwin.IntelliForms());
                    }
                    else
                        hres = E_INVALIDARG;
                }
                break;

                default:
                    hres = OLECMDERR_E_NOTSUPPORTED;
            }
        }
        else
        {
            hres = OLECMDERR_E_UNKNOWNGROUP;
        }
    }
    else
    {
        hres = OLECMDERR_E_UNKNOWNGROUP;
    }

    return hres;
}


BOOL CIEFrameAuto::_fNavigationPending()
{
    // unfortunately, the hyperlink frame doesn't REALLY know when there's
    // a navigation pending or not because people might not call OnReleaseNavigation.
    // only the real browser knows.

    if (_pmsc) 
    {
        MSOCMD rgCmd;
        rgCmd.cmdID = SBCMDID_CANCELNAVIGATION;
        rgCmd.cmdf = 0;

        _pmsc->QueryStatus(&CGID_Explorer, 1, &rgCmd, NULL);
        return (rgCmd.cmdf & MSOCMDF_ENABLED);
    }
    return FALSE;
}

HRESULT CIEFrameAuto::QueryStatus(const GUID *pguidCmdGroup,
    ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
{
    if (pguidCmdGroup && IsEqualGUID(CGID_Explorer, *pguidCmdGroup)) 
    {
        for (ULONG i = 0; i < cCmds; i++)
        {
            switch (rgCmds[i].cmdID)
            {
            case SBCMDID_CANCELNAVIGATION:
                rgCmds[i].cmdf = _fNavigationPending() ? MSOCMDF_ENABLED : 0;
                break;

            case SBCMDID_WRITEHIST:
                rgCmds[i].cmdf = _fSuppressHistory ? 0:MSOCMDF_ENABLED;
                break;
            
            case SBCMDID_SELECTHISTPIDL:
                rgCmds[i].cmdf = _fSuppressSelect || !_poctFrameTop ? 0:MSOCMDF_ENABLED;
                break;

            default:
                rgCmds[i].cmdf = 0;
                break;
            }
        }
    }
    else
    {
        return OLECMDERR_E_UNKNOWNGROUP;
    }

    if (pcmdtext)
    {
        pcmdtext->cmdtextf = MSOCMDTEXTF_NONE;
        pcmdtext->cwActual = 0;
    }

    return NOERROR;
}

HRESULT CIEFrameAuto::_PidlFromUrlEtc(UINT uiCP, LPCWSTR pszUrl, LPWSTR pszLocation, LPITEMIDLIST* ppidl)
{
    *ppidl = NULL;      // assumes error

    // ALGORITHM:
    //  - First, we call IEParseDisplayName to generate the pidl
    //    to the specified URL or file name.
    //  - if we have fragment (pszLocation) specified,
    //    we call IEILAppendFragment() to add the hidden fragment id
    if (_pbs == NULL)  //Make sure we have a IBrowserService.
        return (S_FALSE);
        
    HRESULT hr = _pbs->IEParseDisplayName(uiCP, pszUrl, ppidl);

    // This is ugly, if it's a file path that failed to parse because
    // it doesn't exist, we want to create a SimpleIDList so we display
    // a res: navigation failed IFrame instead of the err dlg displayed
    // in DisplayParseError() below.
    if (FAILED(hr)) 
    {
        TCHAR szPath[MAX_PATH];
        DWORD cchBuf = ARRAYSIZE(szPath);

        // If it's a FILE URL, convert it to a path.
        if (IsFileUrlW(pszUrl) && SUCCEEDED(PathCreateFromUrl(pszUrl, szPath, &cchBuf, 0)))
        {
            // That worked, we are done because our buffer is now full.
        }
        else        
        {
            // We now need to copy to the buffer and we assume it's a path.
            StrCpyN(szPath, pszUrl, ARRAYSIZE(szPath));
        }

        *ppidl = SHSimpleIDListFromPath(szPath);
        if (*ppidl)
            hr = S_OK;
    }

    if (SUCCEEDED(hr))
    {
        if (pszLocation && *pszLocation)
        {
            *ppidl = IEILAppendFragment(*ppidl, pszLocation);
            hr = *ppidl ? S_OK : E_OUTOFMEMORY;
        }
    } 
    else 
    {
        //
        // NOTES: This behavior is new in IE4.0. We are adding
        //  this message box based on the request (bug-report)
        //  from Office guys. (SatoNa)
        //
        hr = _pbs->DisplayParseError(hr, pszUrl);
    }
    return hr;
}

HRESULT CIEFrameAuto::_NavIEShortcut(VARIANT *pvarIn, VARIANT *pvarargOut)
{
    //  need to validate verb and clsid
    HRESULT hr = E_ACCESSDENIED;
    READYSTATE ready;
    BOOL fForceNavigate = pvarargOut ? ((VT_BOOL == pvarargOut->vt ) && (pvarargOut->boolVal)) : FALSE;
    
    get_ReadyState(&ready);

    ASSERT(pvarIn);
    ASSERT(pvarIn->vt == VT_BSTR);
    //
    //  we dont want to allow the exec to go through if this window
    //  is busy with something else.  we should probably allow
    //  READYSTATE_COMPLETE and READYSTATE_UNINITIALIZED.
    //  if we use READYSTATE_UNINITIALIZED, we need to init the browser
    //  and make it visible and stuff like that.  something to the 
    //  check that IPersisteHistory->LoadHistory() does in shvocx.cpp.
    //  right now we will only allow COMPLETE.
    //
    TraceMsgW(DM_SHELLEXECOBJECT, "[%X] IEAuto_NavIEShortcut entered '%s' ready = %d", this, pvarIn->bstrVal, ready);

    
    if (((ready == READYSTATE_COMPLETE || ready == READYSTATE_UNINITIALIZED) || (fForceNavigate))
        && S_OK == IUnknown_Exec(_psbTop, &CGID_Explorer, SBCMDID_ISIEMODEBROWSER, 0, NULL, NULL))
        
    {
        IPersistFile *ppf;
        if (SUCCEEDED(CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_ALL, IID_PPV_ARG(IPersistFile, &ppf))))
        {
            if (SUCCEEDED(ppf->Load(pvarIn->bstrVal, STGM_READ)))
            {
                LPWSTR pszUrl = NULL;
                TraceMsg(DM_SHELLEXECOBJECT, "[%X] IEAuto_NavIEShortcut shortcut inited with file", this);

                IUniformResourceLocatorW *purl;
                if (SUCCEEDED(ppf->QueryInterface(IID_PPV_ARG(IUniformResourceLocatorW, &purl))))
                {
                    purl->GetURL(&pszUrl);
                    purl->Release();
                }
                
                if (pszUrl)
                {
                    TraceMsgW(DM_SHELLEXECOBJECT, "[%X] IEAuto_NavIEShortcut found %s", this, pszUrl);
                    
                    LPITEMIDLIST pidl;
                    IEParseDisplayNameW(CP_ACP, pszUrl, &pidl);
                    if (pidl)
                    {
                        ASSERT(NULL == _pwszShortcutPathPending);
                        if (_pwszShortcutPathPending)
                            LocalFree(_pwszShortcutPathPending);

                        _pwszShortcutPathPending = StrDupW(pvarIn->bstrVal);

                        hr = _BrowseObject(pidl, SBSP_SAMEBROWSER);

                        if (SUCCEEDED(hr))
                        {
                            if (ready == READYSTATE_UNINITIALIZED)
                                put_Visible(VARIANT_TRUE);
                            HWND hwnd = _GetHWND();
                            if (hwnd)
                            {
                                if (IsIconic(hwnd))
                                    ShowWindow(hwnd, SW_RESTORE);
                                else
                                    SetForegroundWindow(hwnd);
                            }
                        }
                        ILFree(pidl);
                    }
                    SHFree(pszUrl);
                }
            }
            ppf->Release();
        }
    }
    TraceMsg(DM_SHELLEXECOBJECT, "IEAuto_NavIEShortcut returns 0x%X", hr);

    return hr;
}

