//
// Shell UI Control Class (CShellUIHelper)
//
// Sample code : shell\docs\shuiod.htm
//
// This is the source code of the ShellUI control class. You can put an instance
// of ShellUI control on any HTML pages then
// 
// The key thing is it always bring up some UI and never alter the system
// silently (without the User's interaction). By doing this, we can expose
// shell features as much as we want without worrying about the security
// problem (which automation interface has).
// 
// This control also allow us to provide many configuration type UI (such as
// "Customize your IE page") as well as rich web-view on some folders
// (especially control panel ;-) without paying the cost of data-binding. 
// 
#include "priv.h"
#include "sccls.h"
#ifndef UNIX
#include <webcheck.h>
#else
#include <subsmgr.h>
#endif
#include "favorite.h"
#include "caggunk.h"
#include "resource.h"
#include "channel.h"
#include "chanmgr.h"
#include "chanmgrp.h"
#include "iforms.h"
#include "dspsprt.h"
#include "impexp.h" // needed for RunImportExportWizard()
#include "iforms.h"
//#include "cobjsafe.h" // CObjectSafety
#include "shvocx.h" // WrapSpecialUrl()

#include <mluisupp.h>

#define REG_DESKCOMP_SCHEME                 TEXT("Software\\Microsoft\\Internet Explorer\\Desktop\\Scheme")
#define REG_VAL_SCHEME_DISPLAY              TEXT("Display")
#define REG_VAL_GENERAL_WALLPAPER           TEXT("Wallpaper")
#define REG_VAL_GENERAL_TILEWALLPAPER       TEXT("TileWallpaper")
#define REG_DESKCOMP_GENERAL                TEXT("Software\\Microsoft\\Internet Explorer\\Desktop%sGeneral")

STDAPI SHAddSubscribeFavorite (HWND hwnd, LPCWSTR pwszURL, LPCWSTR pwszName, DWORD dwFlags,
                               SUBSCRIPTIONTYPE subsType, SUBSCRIPTIONINFO* pInfo);

// move it to shdocvw.h
UINT IE_ErrorMsgBox(IShellBrowser* psb,
                    HWND hwndOwner, HRESULT hrError, LPCWSTR szError, LPCTSTR pszURLparam,
                    UINT idResource, UINT wFlags);

#define DM_SHUIOC   DM_TRACE

LONG GetSearchFormatString(DWORD dwIndex, LPTSTR psz, DWORD cbpsz);

HRESULT TargetQueryService(IUnknown *punk, REFIID riid, void **ppvObj);

EXTERN_C const SA_BSTRGUID s_sstrSearch;
EXTERN_C const SA_BSTRGUID s_sstrFailureUrl;


class CShellUIHelper :
        public CAggregatedUnknown,
        public IObjectWithSite,
        public IObjectSafety,
        public IShellUIHelper,  // dual, IDispatch
        public IDispatchEx,
        protected CImpIDispatch
{
    // IUnknown
    STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj) { return CAggregatedUnknown::QueryInterface(riid, ppvObj);};
    STDMETHODIMP_(ULONG) AddRef(void) { return CAggregatedUnknown::AddRef();};
    STDMETHODIMP_(ULONG) Release(void) { return CAggregatedUnknown::Release();};

    // IObjectWithSite
    STDMETHODIMP SetSite(IUnknown *pUnkSite);
    STDMETHODIMP GetSite(REFIID riid, void **ppvSite);

    // IObjectSafety
    STDMETHOD(GetInterfaceSafetyOptions)(REFIID riid, DWORD *pdwSupportedOptions, 
        DWORD *pdwEnabledOptions);
    STDMETHOD(SetInterfaceSafetyOptions)(REFIID riid, DWORD dwOptionSetMask, 
        DWORD dwEnabledOptions);

    // IDispatch
    STDMETHODIMP GetTypeInfoCount(UINT * pctinfo);
    STDMETHODIMP GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo);
    STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, UINT cNames,
                               LCID lcid, DISPID * rgdispid);
    STDMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, 
                        DISPPARAMS * pdispparams, VARIANT * pvarResult, 
                        EXCEPINFO * pexcepinfo, UINT * puArgErr);

    // IDispatchEx
    STDMETHODIMP GetDispID(BSTR bstrName, DWORD grfdex, DISPID *pid);
    STDMETHODIMP InvokeEx(DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
                          VARIANT *pvarRes, EXCEPINFO *pei, 
                          IServiceProvider *pspCaller);
    STDMETHODIMP DeleteMemberByName(BSTR bstr, DWORD grfdex);           
    STDMETHODIMP DeleteMemberByDispID(DISPID id);           
    STDMETHODIMP GetMemberProperties(DISPID id, DWORD grfdexFetch, DWORD *pgrfdex);           
    STDMETHODIMP GetMemberName(DISPID id, BSTR *pbstrName);
    STDMETHODIMP GetNextDispID(DWORD grfdex, DISPID id, DISPID *pid);
    STDMETHODIMP GetNameSpaceParent(IUnknown **ppunk);

    // IShellUIHelper
    STDMETHODIMP Execute();
    STDMETHODIMP ResetSafeMode();
    STDMETHODIMP ResetFirstBootMode();
    STDMETHODIMP RefreshOfflineDesktop();
    STDMETHODIMP AddFavorite(BSTR strURL, VARIANT *Title);
    STDMETHODIMP AddChannel(BSTR bstrURL);
    STDMETHODIMP AddDesktopComponent(BSTR strURL, BSTR strType, 
                        VARIANT *Left, VARIANT *Top, 
                        VARIANT *Width, VARIANT *Height);
    STDMETHODIMP IsSubscribed(BSTR bstrURL, VARIANT_BOOL* pBool);
    STDMETHODIMP NavigateAndFind(BSTR URL, BSTR strQuery, VARIANT* varTargetFrame);
    STDMETHODIMP ImportExportFavorites(VARIANT_BOOL fImport, BSTR strImpExpPath);
    STDMETHODIMP AutoCompleteSaveForm(VARIANT *Form);
    STDMETHODIMP AutoScan(BSTR strSearch, BSTR strFailureUrl, VARIANT* pvarTargetFrame);
    STDMETHODIMP AutoCompleteAttach(VARIANT *Form);
    STDMETHODIMP ShowBrowserUI(BSTR bstrName, VARIANT *pvarIn, VARIANT *pvarOut);

    HRESULT v_InternalQueryInterface(REFIID riid, void ** ppvObj);

    CShellUIHelper(IUnknown* punkAgg);
    ~CShellUIHelper();

    inline IDispatch *GetExternalDispatch()
    {
        return _pExternalDispEx ? _pExternalDispEx : _pExternalDisp;
    }

    void SetExternalDispatch(IDispatch *pExternalDisp)
    {
        ATOMICRELEASE(_pExternalDisp);
        ATOMICRELEASE(_pExternalDispEx);

        //  If we were passed an IDispatch to delegate to then we need to
        //  see if it can do IDispatchEx so we can support it as well,
        //  otherwise we just fall back to good ole IDispatch.
        if (pExternalDisp)
        {
            if (FAILED(pExternalDisp->QueryInterface(IID_IDispatchEx, 
                                      (void **)&_pExternalDispEx)))
            {
                _pExternalDisp = pExternalDisp;
                _pExternalDisp->AddRef();
            }
        }
    }

    STDMETHODIMP ShowChannel(IChannelMgrPriv *pChMgrPriv, LPWSTR pwszURL, HWND hwnd);
    HWND _GetOwnerWindow();
    HRESULT _ConnectToTopLevelConnectionPoint(BOOL fConnect, IUnknown* punk);
    STDMETHODIMP _DoFindOnPage(IDispatch* pdisp);

    friend HRESULT CShellUIHelper_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi);
    friend HRESULT CShellUIHelper_CreateInstance2(IUnknown** ppunk, REFIID riid, 
                                                 IUnknown *pSite, IDispatch *pExternalDisp);

    DWORD               _dwSafety;
    // Cached pointers, hwnd
    IUnknown*           _punkSite;  // site pointer
    IDispatchEx*        _pExternalDispEx;
    IDispatch*          _pExternalDisp;
    DWORD               _dwcpCookie;
    BOOL                _fWaitingToFindText;
    BSTR                _bstrQuery;
    VOID *              _pvIntelliForms;
};

STDAPI CShellUIHelper_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
{
    HRESULT hres = E_OUTOFMEMORY;
    CShellUIHelper* psuo = new CShellUIHelper(punkOuter);
    if (psuo)
    {
        *ppunk = psuo->_GetInner();
        hres = S_OK;
    }
    return hres;
}

HRESULT CShellUIHelper_CreateInstance2(IUnknown** ppunk, REFIID riid, 
                                      IUnknown *pSite, IDispatch *pExternalDisp)
{
    HRESULT hres = E_OUTOFMEMORY;
    CShellUIHelper* psuo = new CShellUIHelper(NULL);
    
    if (psuo)
    {
        hres = psuo->QueryInterface(riid, (void **)ppunk);
        psuo->Release();

        if (SUCCEEDED(hres))
        {
            psuo->SetSite(pSite);
            psuo->SetExternalDispatch(pExternalDisp);
        }
    }

    return hres;
}

CShellUIHelper::CShellUIHelper(IUnknown* punkAgg) :
    CAggregatedUnknown(punkAgg),
    CImpIDispatch(LIBID_SHDocVw, 1, 1, IID_IShellUIHelper)
{
    DllAddRef();
    _fWaitingToFindText = FALSE;
    _bstrQuery = NULL;
}

CShellUIHelper::~CShellUIHelper()
{

    ReleaseIntelliForms(_pvIntelliForms);

    if (_punkSite)
        SetSite(NULL);  // In case the parent did not clean it up.
    if (_bstrQuery)
        SysFreeString(_bstrQuery);
    ATOMICRELEASE(_pExternalDisp);
    ATOMICRELEASE(_pExternalDispEx);

    DllRelease();
}

HRESULT CShellUIHelper::v_InternalQueryInterface(REFIID riid, void ** ppvObj)
{
    if (IsEqualIID(riid, IID_IDispatch) ||
        IsEqualIID(riid, IID_IShellUIHelper))
    {
        *ppvObj = SAFECAST(this, IShellUIHelper *);
    }
    else if (IsEqualIID(riid, IID_IObjectWithSite))
    {
        *ppvObj = SAFECAST(this, IObjectWithSite *);
    }
    else if (IsEqualIID(riid, IID_IObjectSafety))
    {
        *ppvObj = SAFECAST(this, IObjectSafety *);
    }
    else if (IsEqualIID(riid, IID_IDispatchEx))
    {
        *ppvObj = SAFECAST(this, IDispatchEx *);
    }
    else
    {
        *ppvObj = NULL;
        return E_NOINTERFACE;
    }

    AddRef();
    return S_OK;
}

HWND CShellUIHelper::_GetOwnerWindow()
{
    HWND hwnd;
    HRESULT hres;

    // this deals with NULL _punkSite and zeros hwnd on failure
    IUnknown_GetWindow(_punkSite, &hwnd);

    if (!hwnd)
    {
        //
        //  We get to this point if we are instantiated like this
        //  in jscript:
        //      foo = new ActiveXObject("Shell.UIControl");
        //  or vbscript:
        //      set foo = CreateObject("Shell.UIControl");
        //
        if (_punkSite)
        {
            IServiceProvider *pSP = NULL;
            IOleWindow *pOleWindow = NULL;
            hres = _punkSite->QueryInterface(IID_IServiceProvider, (void **)&pSP);

            if (SUCCEEDED(hres))
            {
                ASSERT(pSP);

                hres = pSP->QueryService(SID_SContainerDispatch, IID_IOleWindow, 
                                         (void **)&pOleWindow);
                if (SUCCEEDED(hres))
                {
                    pOleWindow->GetWindow(&hwnd);
                    pOleWindow->Release();
                }
                pSP->Release();
            }
        }
        else
        {
            //  It's either this or the functions we call should take NULL for HWNDs.
            hwnd = GetDesktopWindow();
        }
    }

    return hwnd;
}

HRESULT CShellUIHelper::_ConnectToTopLevelConnectionPoint(BOOL fConnect, IUnknown* punk)
{
    HRESULT hr = E_INVALIDARG;
    IConnectionPointContainer* pcpContainer;
    IServiceProvider*          psp;
    IServiceProvider*          psp2;

    ASSERT(punk);
    if (punk)
    {
        hr = punk->QueryInterface(IID_IServiceProvider, (void**)&psp);

        if (SUCCEEDED(hr))
        {
            hr = psp->QueryService(SID_STopLevelBrowser, IID_IServiceProvider, (void**) &psp2);

            if (SUCCEEDED(hr))
            {
                hr = psp2->QueryService(SID_SWebBrowserApp, IID_IConnectionPointContainer, (void **)&pcpContainer);

                if (SUCCEEDED(hr))
                {
                    //to avoid ambiguous reference
                    IDispatch* pdispThis;
                    this->QueryInterface(IID_IDispatch, (void **)&pdispThis);
                    ASSERT(pdispThis);
                
                    hr = ConnectToConnectionPoint(pdispThis, DIID_DWebBrowserEvents2, fConnect,
                                                  pcpContainer, &_dwcpCookie, NULL);
                    pcpContainer->Release();
                    pdispThis->Release();
                }
                psp2->Release();
            }
            psp->Release();
        }
    }
    
    return hr;
}

HRESULT CShellUIHelper::SetSite(IUnknown *punkSite)
{
    if (!_punkSite)
    {
        _ConnectToTopLevelConnectionPoint(TRUE, punkSite);
    } 
    else
    {
        ASSERT(punkSite == NULL);   //if we've already got _punkSite, we'd better be releasing the site

        _ConnectToTopLevelConnectionPoint(FALSE, _punkSite);
        ATOMICRELEASE(_punkSite);
    }
    
    _punkSite = punkSite;

    if (_punkSite)
        _punkSite->AddRef();

    return S_OK;
}

HRESULT CShellUIHelper::GetSite(REFIID riid, void **ppvSite)
{
    TraceMsg(DM_SHUIOC, "SHUO::GetSite called");

    if (_punkSite) 
        return _punkSite->QueryInterface(riid, ppvSite);

    *ppvSite = NULL;
    return E_FAIL;
}

STDMETHODIMP CShellUIHelper::GetInterfaceSafetyOptions(REFIID riid, DWORD *pdwSupportedOptions, 
                                             DWORD *pdwEnabledOptions)
{
    HRESULT hr = S_OK;

    if (!pdwSupportedOptions || !pdwEnabledOptions)
        return E_POINTER;

    if (IID_IDispatch == riid)
    {
        *pdwSupportedOptions = INTERFACESAFE_FOR_UNTRUSTED_CALLER;
        *pdwEnabledOptions = _dwSafety & INTERFACESAFE_FOR_UNTRUSTED_CALLER;
    }
    else
    {
        *pdwSupportedOptions = 0;
        *pdwEnabledOptions = 0;
        hr = E_NOINTERFACE;
    }

    return hr;
}


STDMETHODIMP CShellUIHelper::SetInterfaceSafetyOptions(REFIID riid, DWORD dwOptionSetMask, 
                                             DWORD dwEnabledOptions)
{
    HRESULT hr = S_OK;
    
    if (IID_IDispatch == riid)
        _dwSafety = dwOptionSetMask & dwEnabledOptions;
    else
        hr = E_NOINTERFACE;

    return hr;
}


struct SHUI_STRMAP 
{
    LPCTSTR psz;
    int id;
};

int _MapStringToId(LPCTSTR pszStr, const SHUI_STRMAP* const psmap, int cel, int idDefault)
{
    if (pszStr)
    {
        for (int i=0; i<cel ; i++) 
        {
            if (StrCmpI(psmap[i].psz, pszStr) == 0) 
            {
                return psmap[i].id;
            }
        }
    }
    return idDefault;
}


LPCTSTR OptionalVariantToStr(VARIANT *pvar, LPTSTR pszBuf, UINT cchBuf)
{
    if (pvar->vt == VT_BSTR && pvar->bstrVal)
    {
        SHUnicodeToTChar(pvar->bstrVal, pszBuf, cchBuf);
        return pszBuf;
    }
    *pszBuf = 0;
    return NULL;
}

int OptionalVariantToInt(VARIANT *pvar, int iDefault)
{
    VARIANT v;
    VariantInit(&v);
    if (SUCCEEDED(VariantChangeType(&v, pvar, 0, VT_I4)))
    {
        iDefault = v.lVal;
        // VariantClear(&v);   // not needed, VT_I4 has no allocs
    }
    return iDefault;
}

BOOL OptionalVariantToBool(VARIANT *pvar, BOOL fDefault)
{
    VARIANT v;
    VariantInit(&v);
    if (SUCCEEDED(VariantChangeType(&v, pvar, 0, VT_BOOL)))
    {
        fDefault = v.boolVal;
    }
    return fDefault;
}

//------------------------------------------------------------------------
STDMETHODIMP CShellUIHelper::AddFavorite(/* [in] */ BSTR strURL, /* [in][optional] */ VARIANT *Title)
{
    HRESULT hres = S_OK;
    LPITEMIDLIST pidl;
    BSTR bstrTemp = NULL;

    if (IsSpecialUrl(strURL))
    {
        bstrTemp = SysAllocString(strURL);
        if (bstrTemp)
        {
            hres = WrapSpecialUrl(&bstrTemp);
            if (SUCCEEDED(hres))
                strURL = bstrTemp;
        }
        else
            hres = E_OUTOFMEMORY;
    }

    if (SUCCEEDED(hres))
        hres = IECreateFromPath(strURL, &pidl);
    if (SUCCEEDED(hres)) 
    {
        TCHAR szTitle[MAX_PATH];
        hres = ::AddToFavorites(_GetOwnerWindow(), pidl, OptionalVariantToStr(Title, szTitle, ARRAYSIZE(szTitle)), TRUE, NULL, NULL);
        ILFree(pidl);
    }
    if (bstrTemp)
        SysFreeString(bstrTemp);
    return hres;
}


//------------------------------------------------------------------------

STDMETHODIMP CShellUIHelper::ShowChannel(IChannelMgrPriv *pChMgrPriv, LPWSTR pwszURL, HWND hwnd)
{
    HRESULT hres = E_FAIL;
    IServiceProvider *pSP1 = NULL,
                     *pSP2 = NULL;
    IWebBrowser2 *pWebBrowser2 = NULL;

    if (_punkSite)
    {
        hres = _punkSite->QueryInterface(IID_IServiceProvider, (void **)&pSP1);
        if (SUCCEEDED(hres))
        {
            ASSERT(pSP1);
            hres = pSP1->QueryService(SID_STopLevelBrowser,
                                      IID_IServiceProvider,
                                      (void**)&pSP2);
            if (SUCCEEDED(hres))
            {
                ASSERT(pSP2);
                hres = pSP2->QueryService(SID_SWebBrowserApp,
                                          IID_IWebBrowser2,
                                          (void**)&pWebBrowser2);
                ASSERT((SUCCEEDED(hres) && pWebBrowser2) || FAILED(hres));
                pSP2->Release();
            }
            pSP1->Release();
        }
    }

    if (FAILED(hres))
    {
        hres = CoCreateInstance(CLSID_InternetExplorer, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARG(IWebBrowser2, &pWebBrowser2));
    }

    if (SUCCEEDED(hres))
    {
        ASSERT(pWebBrowser2);
        hres = pChMgrPriv->ShowChannel(pWebBrowser2, pwszURL, hwnd);
        pWebBrowser2->Release();
    }
    
    return hres;
}

STDMETHODIMP CShellUIHelper::AddChannel(BSTR bstrURL)
{
    HRESULT hres;
    IChannelMgrPriv *pChMgrPriv;

    TCHAR szURL[MAX_URL_STRING];
    HWND hwnd;

    if (!bstrURL)
    {
        return E_INVALIDARG;
    }
    
    hwnd = _GetOwnerWindow();

    //
    //  As long as the underlying functions choke on NULL HWNDs then we may as well
    //  bail early.
    //
    if (hwnd)
    {
        if (!SHIsRestricted2W(hwnd, REST_NoChannelUI, NULL, 0) && !SHIsRestricted2W(hwnd, REST_NoAddingChannels, NULL, 0))
        {
            StrCpyNW(szURL, bstrURL, ARRAYSIZE(szURL));

            hres = JITCoCreateInstance(CLSID_ChannelMgr, 
                                    NULL,
                                    CLSCTX_INPROC_SERVER, 
                                    IID_IChannelMgrPriv, 
                                    (void **)&pChMgrPriv,
                                    hwnd,
                                    FIEF_FLAG_FORCE_JITUI);

            if (S_OK == hres)
            {
                ASSERT(pChMgrPriv);
            
                hres = pChMgrPriv->AddAndSubscribe(hwnd, bstrURL, NULL);
                if (hres == S_OK)
                {
                    hres = ShowChannel(pChMgrPriv, bstrURL, hwnd);
                }
                else if (FAILED(hres))
                {
                    IE_ErrorMsgBox(NULL, hwnd, hres, NULL, szURL, IDS_CHANNEL_UNAVAILABLE, MB_OK| MB_ICONSTOP);
                    hres = S_FALSE;
                }
                pChMgrPriv->Release();
            }
            else if (SUCCEEDED(hres))
            {
                hres = S_FALSE; // FAIL silently for now - throw dialog for indicating that reboot required if needed
            } else
            {
                IE_ErrorMsgBox(NULL, hwnd, hres, NULL, szURL, IDS_FAV_UNABLETOCREATE, MB_OK| MB_ICONSTOP);
                hres = S_FALSE;
            }
        }
        else
        {
            hres = S_FALSE;  // Failure code results in a script error.
        }
    }
    else    //  !hwnd
    {
        hres = E_FAIL;
    }
    return hres;
}

STDAPI GetHTMLDoc2(IUnknown *punk, IHTMLDocument2 **ppHtmlDoc)
{
    *ppHtmlDoc = NULL;

    if (!punk)
        return E_FAIL;
        
    *ppHtmlDoc = NULL;
    //  The window.external, jscript "new ActiveXObject" and the <OBJECT> tag
    //  don't take us down the same road.

    IOleClientSite *pClientSite;
    HRESULT hr = punk->QueryInterface(IID_PPV_ARG(IOleClientSite, &pClientSite));
    if (SUCCEEDED(hr))
    {
        //  <OBJECT> tag path

        IOleContainer *pContainer;
        hr = pClientSite->GetContainer(&pContainer);
        if (SUCCEEDED(hr))
        {
            hr = pContainer->QueryInterface(IID_PPV_ARG(IHTMLDocument2, ppHtmlDoc));
            pContainer->Release();
        }
    
        if (FAILED(hr))
        {
            //  window.external path
            IWebBrowser2 *pWebBrowser2;
            hr = IUnknown_QueryService(pClientSite, SID_SWebBrowserApp, IID_PPV_ARG(IWebBrowser2, &pWebBrowser2));
            if (SUCCEEDED(hr))
            {
                IDispatch *pDispatch;
                hr = pWebBrowser2->get_Document(&pDispatch);
                if (SUCCEEDED(hr))
                {
                    hr = pDispatch->QueryInterface(IID_PPV_ARG(IHTMLDocument2, ppHtmlDoc));
                    pDispatch->Release();
                }
                pWebBrowser2->Release();
            }
        }
        pClientSite->Release();
    }
    else
    {
        //  jscript path
        hr = IUnknown_QueryService(punk, SID_SContainerDispatch, IID_PPV_ARG(IHTMLDocument2, ppHtmlDoc));
    }

    ASSERT(FAILED(hr) || (*ppHtmlDoc));

    return hr;
}


// A lot like GetHTMLDoc2, but only cares about window.external
STDMETHODIMP GetTopLevelBrowser(IUnknown *punk, IWebBrowser2 **ppwb2)
{
    return IUnknown_QueryService(punk, SID_SWebBrowserApp, IID_PPV_ARG(IWebBrowser2, ppwb2));
}


STDMETHODIMP ZoneCheck(IUnknown *punkSite, BSTR bstrReqUrl)
{
    HRESULT hr = E_ACCESSDENIED;

    //  Return S_FALSE if we don't have a host site since we have no way of doing a 
    //  security check.  This is as far as VB 5.0 apps get.
    if (!punkSite)
        return S_FALSE;

    //  1)  Get an IHTMLDocument2 pointer
    //  2)  Get URL from doc
    //  3)  Create security manager
    //  4)  Check if doc URL zone is local, if so everything's S_OK
    //  5)  Otherwise, get and compare doc URL SID to requested URL SID

    IHTMLDocument2 *pHtmlDoc;
    if (SUCCEEDED(GetHTMLDoc2(punkSite, &pHtmlDoc)))
    {
        ASSERT(pHtmlDoc);
        BSTR bstrDocUrl;
        if (SUCCEEDED(pHtmlDoc->get_URL(&bstrDocUrl)))
        {
            ASSERT(bstrDocUrl);
            IInternetSecurityManager *pSecMgr;

            if (SUCCEEDED(CoCreateInstance(CLSID_InternetSecurityManager, 
                                           NULL, 
                                           CLSCTX_INPROC_SERVER,
                                           IID_IInternetSecurityManager, 
                                           (void **)&pSecMgr)))
            {
                ASSERT(pSecMgr);
                DWORD dwZoneID = URLZONE_UNTRUSTED;
                if (SUCCEEDED(pSecMgr->MapUrlToZone(bstrDocUrl, &dwZoneID, 0)))
                {
                    if (dwZoneID == URLZONE_LOCAL_MACHINE)
                        hr = S_OK;
                }
                if (hr != S_OK && bstrReqUrl)
                {
                    BYTE reqSid[MAX_SIZE_SECURITY_ID], docSid[MAX_SIZE_SECURITY_ID];
                    DWORD cbReqSid = ARRAYSIZE(reqSid);
                    DWORD cbDocSid = ARRAYSIZE(docSid);

                    if (   SUCCEEDED(pSecMgr->GetSecurityId(bstrReqUrl, reqSid, &cbReqSid, 0))
                        && SUCCEEDED(pSecMgr->GetSecurityId(bstrDocUrl, docSid, &cbDocSid, 0))
                        && (cbReqSid == cbDocSid)
                        && (memcmp(reqSid, docSid, cbReqSid) == 0))
                    {

                        hr = S_OK;
                    }
                }
                pSecMgr->Release();
            }
            SysFreeString(bstrDocUrl);
        }
        pHtmlDoc->Release();
    }
    else
    {
        //  If we don't have an IHTMLDocument2 we aren't running in a browser that supports
        //  our OM.  We shouldn't block in this case since we could potentially
        //  get here from other hosts (VB, WHS, etc.).
        hr = S_FALSE;
    }

    return hr;
}

// This function checks to see if a navigation is happening from either internet
// or restricted url to LOCAL_MACHINE.
BOOL CanNavigateToUrlWithLocalMachineCheck(IUnknown *punkSite, BSTR pchurltarget)
{
    BOOL    bCanNavigate = FALSE;

    // Get the DochostUI flags for the site.
    IDocHostUIHandler *pIDocHostUI;
    if (SUCCEEDED(punkSite->QueryInterface(IID_PPV_ARG(IDocHostUIHandler, &pIDocHostUI))))
    {
        DOCHOSTUIINFO   dhInfo;
        if (SUCCEEDED(pIDocHostUI->GetHostInfo(&dhInfo)))
        {
            if (!(dhInfo.dwFlags & DOCHOSTUIFLAG_LOCAL_MACHINE_ACCESS_CHECK))
                bCanNavigate = TRUE;
        }
        pIDocHostUI->Release();
    }

    // If the DOCHOSTUIFLAG_LOCAL_MACHINE_ACCESS_CHECK is set or if we failed to
    // get the flags, do a check to see if the zone is elevated to local machine.
    if (!bCanNavigate)
    {
        // Get the site's url.
        IHTMLDocument2 *pHtmlDoc;
        if (SUCCEEDED(GetHTMLDoc2(punkSite, &pHtmlDoc)))
        {
            BSTR pchurlsource;
            if (SUCCEEDED(pHtmlDoc->get_URL(&pchurlsource)))
            {
                IInternetSecurityManager *pSecMgr;
                if (SUCCEEDED(IUnknown_QueryService(punkSite, SID_SInternetSecurityManager,
                                                    IID_PPV_ARG(IInternetSecurityManager, &pSecMgr))) ||
                    SUCCEEDED(CoCreateInstance(CLSID_InternetSecurityManager, NULL, CLSCTX_INPROC_SERVER,
                                            IID_PPV_ARG(IInternetSecurityManager, &pSecMgr))))
                {
                    DWORD   dwZoneIDSource  = URLZONE_UNTRUSTED;
                    DWORD   dwZoneIDTarget  = URLZONE_LOCAL_MACHINE;
                    DWORD   dwFlags         = PUAF_DEFAULT;

                    // Get zone of the source url.
                    if (!pchurlsource || IsSpecialUrl(pchurlsource))
                    {
                        // Treat special Urls as restricted.
                        dwZoneIDSource = URLZONE_UNTRUSTED;
                    }
                    else if (!SUCCEEDED(pSecMgr->MapUrlToZone(pchurlsource, &dwZoneIDSource, 0)))
                    {
                        // If MapUrlToZone fails, treat the url as restricted.
                        dwZoneIDSource = URLZONE_UNTRUSTED;
                    }

                    // Get zone of the target url
                    if (!SUCCEEDED(pSecMgr->MapUrlToZone(pchurltarget, &dwZoneIDTarget, 0)))
                    {
                        // If MapUrlToZone fails, treat the url as MyComputer.  This is safe.
                        dwZoneIDTarget = URLZONE_LOCAL_MACHINE;
                    }

                    // Check if there is a zone elevation.
                    if ((dwZoneIDSource != URLZONE_INTERNET &&
                        dwZoneIDSource != URLZONE_UNTRUSTED) ||
                        dwZoneIDTarget != URLZONE_LOCAL_MACHINE)
                    {
                        // There is no zone elevation.
                        bCanNavigate = TRUE;
                    }
                    pSecMgr->Release();
                }
                SysFreeString(pchurlsource);
            }
            pHtmlDoc->Release();
        }
    }
    return bCanNavigate;
}

STDMETHODIMP CShellUIHelper::IsSubscribed(BSTR bstrURL, VARIANT_BOOL* pBool)
{
    HRESULT hr;

    if (!bstrURL || !pBool)
    {
        return E_INVALIDARG;
    }

    hr = ZoneCheck(_punkSite, bstrURL);

    if (SUCCEEDED(hr))
    {
        ISubscriptionMgr *pSubscriptionMgr;

        hr = CoCreateInstance(CLSID_SubscriptionMgr, NULL, CLSCTX_INPROC_SERVER,
                              IID_ISubscriptionMgr, (void**)&pSubscriptionMgr);
        if (SUCCEEDED(hr))
        {
            ASSERT(pSubscriptionMgr);

            BOOL tmpBool;
            
            hr = pSubscriptionMgr->IsSubscribed(bstrURL, &tmpBool);
            *pBool = tmpBool ? VARIANT_TRUE : VARIANT_FALSE;
            
            pSubscriptionMgr->Release();
        }
    }

    return hr;
}

/****************************************************************************
 *
 *  AddDesktopComponentA - Adds a component to the desktop
 *
 *  ENTRY:
 *      hwnd - the parent for all UI
 *      pszUrlA - the URL of the component
 *      iCompType - one of COMP_TYPE_*
 *      iLeft, iTop, iWidth, iHeight - dimensions of the component
 *      dwFlags - additional flags
 *
 *  RETURNS:
 *      TRUE on success
 *      
 ****************************************************************************/
BOOL AddDesktopComponentW(HWND hwnd, LPCWSTR pszUrl, int iCompType,
                                    int iLeft, int iTop, int iWidth, int iHeight,
                                    DWORD dwFlags)
{
    COMPONENT Comp;
    BOOL    fRet = FALSE;
    HRESULT hres;

    Comp.dwSize = sizeof(Comp);

    //
    // Build the pcomp structure.
    //
    Comp.dwID = -1;
    Comp.iComponentType = iCompType;
    Comp.fChecked = TRUE;
    Comp.fDirty = FALSE;
    Comp.fNoScroll = FALSE;
    Comp.dwSize = SIZEOF(Comp);
    Comp.cpPos.dwSize = SIZEOF(COMPPOS);
    Comp.cpPos.iLeft = iLeft;
    Comp.cpPos.iTop = iTop;
    Comp.cpPos.dwWidth = iWidth;
    Comp.cpPos.dwHeight = iHeight;
    Comp.cpPos.izIndex = COMPONENT_TOP;
    Comp.cpPos.fCanResize = TRUE;
    Comp.cpPos.fCanResizeX = TRUE;
    Comp.cpPos.fCanResizeY = TRUE;
    Comp.cpPos.iPreferredLeftPercent = 0;
    Comp.cpPos.iPreferredTopPercent = 0;
    StrCpyNW(Comp.wszSource, pszUrl, ARRAYSIZE(Comp.wszSource));
    StrCpyNW(Comp.wszSubscribedURL, pszUrl, ARRAYSIZE(Comp.wszSource));
    StrCpyNW(Comp.wszFriendlyName, pszUrl, ARRAYSIZE(Comp.wszFriendlyName));
    Comp.dwCurItemState = IS_NORMAL;

    IActiveDesktop * piad;

    //
    // Add it to the system.
    //
    hres = CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER, IID_IActiveDesktop, (LPVOID*)&piad);
    if (SUCCEEDED(hres))
    {
        dwFlags |= DTI_ADDUI_POSITIONITEM;
        piad->AddDesktopItemWithUI(hwnd, &Comp, dwFlags);
        piad->Release();
        fRet = TRUE;
    } 

    return fRet;
}

//------------------------------------------------------------------------

STDMETHODIMP CShellUIHelper::AddDesktopComponent(BSTR strURL, BSTR strType, 
            /* [optional, in] */ VARIANT *Left,
            /* [optional, in] */ VARIANT *Top,
            /* [optional, in] */ VARIANT *Width,
            /* [optional, in] */ VARIANT *Height)
{
#ifndef DISABLE_ACTIVEDESKTOP_FOR_UNIX

    HRESULT hres;
    int iType;

    if (StrCmpIW(strType, L"image")==0) 
    {
        iType = COMP_TYPE_PICTURE;
    } 
    else if (StrCmpIW(strType, L"website")==0) 
    {
        iType = COMP_TYPE_WEBSITE;
    }
    else
    {
        iType = 0;
    }

    if (iType) 
    {
        AddDesktopComponentW(_GetOwnerWindow(), strURL, iType,
                             OptionalVariantToInt(Left, -1),
                             OptionalVariantToInt(Top, -1),
                             OptionalVariantToInt(Width, -1),
                             OptionalVariantToInt(Height, -1),
                             DTI_ADDUI_DISPSUBWIZARD);
        hres = S_OK;
    }
    else 
    {
        hres = E_INVALIDARG;
    }
    return hres;
#else // #ifndef DISABLE_ACTIVEDESKTOP_FOR_UNIX

return E_INVALIDARG;

#endif // #ifndef DISABLE_ACTIVEDESKTOP_FOR_UNIX
}
void RemoveDefaultWallpaper();
STDMETHODIMP CShellUIHelper::ResetFirstBootMode()
{
#ifndef DISABLE_ACTIVEDESKTOP_FOR_UNIX

    if (MLShellMessageBox(
                        _GetOwnerWindow(),
                        MAKEINTRESOURCE(IDS_CONFIRM_RESETFLAG),
                        MAKEINTRESOURCE(IDS_TITLE),
                        MB_YESNO | MB_ICONQUESTION) == IDYES)
    {
        RemoveDefaultWallpaper();
        return S_OK;
    }

#endif // #ifndef DISABLE_ACTIVEDESKTOP_FOR_UNIX
    return S_FALSE;
}

// Little helper function used to change the safemode state
void SetSafeMode(DWORD dwFlags)
{
    IActiveDesktopP * piadp;

    HRESULT hres = CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER, IID_IActiveDesktopP, (LPVOID*)&piadp);
    if (SUCCEEDED(hres))
    {
        piadp->SetSafeMode(dwFlags);
        piadp->Release();
    }
}

STDMETHODIMP CShellUIHelper::ResetSafeMode()
{
#ifndef DISABLE_ACTIVEDESKTOP_FOR_UNIX

    if ((ZoneCheck(_punkSite, NULL) == S_OK) || (MLShellMessageBox(
                       _GetOwnerWindow(),
                       MAKEINTRESOURCE(IDS_CONFIRM_RESET_SAFEMODE),
                       MAKEINTRESOURCE(IDS_TITLE),
                       MB_YESNO | MB_ICONQUESTION) == IDYES))
    {
        SetSafeMode(SSM_CLEAR | SSM_REFRESH);
        return S_OK;
    }

#endif // #ifndef DISABLE_ACTIVEDESKTOP_FOR_UNIX
    return S_FALSE;
}

STDMETHODIMP CShellUIHelper::RefreshOfflineDesktop()
{
#ifndef DISABLE_ACTIVEDESKTOP_FOR_UNIX
    IADesktopP2 * piad;

    HRESULT hres = CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER, IID_IADesktopP2, (LPVOID*)&piad);
    if (SUCCEEDED(hres))
    {
        piad->UpdateAllDesktopSubscriptions();
        piad->Release();
    }
#endif // #ifndef DISABLE_ACTIVEDESKTOP_FOR_UNIX
    return S_OK;
}

STDMETHODIMP CShellUIHelper::GetTypeInfoCount(UINT * pctinfo)
{ 
    return E_NOTIMPL;
}

STDMETHODIMP CShellUIHelper::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo)
{ 
    return E_NOTIMPL;
}

STDMETHODIMP CShellUIHelper::GetIDsOfNames(REFIID riid, OLECHAR **rgszNames, 
                                      UINT cNames, LCID lcid, DISPID * rgdispid)
{ 
    HRESULT hr = E_FAIL;

    //  First let our host get a crack at this method
    IDispatch *pDisp = GetExternalDispatch();

    if (pDisp)
    {
        hr = pDisp->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
        if (SUCCEEDED(hr))
        {
            if (rgdispid[0] > 0)
            {
                //  Offset there dispid
                rgdispid[0] += DISPID_SHELLUIHELPERLAST;
            }
        }
    }

    if (FAILED(hr))
    {
        //  Our host didn't support it so we'll see if it's one of ours.
        hr = CImpIDispatch::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid);
    }

    return hr;
}

HRESULT CShellUIHelper::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,
        DISPPARAMS *pdispparams, VARIANT * pvarResult, EXCEPINFO *pexcepinfo, UINT *puArgErr)
{
    HRESULT hr = DISP_E_MEMBERNOTFOUND;
    
    if ((dispidMember > 0) && (dispidMember <= DISPID_SHELLUIHELPERLAST))
    {
        hr = CImpIDispatch::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, 
                                   pvarResult, pexcepinfo, puArgErr);
    }
    else if (_fWaitingToFindText && (dispidMember == DISPID_DOCUMENTCOMPLETE))
    {
        ASSERT(pdispparams->rgvarg[1].vt == VT_DISPATCH);

        _fWaitingToFindText = FALSE;
        hr = _DoFindOnPage(pdispparams->rgvarg[1].pdispVal);
    }
    else
    {
        IDispatch *pDisp = GetExternalDispatch();
        if (pDisp)
        {
            if (dispidMember > 0)
            {
                //  Fixup the offset we added in GetIDsOfNames
                dispidMember -= DISPID_SHELLUIHELPERLAST;
            }
            hr = pDisp->Invoke(dispidMember, riid, lcid, wFlags, pdispparams, 
                                        pvarResult, pexcepinfo, puArgErr);
        }
    }

    return hr;                                   
}

STDMETHODIMP CShellUIHelper::GetDispID(BSTR bstrName, DWORD grfdex, DISPID *pid)
{
    HRESULT hr = E_FAIL;
    LCID lcid = GetSystemDefaultLCID();

    //  First let our host get a crack at this method via IDispatchEx or IDispatch
    if (_pExternalDispEx)
    {
        hr = _pExternalDispEx->GetDispID(bstrName, grfdex, pid);
    }
    else if (_pExternalDisp)
    {
        hr = _pExternalDisp->GetIDsOfNames(IID_NULL, &bstrName, 1, lcid, pid);
    }

    if (SUCCEEDED(hr))
    {
        if (*pid > 0)
        {
            *pid += DISPID_SHELLUIHELPERLAST;
        }
    }
    else
    {
        //  Our host didn't support it so we'll see if it's one of ours.
        hr = CImpIDispatch::GetIDsOfNames(IID_NULL, &bstrName, 1, lcid, pid);
    }

    return hr;
}

STDMETHODIMP CShellUIHelper::InvokeEx(DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp,
                                 VARIANT *pvarRes, EXCEPINFO *pei, 
                                 IServiceProvider *pspCaller)
{
    HRESULT hr = DISP_E_MEMBERNOTFOUND;
    UINT ArgErr;    //  inetsdk says this isn't used here

    //  First see if it's ours
    if ((id > 0) && (id <= DISPID_SHELLUIHELPERLAST))
    {
        hr = CImpIDispatch::Invoke(id, IID_NULL, lcid, wFlags, pdp, 
                                   pvarRes, pei, &ArgErr);
    }
    else
    {
        if (id > 0)
        {
            id -= DISPID_SHELLUIHELPERLAST;
        }
        //  otherwise try external IDispatchEx 
        if (_pExternalDispEx)
        {
            hr = _pExternalDispEx->InvokeEx(id, lcid, wFlags, pdp, pvarRes, pei, pspCaller);
        }
        //  finally try the external IDispatch
        else if (_pExternalDisp)
        {
            hr = _pExternalDisp->Invoke(id, IID_NULL, lcid, wFlags, pdp, 
                                        pvarRes, pei, &ArgErr);
        }
    }
    
    return hr;
}

STDMETHODIMP CShellUIHelper::DeleteMemberByName(BSTR bstr, DWORD grfdex)
{
    HRESULT hr = E_NOTIMPL;
    
    if (_pExternalDispEx)
    {
        hr = _pExternalDispEx->DeleteMemberByName(bstr, grfdex);
    }

    return hr;
}
        
STDMETHODIMP CShellUIHelper::DeleteMemberByDispID(DISPID id)
{
    HRESULT hr = E_NOTIMPL;
    
    if (_pExternalDispEx)
    {
        hr = _pExternalDispEx->DeleteMemberByDispID(id);
    }

    return hr;
}
        
STDMETHODIMP CShellUIHelper::GetMemberProperties(DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
{
    HRESULT hr = E_NOTIMPL;
    
    if (_pExternalDispEx)
    {
        hr = _pExternalDispEx->GetMemberProperties(id, grfdexFetch, pgrfdex);
    }

    return hr;
}
        
STDMETHODIMP CShellUIHelper::GetMemberName(DISPID id, BSTR *pbstrName)
{
    HRESULT hr = E_NOTIMPL;
    
    if (_pExternalDispEx)
    {
        hr = _pExternalDispEx->GetMemberName(id, pbstrName);
    }

    return hr;
}

STDMETHODIMP CShellUIHelper::GetNextDispID(DWORD grfdex, DISPID id, DISPID *pid)
{
    HRESULT hr = E_NOTIMPL;
    
    if (_pExternalDispEx)
    {
        hr = _pExternalDispEx->GetNextDispID(grfdex, id, pid);
    }

    return hr;
}
        
STDMETHODIMP CShellUIHelper::GetNameSpaceParent(IUnknown **ppunk)
{
    HRESULT hr = E_NOTIMPL;
    
    if (_pExternalDispEx)
    {
        hr = _pExternalDispEx->GetNameSpaceParent(ppunk);
    }

    return hr;
}

int GetIntFromReg(HKEY    hKey,
                  LPCTSTR lpszSubkey,
                  LPCTSTR lpszNameValue,
                  int     iDefault)
{
    TCHAR szValue[20];
    DWORD dwSizeofValueBuff = SIZEOF(szValue);
    int iRetValue = iDefault;
    DWORD dwType;

    if ((SHGetValue(hKey, lpszSubkey, lpszNameValue, &dwType,(LPBYTE)szValue,
                   &dwSizeofValueBuff) == ERROR_SUCCESS) && dwSizeofValueBuff)
    {
        if (dwType == REG_SZ)
        {
            iRetValue = (int)StrToInt(szValue);
        }
    }

    return iRetValue;
}

void GetRegLocation(LPTSTR lpszResult, DWORD cchResult, LPCTSTR lpszKey, LPCTSTR lpszScheme)
{
    TCHAR szSubkey[MAX_PATH];
    DWORD dwDataLength = sizeof(szSubkey) - 2 * sizeof(TCHAR);
    DWORD dwType;

    StrCpyN(szSubkey, TEXT("\\"), ARRAYSIZE(szSubkey));
    if (lpszScheme)
        StrCatBuff(szSubkey, lpszScheme, ARRAYSIZE(szSubkey));
    else
        SHGetValue(HKEY_CURRENT_USER, REG_DESKCOMP_SCHEME, REG_VAL_SCHEME_DISPLAY, &dwType,
            (LPBYTE)(szSubkey) + sizeof(TCHAR), &dwDataLength);
    if (szSubkey[1])
        StrCatBuff(szSubkey, TEXT("\\"), ARRAYSIZE(szSubkey));

    wnsprintf(lpszResult, cchResult, lpszKey, szSubkey);
}

#define c_szWallpaper  REG_VAL_GENERAL_WALLPAPER
void RemoveDefaultWallpaper()
{
    // Read the Wallpaper from the Old location.
    TCHAR   szOldWallpaper[MAX_PATH];
    DWORD dwType;
    DWORD dwSize = SIZEOF(szOldWallpaper);
    if (SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_DESKTOP, c_szWallpaper, &dwType, szOldWallpaper, &dwSize) != ERROR_SUCCESS)
        szOldWallpaper[0] = TEXT('\0');

    // Read the wallpaper style
    DWORD dwWallpaperStyle = GetIntFromReg(HKEY_CURRENT_USER, REGSTR_PATH_DESKTOP, REG_VAL_GENERAL_TILEWALLPAPER, WPSTYLE_TILE);

    TCHAR szDeskcomp[MAX_PATH];
    GetRegLocation(szDeskcomp, ARRAYSIZE(szDeskcomp), REG_DESKCOMP_GENERAL, NULL);

    // Set the old wallpaper into the new location.
    SHSetValue(HKEY_CURRENT_USER, szDeskcomp,
        c_szWallpaper, REG_SZ, (LPBYTE)szOldWallpaper, SIZEOF(szOldWallpaper));


//  98/08/14 vtan: This used to write out a REG_DWORD. It should've
//  written out a REG_SZ.

    TCHAR   szWallpaperStyle[2];

    (TCHAR*)StrCpyN(szWallpaperStyle, TEXT("0"), ARRAYSIZE(szWallpaperStyle));
    szWallpaperStyle[0] += static_cast<TCHAR>(dwWallpaperStyle & WPSTYLE_MAX);

    // Set the old wallpaper style into the new location.
    SHSetValue(HKEY_CURRENT_USER, szDeskcomp,
        REG_VAL_GENERAL_TILEWALLPAPER, REG_SZ, (LPBYTE)szWallpaperStyle, SIZEOF(szWallpaperStyle));

//  98/08/14 vtan #196226: Moved the create instance of IActiveDesktop
//  to here from entry of the function. When the instance is created the
//  registry information is cached in the member variable data. The
//  registry information for the wallpaper is then changed behind the
//  object instance and ApplyChanges() is called on STALE information.
//  By deferring the object instantiation to after the registry changes
//  the changes are applied correctly.

    IActiveDesktop * piad;
    HRESULT hres = CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER, IID_IActiveDesktop, (LPVOID*)&piad);
    if (SUCCEEDED(hres))
    {
        piad->ApplyChanges(AD_APPLY_FORCE | AD_APPLY_HTMLGEN | AD_APPLY_REFRESH);
        piad->Release();
    }
}


HRESULT GetTargetFrame(IUnknown* _punkSite, BSTR bstrFrame, IWebBrowser2** ppunkTargetFrame)
{
    IWebBrowser2 * pwb2;
    HRESULT        hr = GetTopLevelBrowser(_punkSite, &pwb2);

    *ppunkTargetFrame = NULL;

    if (SUCCEEDED(hr))
    {
        ITargetFrame2 * pOurTargetFrame;
        IUnknown      * punkTargetFrame = NULL;

        // See if there is an existing frame with the specified target name
        hr = TargetQueryService(pwb2, IID_PPV_ARG(ITargetFrame2, &pOurTargetFrame));
        
        if (SUCCEEDED(hr))
        {
            hr = pOurTargetFrame->FindFrame(bstrFrame, FINDFRAME_JUSTTESTEXISTENCE, &punkTargetFrame);
            if (SUCCEEDED(hr) && punkTargetFrame)
            {
                // yes, we found a frame with that name.  QI for the automation
                // interface on that frame.
                hr = punkTargetFrame->QueryInterface(IID_PPV_ARG(IWebBrowser2, ppunkTargetFrame));
                punkTargetFrame->Release();
            }
            pOurTargetFrame->Release();
        }
        pwb2->Release();
    }
    return hr;
}

// NavigateAndFind
// 1. navigate the specified target frame to the specified url
// 2. set _fWaitingToFindText so that on DocumentComplete, 
//    mimic the find dialog and select/highlight the specified text
STDMETHODIMP CShellUIHelper::NavigateAndFind(BSTR URL, BSTR strQuery, VARIANT* varTargetFrame)
{
    HRESULT        hr;
    IWebBrowser2 * pwb2         = NULL;
    BSTR           bstrFrameSrc = NULL;

    if (_bstrQuery)
        SysFreeString(_bstrQuery);
    _bstrQuery = SysAllocString(strQuery);
     
    BSTR bstrTemp = NULL;
    if (!IsSpecialUrl(URL))
    {
        hr = S_OK;
    }
    else
    {
        bstrTemp = SysAllocString(URL);
        if (bstrTemp)
        {
            hr = WrapSpecialUrl(&bstrTemp);
            if (SUCCEEDED(hr))
                URL = bstrTemp;
        }
        else
            hr = E_OUTOFMEMORY;
    }
        
    // Security:  Don't allow javascript on one web page
    //            to automatically execute in the zone
    //            of the target frame URL.
    if (SUCCEEDED(hr) && varTargetFrame)
    {
        hr = GetTargetFrame(_punkSite, varTargetFrame->bstrVal, &pwb2); // GetTargetFrame can return NULL pwb2 on S_OK (when bstrVal is "_blank")
        if (SUCCEEDED(hr) && pwb2)
        {
            hr = pwb2->get_LocationURL(&bstrFrameSrc);
            
            if (SUCCEEDED(hr))
            {
                IHTMLDocument2 *pHtmlDoc;
                if (SUCCEEDED(GetHTMLDoc2(_punkSite, &pHtmlDoc)))
                {
                    BSTR bstrDocUrl;
                    if (SUCCEEDED(pHtmlDoc->get_URL(&bstrDocUrl)))
                    {
                        hr = AccessAllowed(bstrDocUrl, bstrFrameSrc) ? S_OK : E_ACCESSDENIED;

                        if(!IsSpecialUrl(URL) && hr == E_ACCESSDENIED)
                        {
                            // For non-script navigates check policy
                            DWORD dwPolicy = 0;
                            DWORD dwContext = 0;
                            if( SUCCEEDED(ZoneCheckUrlEx(bstrDocUrl, &dwPolicy, sizeof(dwPolicy), &dwContext, 
                                    sizeof(dwContext), URLACTION_HTML_SUBFRAME_NAVIGATE, 0, NULL)) && 
                                    GetUrlPolicyPermissions(dwPolicy) == URLPOLICY_ALLOW) 
                            {
                                hr = S_OK;
                            }
                        }
                        SysFreeString(bstrDocUrl);
                    }
                    pHtmlDoc->Release();
                }
                SysFreeString(bstrFrameSrc);
            }
            pwb2->Release();
            pwb2 = NULL;
        }
    }

    // Don't allow access to MyComputer zone from non-MyComputer zone.
    if (SUCCEEDED(hr) && !CanNavigateToUrlWithLocalMachineCheck(_punkSite, URL))
        hr = E_ACCESSDENIED;

    if (SUCCEEDED(hr))
        hr = GetTopLevelBrowser(_punkSite, &pwb2);

    if (SUCCEEDED(hr))
    {
        _fWaitingToFindText = TRUE;
        pwb2->Navigate(URL, PVAREMPTY, varTargetFrame, PVAREMPTY, PVAREMPTY);
        pwb2->Release();
    }

    if (bstrTemp)
        SysFreeString(bstrTemp);
        
    //script runtime error protection
    return SUCCEEDED(hr) ? S_OK : S_FALSE;
}


// AutoScan
//
// Takes the search string and tries to navigate to www.%s.com, www.%s.org etc.  If all of
// these fail then we navigate to pvarTargetFrame.
//
STDMETHODIMP CShellUIHelper::AutoScan
(
    BSTR strSearch,             // String to autoscan
    BSTR strFailureUrl,         // url to display is search fails
    VARIANT* pvarTargetFrame    // [optional] target frame
)
{
    HRESULT hr = E_FAIL;
    IWebBrowser2* pwb2;

    // Don't bother autoscanning if there are extended characters in the search string
    if (!HasExtendedChar(strSearch))
    {
        //first, check to see if the url is trying to spoof /1 security
        BSTR bstrTemp = NULL;
        if (IsSpecialUrl(strFailureUrl))
        {
            bstrTemp = SysAllocString(strFailureUrl);
            if (bstrTemp)
            {
                hr = WrapSpecialUrl(&bstrTemp);
                if (SUCCEEDED(hr))
                    strFailureUrl = bstrTemp;
            }
            else
                hr = E_OUTOFMEMORY;
        }
        else
        {
            hr = S_OK;
        }

        if (SUCCEEDED(hr))
        {
            LPWSTR pszTarget = (pvarTargetFrame && pvarTargetFrame->vt == VT_BSTR && pvarTargetFrame->bstrVal) ?
                                    pvarTargetFrame->bstrVal : L"_main";

            hr = GetTargetFrame(_punkSite, pszTarget, &pwb2); // GetTargetFrame can return NULL pwb2 on S_OK (when bstrVal is "_blank")
            if (!pwb2)
                hr = E_FAIL;
            if (SUCCEEDED(hr))
            {
                BSTR bstrFrameSrc = NULL;               
                
                hr = pwb2->get_LocationURL(&bstrFrameSrc);
                
                if (SUCCEEDED(hr))
                {
                    IHTMLDocument2 *pHtmlDoc;
                    if (SUCCEEDED(GetHTMLDoc2(_punkSite, &pHtmlDoc)))
                    {
                        BSTR bstrDocUrl;
                        if (SUCCEEDED(pHtmlDoc->get_URL(&bstrDocUrl)))
                        {
                            hr = AccessAllowed(bstrDocUrl, bstrFrameSrc) ? S_OK : E_ACCESSDENIED;
                            SysFreeString(bstrDocUrl);
                        }
                        pHtmlDoc->Release();
                    }
                    SysFreeString(bstrFrameSrc);
                }

                // Don't allow access to MyComputer zone from non-MyComputer zone.
                if (SUCCEEDED(hr) && !CanNavigateToUrlWithLocalMachineCheck(_punkSite, strFailureUrl))
                    hr = E_ACCESSDENIED;

                if (SUCCEEDED(hr))
                {
                    
                    // We don't want to navigate to strSearch.  Start searching using the 
                    // first autoscan substitution.
                    WCHAR szFormat[MAX_PATH];
                    WCHAR szUrl[MAX_URL_STRING];
                    if (GetSearchFormatString(1, szFormat, sizeof(szFormat)) != ERROR_SUCCESS)
                    {
                        hr = E_FAIL;
                    }
                    else
                    {
                        wnsprintf(szUrl, ARRAYSIZE(szUrl), szFormat, strSearch);
                        BSTRBLOB bstrBlob;
                        bstrBlob.cbSize = lstrlenW(szUrl) * sizeof(WCHAR);
                        bstrBlob.pData = (BYTE*)szUrl;
                        BSTR bstrUrl = (BSTR)bstrBlob.pData;
                        
                        // Save the original search string for autoscanning.  Normally
                        // this come from the addressbar, but in our case we store it as
                        // a property
                        VARIANT v;
                        VariantInit (&v);
                        
                        v.vt = VT_BSTR;
                        v.bstrVal = strSearch;
                        pwb2->PutProperty((BSTR)s_sstrSearch.wsz, v);
                        
                        // Save the error page in case the scan fails
                        v.vt = VT_BSTR;
                        v.bstrVal = strFailureUrl;
                        pwb2->PutProperty((BSTR)s_sstrFailureUrl.wsz, v);
                        
                        // Navigate with autosearch enabled
                        VARIANT vFlags;
                        vFlags.vt = VT_I4;
                        vFlags.lVal = navAllowAutosearch;
                        
                        pwb2->Navigate(bstrUrl, &vFlags, pvarTargetFrame, PVAREMPTY, PVAREMPTY);
                    }
                }
                pwb2->Release();
            }
        }
        if (bstrTemp)
            SysFreeString(bstrTemp);
    }

    //script runtime error protection
    return SUCCEEDED(hr) ? S_OK : S_FALSE;
}

typedef HRESULT (*PFNSHOWBROWSERUI)(IUnknown *punkSite, HWND hwnd, VARIANT *pvarIn, VARIANT *pvarOut);

typedef void (*PFNOPENLANGUAGEDIALOG)(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);

HRESULT ShowLanguageDialog(IUnknown *punkSite, HWND hwnd, VARIANT *pvarIn, VARIANT *pvarOut)
{
    HRESULT hr = E_FAIL;
    HINSTANCE hInstInetcpl = LoadLibrary(TEXT("inetcpl.cpl"));

    if (hInstInetcpl)
    {
        
        PFNOPENLANGUAGEDIALOG pfnOpenLanguageDialog = 
            (PFNOPENLANGUAGEDIALOG)GetProcAddress( hInstInetcpl, "OpenLanguageDialog" );

        if (pfnOpenLanguageDialog)
        {
            pfnOpenLanguageDialog(hwnd, NULL, NULL, SW_SHOW);
            hr = S_OK;
        }

        FreeLibrary(hInstInetcpl);
    }

    return hr;
}

HRESULT ShowOrganizeFavorites(IUnknown *punkSite, HWND hwnd, VARIANT *pvarIn, VARIANT *pvarOut)
{
    return DoOrganizeFavDlgW(hwnd, NULL) ? S_OK : E_FAIL;
}

HRESULT ShowPrivacySettings(IUnknown *punkSite, HWND hwnd, VARIANT *pvarIn, VARIANT *pvarOut)
{
    LaunchPrivacySettings(hwnd);

    return S_OK;
}

HRESULT ShowProgramAccessAndDefaults(IUnknown *punkSite, HWND hwnd, VARIANT *pvarIn, VARIANT *pvarOut)
{
    HRESULT hr;

    const WCHAR c_szControlExe[] = L"control.exe";
    WCHAR szControlPath[MAX_PATH];

    UINT nLen = GetSystemDirectory(szControlPath, ARRAYSIZE(szControlPath));

    //  This accounts for needing a backslash after the sysdir
    if ((nLen + ARRAYSIZE(c_szControlExe)) < ARRAYSIZE(szControlPath))
    {
        PathAppend(szControlPath, c_szControlExe);

        HINSTANCE hInst = ShellExecute(NULL, NULL, szControlPath, L"appwiz.cpl,,3", NULL, SW_SHOWNORMAL);

        if (hInst > (HINSTANCE)HINSTANCE_ERROR)
        {
            hr = S_OK;
        }
        else
        {
            hr = HRESULT_FROM_WIN32(PtrToInt(hInst));
        }
    }
    else
    {
        hr = E_OUTOFMEMORY;
    }

    return hr;
}

struct BROWSERUI_MAP
{
    LPWSTR pwszName;
    PFNSHOWBROWSERUI pfnShowBrowserUI;
};

const BROWSERUI_MAP s_browserUIMap[] =
{
    { L"LanguageDialog",            ShowLanguageDialog              },
    { L"OrganizeFavorites",         ShowOrganizeFavorites           },
    { L"PrivacySettings",           ShowPrivacySettings             },
    { L"ProgramAccessAndDefaults",  ShowProgramAccessAndDefaults    }
};

STDMETHODIMP CShellUIHelper::ShowBrowserUI(BSTR bstrName, VARIANT *pvarIn, VARIANT *pvarOut)
{
    HRESULT hr = E_FAIL;

    for (int i = 0; i < ARRAYSIZE(s_browserUIMap); i++)
    {
        if (pvarOut)
        {
            VariantInit(pvarOut);
        }
        
        if (0 == StrCmpIW(s_browserUIMap[i].pwszName, bstrName))
        {
            hr = s_browserUIMap[i].pfnShowBrowserUI(_punkSite, _GetOwnerWindow(), pvarIn, pvarOut);
        }
    }

    return S_OK == hr ? S_OK : S_FALSE;
}


// the find dialog does the following:
// 
// rng = document.body.createTextRange();
// if (rng.findText("Find this text"))
//     rng.select();
STDMETHODIMP CShellUIHelper::_DoFindOnPage(IDispatch* pdisp)
{
    HRESULT           hr;
    IWebBrowser2*     pwb2;
    IDispatch*        pdispDocument;
    IHTMLDocument2*   pDocument;
    IHTMLElement*     pBody;
    IHTMLBodyElement* pBodyElement;
    IHTMLTxtRange*    pRange;

    ASSERT(pdisp);
    if (!pdisp)
        return E_FAIL;

    hr = pdisp->QueryInterface(IID_IWebBrowser2, (void**)&pwb2);

    if (SUCCEEDED(hr))
    {
        hr = pwb2->get_Document(&pdispDocument);

        if (SUCCEEDED(hr) && (NULL != pdispDocument))
        {
            hr = pdispDocument->QueryInterface(IID_IHTMLDocument2, (void**)&pDocument);
            
            if (SUCCEEDED(hr))
            {
                hr = pDocument->get_body(&pBody);
                
                if (SUCCEEDED(hr) && (NULL != pBody))
                {
                    hr = pBody->QueryInterface(IID_IHTMLBodyElement, (void**)&pBodyElement);

                    if (SUCCEEDED(hr))
                    {
                        hr = pBodyElement->createTextRange(&pRange);

                        if (SUCCEEDED(hr) && (NULL != pRange))
                        {
                            VARIANT_BOOL vbfFoundText;
                            
                            hr = pRange->findText(_bstrQuery, 1000000, 0, &vbfFoundText);

                            if (SUCCEEDED(hr) && (vbfFoundText == VARIANT_TRUE))
                            {
                                hr = pRange->select();
                            }
                            
                            pRange->Release();
                        }
                        pBodyElement->Release();
                    }
                    pBody->Release();
                }
                pDocument->Release();
            }
            pdispDocument->Release();
        }
        pwb2->Release();
    }
    return hr;
}

//
// Launch the favorites import/export wizard
//
STDMETHODIMP CShellUIHelper::ImportExportFavorites(VARIANT_BOOL fImport, BSTR strImpExpPath) 
{
    //don't allow to import/export to folders other than Favorites from OM
    DoImportOrExport(fImport==VARIANT_TRUE, NULL, (LPCWSTR)strImpExpPath, TRUE);
    return S_OK;
}

//
// Save the form data via intelliforms
//
STDMETHODIMP CShellUIHelper::AutoCompleteSaveForm(VARIANT *Form)
{
    HRESULT hrRet = S_FALSE;

    IHTMLDocument2 *pDoc2=NULL;

    GetHTMLDoc2(_punkSite, &pDoc2);
    
    if (pDoc2)
    {
        hrRet = IntelliFormsSaveForm(pDoc2, Form);
        pDoc2->Release();
    }

    return hrRet;
}

//
// Attach intelliforms to this document
//
STDMETHODIMP CShellUIHelper::AutoCompleteAttach(VARIANT *Reserved)
{
    HRESULT hr=E_FAIL;

    if (_pvIntelliForms == NULL)
    {
        IHTMLDocument2 *pDoc2=NULL;

        GetHTMLDoc2(_punkSite, &pDoc2);

        if (pDoc2)
        {
            hr = S_OK;
            AttachIntelliForms(NULL, _GetOwnerWindow(), pDoc2, &_pvIntelliForms);
            pDoc2->Release();
        }
    }

    return SUCCEEDED(hr) ? S_OK : S_FALSE;
}