Windows2003-3790/shell/shdocvw/basesb.cpp
2020-09-30 16:53:55 +02:00

9148 lines
283 KiB
C++

#include "priv.h"
#include <mluisupp.h>
// stuff that should be turned off while in here, but on when
// it goes to commonsb or shbrows2.cpp
#define IN_BASESB2
#ifdef IN_BASESB2
#define _fFullScreen FALSE
#endif
#include "sccls.h"
#include <idhidden.h>
#include "basesb.h"
#include "iedde.h"
#include "bindcb.h"
#include "resource.h"
#include "security.h"
#include <urlmon.h>
#include "favorite.h"
#include "uemapp.h"
#include <varutil.h>
#include "interned.h" // IHTMLPrivateWindow
#ifdef FEATURE_PICS
#include <ratings.h>
#include <ratingsp.h>
#endif
#include "msiehost.h"
#include "dhuihand.h"
#include "mypics.h"
#include "airesize.h"
#define DM_ACCELERATOR 0
#define DM_WEBCHECKDRT 0
#define DM_COCREATEHTML 0
#define DM_CACHEOLESERVER 0
#define DM_DOCCP 0
#define DM_PICS 0
#define DM_SSL 0
#define DM_MISC DM_TRACE // misc/tmp
// get at defs to run a privacy dialog box
#include "privacyui.hpp"
//
// Remove this #include by defining _bbd._pauto as IWebBrowserApp, just like
// Explorer.exe.
//
#include "hlframe.h"
extern IUnknown* ClassHolder_Create(const CLSID* pclsid);
extern HRESULT VariantClearLazy(VARIANTARG *pvarg);
#define WMC_ASYNCOPERATION (WMC_RESERVED_FIRST + 0x0000)
#define ISSPACE(ch) (((ch) == 32) || ((unsigned)((ch) - 9)) <= 13 - 9)
#define IDT_STARTING_APP_TIMER 9001 // trying to pick a unique number... (THIS IS BOGUS! FIX THIS!)
#define STARTING_APP_DURATION 2500
UINT g_uMsgFileOpened = (UINT)-1; // Registered window message for file opens
// _uActionQueued of WMC_ACYNCOPERATION specifies the operation.
#define ASYNCOP_NIL 0
#define ASYNCOP_GOTO 1
#define ASYNCOP_ACTIVATEPENDING 2
#define ASYNCOP_CANCELNAVIGATION 3
void IEInitializeClassFactoryObject(IUnknown* punkAuto);
BOOL ParseRefreshContent(LPWSTR pwzContent,
UINT * puiDelay, LPWSTR pwzUrlBuf, UINT cchUrlBuf);
#define VALIDATEPENDINGSTATE() ASSERT((_bbd._psvPending && _bbd._psfPending) || (!_bbd._psvPending && !_bbd._psfPending))
#define DM_HTTPEQUIV TF_SHDNAVIGATE
#define DM_NAV TF_SHDNAVIGATE
#define DM_ZONE TF_SHDNAVIGATE
#define DM_IEDDE DM_TRACE
#define DM_CANCELMODE 0
#define DM_UIWINDOW 0
#define DM_ENABLEMODELESS TF_SHDNAVIGATE
#define DM_EXPLORERMENU 0
#define DM_BACKFORWARD 0
#define DM_PROTOCOL 0
#define DM_ITBAR 0
#define DM_STARTUP 0
#define DM_AUTOLIFE 0
#define DM_PALETTE 0
#define DM_PERSIST 0 // trace IPS::Load, ::Save, etc.
#define DM_VIEWSTREAM DM_TRACE
#define DM_FOCUS 0
#define DM_FOCUS2 0 // like DM_FOCUS, but verbose
// these two MUST be in order because we peek them together
STDAPI SafeGetItemObject(IShellView *psv, UINT uItem, REFIID riid, void **ppv);
extern HRESULT TargetQueryService(IUnknown *punk, REFIID riid, void **ppvObj);
HRESULT CreateTravelLog(ITravelLog **pptl);
HRESULT CreatePublicTravelLog(IBrowserService *pbs, ITravelLogEx *ptlx, ITravelLogStg **pptlstg);
#ifdef MESSAGEFILTER
/*
* CMsgFilter - implementation of IMessageFilter
*
* Used to help distribute WM_TIMER messages during OLE operations when
* we are busy. If we don't install the CoRegisterMessageFilter
* then OLE can PeekMessage(PM_NOREMOVE) the timers such that they pile up
* and fill the message queue.
*
*/
class CMsgFilter : public IMessageFilter {
public:
// *** IUnknown methods ***
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj)
{
// This interface doesn't get QI'ed.
ASSERT(FALSE);
return E_NOINTERFACE;
};
STDMETHODIMP_(ULONG) AddRef(void) { return ++_cRef; };
STDMETHODIMP_(ULONG) Release(void) { ASSERT(_cRef > 0);
_cRef--;
if (_cRef > 0)
return _cRef;
delete this;
return 0;
};
// *** IMessageFilter specific methods ***
STDMETHODIMP_(DWORD) HandleInComingCall(
IN DWORD dwCallType,
IN HTASK htaskCaller,
IN DWORD dwTickCount,
IN LPINTERFACEINFO lpInterfaceInfo)
{
if (_lpMFOld)
return (_lpMFOld->HandleInComingCall(dwCallType, htaskCaller, dwTickCount, lpInterfaceInfo));
else
return SERVERCALL_ISHANDLED;
};
STDMETHODIMP_(DWORD) RetryRejectedCall(
IN HTASK htaskCallee,
IN DWORD dwTickCount,
IN DWORD dwRejectType)
{
if (_lpMFOld)
return (_lpMFOld->RetryRejectedCall(htaskCallee, dwTickCount, dwRejectType));
else
return 0xffffffff;
};
STDMETHODIMP_(DWORD) MessagePending(
IN HTASK htaskCallee,
IN DWORD dwTickCount,
IN DWORD dwPendingType)
{
DWORD dw;
MSG msg;
// We can get released during the DispatchMessage call...
// If it's our last release, we'll free ourselves and
// fault when we dereference _lpMFOld... Make sure this
// doesn't happen by increasing our refcount.
//
AddRef();
while (PeekMessage(&msg, NULL, WM_TIMER, WM_TIMER, PM_REMOVE))
{
#ifndef DISPATCH_IETIMERS
TCHAR szClassName[40];
GetClassName(msg.hwnd, szClassName, ARRAYSIZE(szClassName));
if (StrCmpI(szClassName, TEXT("Internet Explorer_Hidden")) != 0)
{
#endif
DispatchMessage(&msg);
#ifndef DISPATCH_IETIMERS
}
#endif
}
if (_lpMFOld)
dw = (_lpMFOld->MessagePending(htaskCallee, dwTickCount, dwPendingType));
else
dw = PENDINGMSG_WAITDEFPROCESS;
Release();
return(dw);
};
CMsgFilter() : _cRef(1)
{
ASSERT(_lpMFOld == NULL);
};
BOOL Initialize()
{
return (CoRegisterMessageFilter((LPMESSAGEFILTER)this, &_lpMFOld) != S_FALSE);
};
void UnInitialize()
{
CoRegisterMessageFilter(_lpMFOld, NULL);
// we shouldn't ever get called again, but after 30 minutes
// of automation driving we once hit a function call above
// and we dereferenced this old pointer and page faulted.
ATOMICRELEASE(_lpMFOld);
};
protected:
int _cRef;
LPMESSAGEFILTER _lpMFOld;
};
#endif
void IEHard_NavWarning(HWND hwnd, BOOL fIE);
BOOL IEHard_ShowOnNavigateComplete(void);
BOOL IEHard_HostedInIE(IUnknown* punk);
//--------------------------------------------------------------------------
// Detecting a memory leak
//--------------------------------------------------------------------------
HRESULT GetTopFrameOptions(IServiceProvider * psp, DWORD * pdwOptions)
{
IServiceProvider * pspTop;
HRESULT hres = psp->QueryService(SID_STopLevelBrowser, IID_PPV_ARG(IServiceProvider, &pspTop));
if (SUCCEEDED(hres))
{
ITargetFrame2 *ptgf;
hres = pspTop->QueryService(SID_SContainerDispatch, IID_PPV_ARG(ITargetFrame2, &ptgf));
if (SUCCEEDED(hres))
{
hres = ptgf->GetFrameOptions(pdwOptions);
ptgf->Release();
}
pspTop->Release();
}
return hres;
}
void UpdateDesktopComponentName(LPCWSTR lpcwszURL, LPCWSTR lpcwszName)
{
IActiveDesktop * piad;
if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_ActiveDesktop, NULL, IID_PPV_ARG(IActiveDesktop, &piad))))
{
COMPONENT comp;
comp.dwSize = SIZEOF(comp);
if (SUCCEEDED(piad->GetDesktopItemBySource(lpcwszURL, &comp, 0)) && !comp.wszFriendlyName[0])
{
StrCpyNW(comp.wszFriendlyName, lpcwszName, ARRAYSIZE(comp.wszFriendlyName));
piad->ModifyDesktopItem(&comp, COMP_ELEM_FRIENDLYNAME);
piad->ApplyChanges(AD_APPLY_SAVE);
}
piad->Release();
}
}
HRESULT CBaseBrowser2::_Initialize(HWND hwnd, IUnknown* pauto)
{
if (pauto)
{
pauto->AddRef();
}
else
{
CIEFrameAuto_CreateInstance(NULL, &pauto);
}
// Grab _pauto interfaces we use throughout this code.
//
if (pauto)
{
pauto->QueryInterface(IID_PPV_ARG(IWebBrowser2, &_bbd._pautoWB2));
ASSERT(_bbd._pautoWB2);
pauto->QueryInterface(IID_PPV_ARG(IExpDispSupport, &_bbd._pautoEDS));
ASSERT(_bbd._pautoEDS);
pauto->QueryInterface(IID_PPV_ARG(IShellService, &_bbd._pautoSS));
ASSERT(_bbd._pautoSS);
pauto->QueryInterface(IID_PPV_ARG(ITargetFrame2, &_ptfrm));
ASSERT(_ptfrm);
pauto->QueryInterface(IID_PPV_ARG(IHlinkFrame, &_bbd._phlf));
ASSERT(_bbd._phlf);
IHTMLWindow2 *pWindow;
if( SUCCEEDED(GetWindowFromUnknown( pauto, &pWindow )) )
{
pWindow->QueryInterface(IID_PPV_ARG(IShellHTMLWindowSupport, &_phtmlWS));
pWindow->Release( );
}
ASSERT( _phtmlWS );
_pauto = pauto;
}
// _psbOuter?
if (NULL == _bbd._phlf)
{
Release();
return E_FAIL;
}
else
{
_SetWindow(hwnd);
return S_OK;
}
}
HRESULT CBaseBrowser2::InitializeTransitionSite()
{
return S_OK;
}
HRESULT CBaseBrowser2_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
{
CBaseBrowser2 *pbb = new CBaseBrowser2(pUnkOuter);
if (pbb)
{
*ppunk = pbb->_GetInner();
return S_OK;
}
*ppunk = NULL;
return E_OUTOFMEMORY;
}
CBaseBrowser2::CBaseBrowser2(IUnknown* punkAgg) :
CAggregatedUnknown(punkAgg),
_bptBrowser(BPT_DeferPaletteSupport)
{
TraceMsg(TF_SHDLIFE, "ctor CBaseBrowser2 %x", this);
_bbd._uActivateState = SVUIA_ACTIVATE_FOCUS;
_InitComCtl32();
ASSERT(S_FALSE == _DisableModeless());
ASSERT(_cp == CP_ACP);
ASSERT(!_fNoTopLevelBrowser);
ASSERT(!_dwDocFlags);
_QueryOuterInterface(IID_PPV_ARG(IBrowserService2, &_pbsOuter));
_QueryOuterInterface(IID_PPV_ARG(IBrowserService3, &_pbsOuter3));
_QueryOuterInterface(IID_PPV_ARG(IShellBrowser, &_psbOuter));
_QueryOuterInterface(IID_PPV_ARG(IServiceProvider, &_pspOuter));
// The following are intercepted by CCommonBrowser, but we don't call 'em
//_QueryOuterInterface(IID_PPV_ARG(IOleCommandTarget, &_pctOuter));
//_QueryOuterInterface(IID_PPV_ARG(IInputObjectSite, &_piosOuter));
_dwStartingAppTick = 0;
}
CBaseBrowser2::~CBaseBrowser2()
{
TraceMsg(TF_SHDLIFE, "dtor CBaseBrowser2 %x", this);
// Are we releasing these too early (i.e. does anything in the
// rest of this func rely on having the 'vtables' still be valid?)
RELEASEOUTERINTERFACE(_pbsOuter);
RELEASEOUTERINTERFACE(_pbsOuter3);
RELEASEOUTERINTERFACE(_psbOuter);
RELEASEOUTERINTERFACE(_pspOuter);
// The following are intercepted by CCommonBrowser, but we don't call 'em
//RELEASEOUTERINTERFACE(_pctOuter);
//RELEASEOUTERINTERFACE(_piosOuter);
ASSERT(_hdpaDLM == NULL); // subclass must free it.
// finish tracking here
if (_ptracking)
{
delete _ptracking;
_ptracking = NULL;
}
//
// Notes: Unlike IE3.0, we release CIEFrameAuto pointers here.
//
ATOMICRELEASE(_bbd._pautoWB2);
ATOMICRELEASE(_bbd._pautoEDS);
ATOMICRELEASE(_bbd._pautoSS);
ATOMICRELEASE(_phtmlWS);
ATOMICRELEASE(_bbd._phlf);
ATOMICRELEASE(_ptfrm);
ATOMICRELEASE(_pauto);
ATOMICRELEASE(_punkSFHistory);
// clean up our palette by simulating a switch out of palettized mode
_bptBrowser = BPT_NotPalettized;
_QueryNewPalette();
ASSERT(!_bbd._phlf);
ASSERT(!_ptfrm);
ASSERT(S_FALSE == _DisableModeless());
ASSERT(_bbd._hwnd==NULL);
ATOMICRELEASE(_pact);
ATOMICRELEASE(_pIUrlHistoryStg);
ATOMICRELEASE(_pITravelLogStg);
ATOMICRELEASE(_poleHistory);
ATOMICRELEASE(_pstmHistory);
ATOMICRELEASE(_bbd._ptl);
ATOMICRELEASE(_pHTMLDocument);
ATOMICRELEASE(_pphHistory);
ATOMICRELEASE(_pDispViewLinkedWebOCFrame);
#ifdef MESSAGEFILTER
if (_lpMF)
{
IMessageFilter* lpMF = _lpMF;
_lpMF = NULL;
((CMsgFilter *)lpMF)->UnInitialize();
EVAL(lpMF->Release() == 0);
}
#endif
// This is created during FileCabinet_CreateViewWindow2
CShellViews_Delete(&_fldBase._cViews);
// If the class factory object has been cached, unlock it and release.
if (_pcfHTML)
{
_pcfHTML->LockServer(FALSE);
_pcfHTML->Release();
}
ATOMICRELEASE(_pToolbarExt);
}
HRESULT CBaseBrowser2::v_InternalQueryInterface(REFIID riid, void **ppvObj)
{
static const QITAB qit[] = {
QITABENT(CBaseBrowser2, IShellBrowser), // IID_IShellBrowser
QITABENTMULTI(CBaseBrowser2, IOleWindow, IShellBrowser), // IID_IOleWindow
QITABENT(CBaseBrowser2, IOleInPlaceUIWindow), // IID_IOleInPlaceUIWindow
QITABENT(CBaseBrowser2, IOleCommandTarget), // IID_IOleCommandTarget
QITABENT(CBaseBrowser2, IDropTarget), // IID_IDropTarget
QITABENTMULTI(CBaseBrowser2, IBrowserService, IBrowserService3), // IID_IBrowserService
QITABENTMULTI(CBaseBrowser2, IBrowserService2, IBrowserService3), // IID_IBrowserService2
QITABENT(CBaseBrowser2, IBrowserService3), // IID_IBrowserService3
QITABENT(CBaseBrowser2, IServiceProvider), // IID_IServiceProvider
QITABENT(CBaseBrowser2, IOleContainer), // IID_IOleContainer
QITABENT(CBaseBrowser2, IAdviseSink), // IID_IAdviseSink
QITABENT(CBaseBrowser2, IInputObjectSite), // IID_IInputObjectSite
QITABENT(CBaseBrowser2, IDocNavigate), // IID_IDocNavigate
QITABENT(CBaseBrowser2, IPersistHistory), // IID_IPersistHistory
QITABENT(CBaseBrowser2, IInternetSecurityMgrSite), // IID_IInternetSecurityMgrSite
QITABENT(CBaseBrowser2, IVersionHost), // IID_IVersionHost
QITABENT(CBaseBrowser2, IProfferService), // IID_IProfferService
QITABENT(CBaseBrowser2, ITravelLogClient), // IID_ITravelLogClient
QITABENT(CBaseBrowser2, ITravelLogClient2), // IID_ITravelLogClient2
QITABENTMULTI(CBaseBrowser2, ITridentService, ITridentService2), // IID_ITridentService
QITABENT(CBaseBrowser2, ITridentService2), // IID_ITridentService2
QITABENT(CBaseBrowser2, IInitViewLinkedWebOC), // IID_IInitViewLinkedWebOC
QITABENT(CBaseBrowser2, INotifyAppStart), // IID_INotifyAppStart
{ 0 },
};
return QISearch(this, qit, riid, ppvObj);
}
BOOL CBaseBrowser2::_IsViewMSHTML(IShellView * psv)
{
BOOL fIsMSHTML = FALSE;
if (psv)
{
IPersist *pPersist = NULL;
HRESULT hres = SafeGetItemObject(psv, SVGIO_BACKGROUND, IID_PPV_ARG(IPersist, &pPersist));
if (SUCCEEDED(hres) && (pPersist != NULL))
{
CLSID clsid;
hres = pPersist->GetClassID(&clsid);
if (SUCCEEDED(hres) && IsEqualGUID(clsid, CLSID_HTMLDocument))
fIsMSHTML = TRUE;
pPersist->Release();
}
}
return fIsMSHTML;
}
HRESULT CBaseBrowser2::ReleaseShellView()
{
// We're seeing some reentrancy here. If _cRefUIActivateSV is non-zero, it means we're
// in the middle of UIActivating the shell view.
//
if (_cRefUIActivateSV)
{
TraceMsg(TF_WARNING,
"CBB(%x)::ReleaseShellView _cRefUIActivateSV(%d)!=0 _bbd._psv=%x ABORTING",
this, _cRefUIActivateSV, _bbd._psv);
return S_OK;
}
BOOL fViewObjectChanged = FALSE;
VALIDATEPENDINGSTATE();
TraceMsg(DM_NAV, "CBaseBrowser2(%x)::ReleaseShellView(%x)", this, _bbd._psv);
ATOMICRELEASE(_pdtView);
if (_bbd._psv)
{
// Disable navigation while we are UIDeactivating/DestroyWindowing
// the IShellView. Some OC/DocObject in it (such as ActiveMovie)
// might have a message loop long enough to cause some reentrancy.
_psbOuter->EnableModelessSB(FALSE);
// Tell the shell's HTML window we are releasing the document.
if (_phtmlWS)
{
_phtmlWS->ViewReleased();
}
//
// We need to cancel the menu mode so that unmerging menu won't
// destroy the menu we are dealing with (which caused GPF in USER).
// DocObject needs to do appropriate thing for context menus.
// (02-03-96 SatoNa)
//
HWND hwndCapture = GetCapture();
TraceMsg(DM_CANCELMODE, "ReleaseShellView hwndCapture=%x _bbd._hwnd=%x", hwndCapture, _bbd._hwnd);
if (hwndCapture && hwndCapture==_bbd._hwnd)
{
TraceMsg(DM_CANCELMODE, "ReleaseShellView Sending WM_CANCELMODE");
SendMessage(_bbd._hwnd, WM_CANCELMODE, 0, 0);
}
//
// We don't want to resize the previous view window while we are
// navigating away from it.
//
TraceMsg(TF_SHDUIACTIVATE, "CSB::ReleaseShellView setting _fDontResizeView");
_fDontResizeView = TRUE;
// If the current view is still waiting for ReadyStateComplete,
// and the view we're swapping in here does not support this property,
// then we'll never go to ReadyStateComplete! Simulate it here:
//
// NOTE: ZekeL put this in _CancelNavigation which happened way too often.
// I think this is the case he was trying to fix, but I don't remember
// the bug number so I don't have a specific repro...
//
if (!_bbd._fIsViewMSHTML)
{
_fReleasingShellView = TRUE;
OnReadyStateChange(_bbd._psv, READYSTATE_COMPLETE);
_fReleasingShellView = FALSE;
}
// At one point during a LOR stress test, we got re-entered during
// this UIActivate call (some rogue 3rd-party IShellView perhaps?)
// which caused _psv to get freed, and we faulted during the unwind.
// Gaurd against this by swapping the _psv out early.
//
IShellView* psv = _bbd._psv;
_bbd._psv = NULL;
if (psv)
{
psv->UIActivate(SVUIA_DEACTIVATE);
if (_cRefUIActivateSV)
{
TraceMsg(TF_WARNING, "CBB(%x)::ReleaseShellView setting _bbd._psv = NULL (was %x) while _cRefUIActivateSV=%d",
this, psv, _cRefUIActivateSV);
}
ATOMICRELEASE(_bbd._pctView);
if (_pvo)
{
IAdviseSink *pSink;
// paranoia: only blow away the advise sink if it is still us
if (SUCCEEDED(_pvo->GetAdvise(NULL, NULL, &pSink)) && pSink)
{
if (pSink == SAFECAST(this, IAdviseSink *))
_pvo->SetAdvise(0, 0, NULL);
pSink->Release();
}
fViewObjectChanged = TRUE;
ATOMICRELEASE(_pvo);
}
psv->SaveViewState();
TraceMsg(DM_NAV, "ief NAV::%s %x",TEXT("ReleaseShellView Calling DestroyViewWindow"), psv);
psv->DestroyViewWindow();
UINT cRef = psv->Release();
TraceMsg(DM_NAV, "ief NAV::%s %x %x",TEXT("ReleaseShellView called psv->Release"), psv, cRef);
_bbd._hwndView = NULL;
TraceMsg(TF_SHDUIACTIVATE, "CSB::ReleaseShellView resetting _fDontResizeView");
_fDontResizeView = FALSE;
if (_bbd._pidlCur)
{
ILFree(_bbd._pidlCur);
_bbd._pidlCur = NULL;
}
}
_psbOuter->EnableModelessSB(TRUE);
//
// If there is any blocked async operation AND we can navigate now,
// unblock it now.
//
_MayUnblockAsyncOperation();
}
ATOMICRELEASE(_bbd._psf);
if (fViewObjectChanged)
_ViewChange(DVASPECT_CONTENT, -1);
if (_bbd._pszTitleCur)
{
LocalFree(_bbd._pszTitleCur);
_bbd._pszTitleCur = NULL;
}
// NOTES: (SatoNa)
//
// This is the best time to clean up the left-over from UI-negotiation
// from the previous DocObject. Excel 97, for some reason, takes 16
// pixels from the top (for the formula bar) when we UI-deactivate it
// by callin gIOleDocumentView::UIActivate(FALSE), which we call above.
//
SetRect(&_rcBorderDoc, 0, 0, 0, 0);
return S_OK;
}
void CBaseBrowser2::_StopCurrentView()
{
// send OLECMDID_STOP
if (_bbd._pctView) // we must check!
{
_bbd._pctView->Exec(NULL, OLECMDID_STOP, OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL);
}
}
//
// This function synchronously cancels the pending navigation if any.
//
HRESULT CBaseBrowser2::_CancelPendingNavigation(BOOL fDontReleaseState)
{
TraceMsg(TF_SHDNAVIGATE, "CBB::_CancelPendingNavigation called");
_StopAsyncOperation();
HRESULT hres = S_FALSE;
#ifndef NON_NATIVE_FRAMES
if (_bbd._psvPending)
{
if (_IsViewMSHTML(_bbd._psvPending))
{
VARIANT varStop;
V_VT(&varStop) = VT_BOOL;
V_BOOL(&varStop) = VARIANT_TRUE;
IUnknown_Exec(_bbd._psvPending, NULL, OLECMDID_STOP, 0, &varStop, NULL);
}
else
{
if (_bbd._phlf && !fDontReleaseState)
{
// release our state
_bbd._phlf->Navigate(0, NULL, NULL, NULL);
}
_CancelPendingView();
}
hres = S_OK;
}
#else
if (_bbd._psvPending)
{
if (_bbd._phlf && !fDontReleaseState)
{
// release our state
_bbd._phlf->Navigate(0, NULL, NULL, NULL);
}
_CancelPendingView();
hres = S_OK;
}
#endif
return hres;
}
void CBaseBrowser2::_SendAsyncNavigationMsg(VARIANTARG *pvarargIn)
{
LPCWSTR psz = VariantToStrCast(pvarargIn);
if (psz)
{
LPITEMIDLIST pidl;
if (EVAL(SUCCEEDED(IECreateFromPathW(psz, &pidl))))
{
_NavigateToPidlAsync(pidl, 0); // takes ownership of pidl
}
}
}
//
// NOTES: It does not cancel the pending view.
//
void CBaseBrowser2::_StopAsyncOperation(void)
{
// Don't remove posted WMC_ASYNCOPERATION message. PeekMesssage removes
// messages for children! (SatoNa)
_uActionQueued = ASYNCOP_NIL;
// Remove the pidl in the queue (single depth)
_FreeQueuedPidl(&_pidlQueued);
}
//
// This function checks if we have any asynchronous operation AND
// we no longer need to postpone. In that case, we unblock it by
// posting a WMC_ASYNCOPERATION.
//
void CBaseBrowser2::_MayUnblockAsyncOperation(void)
{
if (_uActionQueued!=ASYNCOP_NIL && _CanNavigate())
{
TraceMsg(TF_SHDNAVIGATE, "CBB::_MayUnblockAsyncOp posting WMC_ASYNCOPERATION");
PostMessage(_bbd._hwnd, WMC_ASYNCOPERATION, 0, 0);
}
}
BOOL CBaseBrowser2::_PostAsyncOperation(UINT uAction)
{
_uActionQueued = uAction;
return PostMessage(_bbd._hwnd, WMC_ASYNCOPERATION, 0, 0);
}
LRESULT CBaseBrowser2::_SendAsyncOperation(UINT uAction)
{
_uActionQueued = uAction;
return SendMessage(_bbd._hwnd, WMC_ASYNCOPERATION, 0, 0);
}
HRESULT CBaseBrowser2::_CancelPendingNavigationAsync(void)
{
TraceMsg(TF_SHDNAVIGATE, "CBB::_CancelPendingNavigationAsync called");
_StopAsyncOperation();
_PostAsyncOperation(ASYNCOP_CANCELNAVIGATION);
return S_OK;
}
HRESULT CBaseBrowser2::_CancelPendingView(void)
{
if (_bbd._psvPending)
{
TraceMsg(DM_NAV, "ief NAV::%s %x",TEXT("_CancelPendingView Calling DestroyViewWindow"), _bbd._psvPending);
_bbd._psvPending->DestroyViewWindow();
ASSERT(_bbd._psfPending);
// When cancelling a pending navigation, make sure we
// think the pending operation is _COMPLETE otherwise
// we may get stuck in a _LOADING state...
//
TraceMsg(TF_SHDNAVIGATE, "basesb(%x) Fake pending ReadyState_Complete", this);
OnReadyStateChange(_bbd._psvPending, READYSTATE_COMPLETE);
ATOMICRELEASE(_bbd._psvPending);
// Paranoia
ATOMICRELEASE(_bbd._psfPending);
_bbd._hwndViewPending = NULL;
_setDescendentNavigate(NULL);
SetNavigateState(BNS_NORMAL);
if (_bbd._pidlPending)
{
ILFree(_bbd._pidlPending);
_bbd._pidlPending = NULL;
}
if (_bbd._pszTitlePending)
{
LocalFree(_bbd._pszTitlePending);
_bbd._pszTitlePending = NULL;
}
// Pending navigation is canceled.
// since the back button works as a stop on pending navigations, we
// should check that here as well.
_pbsOuter->UpdateBackForwardState();
_NotifyCommandStateChange();
_PauseOrResumeView(_fPausedByParent);
}
return S_OK;
}
void CBaseBrowser2::_UpdateTravelLog(BOOL fForceUpdate /* = FALSE */)
{
//
// we update the travellog in two parts. first we update
// the current entry with the current state info,
// then we create a new empty entry. UpdateEntry()
// and AddEntry() need to always be in pairs, with
// identical parameters.
//
// if this navigation came from a LoadHistory, the
// _fDontAddTravelEntry will be set, and the update and
// cursor movement will have been adjusted already.
// we also want to prevent new frames from updating
// and adding stuff, so unless this is the top we
// wont add to the travellog if this is a new frame.
//
ASSERT(!(_grfHLNFPending & HLNF_CREATENOHISTORY));
ITravelLog *ptl;
GetTravelLog(&ptl);
BOOL fTopFrameBrowser = IsTopFrameBrowser(SAFECAST(this, IServiceProvider *), SAFECAST(this, IShellBrowser *));
if (ptl)
{
//
// some times we are started by another app (MSWORD usually) that has HLink
// capability. we detect this by noting that we are a new browser with an empty
// TravelLog, and then see if we can get a IHlinkBrowseContext. if this is successful,
// we should add an entry and update it immediately with the external info.
//
IHlinkBrowseContext *phlbc = NULL; // init to suppress bogus C4701 warning
BOOL fExternalNavigate = (FAILED(ptl->GetTravelEntry(SAFECAST(this, IBrowserService *), 0, NULL)) &&
fTopFrameBrowser && _bbd._phlf && SUCCEEDED(_bbd._phlf->GetBrowseContext(&phlbc)));
if (fExternalNavigate)
{
ptl->AddEntry(SAFECAST(this, IBrowserService *), FALSE);
ptl->UpdateExternal(SAFECAST(this, IBrowserService *), phlbc);
phlbc->Release();
}
else if (_bbd._psv && (fForceUpdate || !_fIsLocalAnchor || (_dwDocFlags & DOCFLAG_DOCCANNAVIGATE)))
{
ptl->UpdateEntry(SAFECAST(this, IBrowserService *), _fIsLocalAnchor); // CAST for IUnknown
}
if (!_fDontAddTravelEntry && (_bbd._psv || fTopFrameBrowser))
{
ptl->AddEntry(SAFECAST(this, IBrowserService *), _fIsLocalAnchor); // CAST for IUnknown
}
ptl->Release();
}
_fDontAddTravelEntry = FALSE;
_fIsLocalAnchor = FALSE;
}
void CBaseBrowser2::_OnNavigateComplete(LPCITEMIDLIST pidl, DWORD grfHLNF)
{
_pbsOuter->UpdateBackForwardState();
}
//// Does only the top shbrowse need this? or the top oc frame too?
HRESULT CBaseBrowser2::UpdateSecureLockIcon(int eSecureLock)
{
// only the top boy should get to set his stuff
if (!IsTopFrameBrowser(SAFECAST(this, IServiceProvider *), SAFECAST(this, IShellBrowser *)))
return S_OK;
if (eSecureLock != SECURELOCK_NOCHANGE)
_bbd._eSecureLockIcon = eSecureLock;
//
// There is no mixed Security Icon - zekel 6-AUG-97
// right now we have no icon or TT for SECURELOCK_SET_MIXED, which
// is set when the root page is secure but some of the other content
// or frames are not. some PM needs to implement, probably
// with consultation from TonyCi and DBau. by default we currently
// only show for pages that are completely secure.
//
TraceMsg(DM_SSL, "CBB:UpdateSecureLockIcon() _bbd._eSecureLockIcon = %d", _bbd._eSecureLockIcon);
//
// It looks like it doesnt matter what icon we select here,
// the status bar always shows some lock icon that was cached there earlier
// and it treats this HICON as a bool to indicat on or off - zekel - 5-DEC-97
//
HICON hicon = NULL;
TCHAR szText[MAX_TOOLTIP_STRING];
szText[0] = 0;
switch (_bbd._eSecureLockIcon)
{
case SECURELOCK_SET_UNSECURE:
case SECURELOCK_SET_MIXED:
hicon = NULL;
break;
case SECURELOCK_SET_SECUREUNKNOWNBIT:
hicon = g_hiconSSL;
break;
case SECURELOCK_SET_SECURE40BIT:
hicon = g_hiconSSL;
MLLoadString(IDS_SSL40, szText, ARRAYSIZE(szText));
break;
case SECURELOCK_SET_SECURE56BIT:
hicon = g_hiconSSL;
MLLoadString(IDS_SSL56, szText, ARRAYSIZE(szText));
break;
case SECURELOCK_SET_SECURE128BIT:
hicon = g_hiconSSL;
MLLoadString(IDS_SSL128, szText, ARRAYSIZE(szText));
break;
case SECURELOCK_SET_FORTEZZA:
hicon = g_hiconFortezza;
MLLoadString(IDS_SSL_FORTEZZA, szText, ARRAYSIZE(szText));
break;
default:
ASSERT(0);
return E_FAIL;
}
VARIANTARG var = {0};
if (_bbd._pctView && SUCCEEDED(_bbd._pctView->Exec(&CGID_Explorer, SBCMDID_GETPANE, PANE_SSL, NULL, &var))
&& V_UI4(&var) != PANE_NONE)
{
_psbOuter->SendControlMsg(FCW_STATUS, SB_SETICON, V_UI4(&var), (LPARAM)(hicon), NULL);
_psbOuter->SendControlMsg(FCW_STATUS, SB_SETTIPTEXT, V_UI4(&var), (LPARAM)(szText[0] ? szText : NULL), NULL);
// Also add the tip text as the pane's normal text. Because of the pane's size it will be clipped,
// but it will show up as a useful string in MSAA.
_psbOuter->SendControlMsg(FCW_STATUS, SB_SETTEXTW, V_UI4(&var), (LPARAM)(szText[0] ? szText : NULL), NULL);
}
return S_OK;
}
//
// Update Privacy Icon
//
HRESULT CBaseBrowser2::_UpdatePrivacyIcon(BOOL fSetState, BOOL fNewImpacted)
{
static BOOL fHelpShown = FALSE;
//
// only the top boy should get to set his stuff
//
if (!IsTopFrameBrowser(SAFECAST(this, IServiceProvider *), SAFECAST(this, IShellBrowser *)))
return S_OK;
//
// save off the privacy state
//
if(fSetState)
{
_bbd._fPrivacyImpacted = fNewImpacted;
}
HICON hicon = NULL;
TCHAR szText[MAX_TOOLTIP_STRING];
szText[0] = 0;
if(_bbd._fPrivacyImpacted)
{
hicon = g_hiconPrivacyImpact;
MLLoadString(IDS_PRIVACY_TOOLTIP, szText, ARRAYSIZE(szText));
}
if (_bbd._pctView)
{
_psbOuter->SendControlMsg(FCW_STATUS, SB_SETICON, STATUS_PANE_PRIVACY, (LPARAM)(hicon), NULL);
_psbOuter->SendControlMsg(FCW_STATUS, SB_SETTIPTEXT, STATUS_PANE_PRIVACY, (LPARAM)(szText[0] ? szText : NULL), NULL);
// Also add the tip text as the panes normal text. Because of the pane's size it will be clipped,
// but it will show up as a useful string in MSAA.
_psbOuter->SendControlMsg(FCW_STATUS, SB_SETTEXTW, STATUS_PANE_PRIVACY, (LPARAM)(szText[0] ? szText : NULL), NULL);
}
// if impacted and never shown before, show bubble toolhelp
if(FALSE == fHelpShown && _bbd._fPrivacyImpacted)
{
DWORD dwValue, dwSize;
// only do this work once
fHelpShown = TRUE;
// Check to see if we should show discovery UI
dwSize = sizeof(DWORD);
if(ERROR_SUCCESS != SHGetValueW(HKEY_CURRENT_USER,
REGSTR_PATH_INTERNET_SETTINGS,
REGSTR_VAL_PRIVDISCOVER,
NULL, &dwValue, &dwSize)
|| 0 == dwValue)
{
BOOL bStatusBarVisible = FALSE;
INT_PTR i = 1;
HRESULT hr;
// suppression setting not set, show ui if status bar is visible
IBrowserService *pbs;
hr = _pspOuter->QueryService(SID_STopFrameBrowser, IID_PPV_ARG(IBrowserService, &pbs));
if(SUCCEEDED(hr))
{
hr = pbs->IsControlWindowShown(FCW_STATUS, &bStatusBarVisible);
pbs->Release();
}
BOOL fDontShowPrivacyFirstTimeDialogAgain = FALSE;
if(SUCCEEDED(hr) && bStatusBarVisible)
{
fDontShowPrivacyFirstTimeDialogAgain = DoPrivacyFirstTimeDialog( _bbd._hwnd);
}
if( fDontShowPrivacyFirstTimeDialogAgain == TRUE)
{
dwValue = 1;
SHSetValue(HKEY_CURRENT_USER, REGSTR_PATH_INTERNET_SETTINGS, REGSTR_VAL_PRIVDISCOVER,
REG_DWORD, &dwValue, sizeof(DWORD));
}
}
}
return S_OK;
}
//
// This block of code simply prevents calling UIActivate of old
// extensions with a new SVUIA_ value.
//
HRESULT CBaseBrowser2::_UIActivateView(UINT uState)
{
if (_bbd._psv)
{
BOOL fShellView2;
IShellView2* psv2;
if (SUCCEEDED(_bbd._psv->QueryInterface(IID_PPV_ARG(IShellView2, &psv2))))
{
fShellView2 = TRUE;
psv2->Release();
}
else
{
fShellView2 = FALSE;
}
if (uState == SVUIA_INPLACEACTIVATE && !fShellView2)
{
uState = SVUIA_ACTIVATE_NOFOCUS; // map it to old one.
}
if (_cRefUIActivateSV)
{
TraceMsg(TF_WARNING, "CBB(%x)::_UIActivateView(%d) entered reentrantly!!!!!! _cRefUIActivate=%d",
this, uState, _cRefUIActivateSV);
if (uState == SVUIA_DEACTIVATE)
{
_fDeferredUIDeactivate = TRUE;
return S_OK;
}
if (!_HeyMoe_IsWiseGuy())
{
if (_bbd._psv)
_bbd._psv->UIActivate(SVUIA_INPLACEACTIVATE);
return S_OK;
}
}
_cRefUIActivateSV++;
TraceMsg(TF_SHDUIACTIVATE, "CBaseBrowser2(%x)::_UIActivateView(%d) about to call _bbd._psv(%x)->UIActivate",
this, uState, _bbd._psv);
_bbd._psv->UIActivate(uState);
if (uState == SVUIA_ACTIVATE_FOCUS && !fShellView2)
{
// win95 defview expects a SetFocus on activation (nt5 bug#172210)
if (_bbd._hwndView)
SetFocus(_bbd._hwndView);
}
TraceMsg(TF_SHDUIACTIVATE, "CBaseBrowser2(%x)::_UIActivateView(%d) back from _bbd._psv(%x)->UIActivate",
this, uState, _bbd._psv);
_cRefUIActivateSV--;
_UpdatePrivacyIcon(FALSE, FALSE);
UpdateSecureLockIcon(SECURELOCK_NOCHANGE);
}
_bbd._uActivateState = uState;
// If this is a pending view, set the focus to its window even though it's hidden.
// In ActivatePendingView(), we check if this window still has focus and, if it does,
// we will ui-activate the view. Fix for IE5 bug #70632 -- MohanB
if ( SVUIA_ACTIVATE_FOCUS == uState
&& !_bbd._psv
&& !_bbd._hwndView
&& _bbd._psvPending
&& _bbd._hwndViewPending
)
{
::SetFocus(_bbd._hwndViewPending);
}
if (_fDeferredUIDeactivate)
{
TraceMsg(TF_SHDUIACTIVATE, "CBaseBrowser2(%x)::_UIActivateView processing deferred UIDeactivate, _bbd._psv=%x",
this, _bbd._psv);
_fDeferredUIDeactivate = FALSE;
if (_bbd._psv)
_bbd._psv->UIActivate(SVUIA_DEACTIVATE);
_UpdatePrivacyIcon(FALSE, FALSE);
UpdateSecureLockIcon(SECURELOCK_NOCHANGE);
_bbd._uActivateState = SVUIA_DEACTIVATE;
}
if (_fDeferredSelfDestruction)
{
TraceMsg(TF_SHDUIACTIVATE, "CBaseBrowser2(%x)::_UIActivateView processing deferred OnDestroy",
this);
_fDeferredSelfDestruction = FALSE;
_pbsOuter->OnDestroy();
}
return S_OK;
}
//Called from CShellBrowser::OnCommand
HRESULT CBaseBrowser2::Offline(int iCmd)
{
HRESULT hresIsOffline = IsGlobalOffline() ? S_OK : S_FALSE;
switch(iCmd){
case SBSC_TOGGLE:
hresIsOffline = (hresIsOffline == S_OK) ? S_FALSE : S_OK; // Toggle Property
// Tell wininet that the user wants to go offline
SetGlobalOffline(hresIsOffline == S_OK);
SendShellIEBroadcastMessage(WM_WININICHANGE,0,0, 1000); // Tell all browser windows to update their title
break;
case SBSC_QUERY:
break;
default: // Treat like a query
break;
}
return hresIsOffline;
}
BOOL _TrackPidl(LPITEMIDLIST pidl, IUrlHistoryPriv *php, BOOL fIsOffline, LPTSTR pszUrl, DWORD cchUrl)
{
BOOL fRet = FALSE;
// Should use IsBrowserFrameOptionsPidlSet(pidl, BFO_ENABLE_HYPERLINK_TRACKING)
// instead of IsURLChild() because it doesn't work in Folder Shortcuts and doesn't
// work in NSEs outside of the "IE" name space (like Web Folders).
if (pidl && IsURLChild(pidl, FALSE))
{
if (SUCCEEDED(IEGetNameAndFlags(pidl, SHGDN_FORPARSING, pszUrl, cchUrl, NULL)))
{
PROPVARIANT vProp;
if (SUCCEEDED(php->GetProperty(pszUrl, PID_INTSITE_TRACKING, &vProp)))
{
if (vProp.vt == VT_UI4)
{
if (fIsOffline)
fRet = (vProp.ulVal & TRACK_OFFLINE_CACHE_ENTRY) ? TRUE : FALSE;
else
fRet = (vProp.ulVal & TRACK_ONLINE_CACHE_ENTRY) ? TRUE : FALSE;
}
PropVariantClear(&vProp);
}
}
}
return fRet;
}
// End tracking of previous page
// May start tracking of new page
// use SatoN's db to quick check tracking/tracking scope bits, so
// to eliminate call to CUrlTrackingStg::IsOnTracking
void CBaseBrowser2::_MayTrackClickStream(LPITEMIDLIST pidlNew)
{
BOOL fIsOffline = (Offline(SBSC_QUERY) != S_FALSE);
IUrlHistoryStg* phist;
IUrlHistoryPriv* phistp;
PROPVARIANT vProp = {0};
TCHAR szUrl[MAX_URL_STRING];
ASSERT(_bbd._pautoWB2);
HRESULT hr = _pspOuter->QueryService(SID_STopLevelBrowser, IID_PPV_ARG(IUrlHistoryStg, &phist));
if (SUCCEEDED(hr))
{
hr = phist->QueryInterface(IID_PPV_ARG(IUrlHistoryPriv, &phistp));
phist->Release();
}
if (SUCCEEDED(hr))
{
if (_TrackPidl(_bbd._pidlCur, phistp, fIsOffline, szUrl, SIZECHARS(szUrl)))
{
if (_ptracking)
_ptracking->OnUnload(szUrl);
else
hr = E_FAIL;
}
}
if (SUCCEEDED(hr))
{
if (_TrackPidl(pidlNew, phistp, fIsOffline, szUrl, SIZECHARS(szUrl)))
{
// instance of object already exists
BRMODE brMode = BM_NORMAL;
DWORD dwOptions;
if (!_ptracking)
{
_ptracking = new CUrlTrackingStg();
if (!_ptracking)
hr = E_OUTOFMEMORY;
}
if (SUCCEEDED(hr))
{
hr = GetTopFrameOptions(_pspOuter, &dwOptions);
if (SUCCEEDED(hr))
{
//Is this a desktop component?
if (dwOptions & FRAMEOPTIONS_DESKTOP)
brMode = BM_DESKTOP;
//Is it fullscreen?
else if (dwOptions & (FRAMEOPTIONS_SCROLL_AUTO | FRAMEOPTIONS_NO3DBORDER))
brMode = BM_THEATER;
}
_ptracking->OnLoad(szUrl, brMode, FALSE);
}
}
}
phistp->Release();
}
HRESULT CBaseBrowser2::_SwitchActivationNow()
{
ASSERT(_bbd._psvPending);
WORD wNavTypeFlags = 0; // init to suppress bogus C4701 warning
IShellView* psvNew = _bbd._psvPending;
IShellFolder* psfNew = _bbd._psfPending;
HWND hwndViewNew = _bbd._hwndViewPending;
LPITEMIDLIST pidlNew = _bbd._pidlPending;
_bbd._fIsViewMSHTML = _IsViewMSHTML(psvNew);
_bbd._psvPending = NULL;
_bbd._psfPending = NULL;
_bbd._hwndViewPending = NULL;
_bbd._pidlPending = NULL;
// Quickly check tracking prefix string on this page,
// if turned on, log enter/exit events
// Should use IsBrowserFrameOptionsSet(_bbd._psf, BFO_ENABLE_HYPERLINK_TRACKING)
// instead of IsURLChild() because it doesn't work in Folder Shortcuts and doesn't
// work in NSEs outside of the "IE" name space (like Web Folders).
if ((_bbd._pidlCur && IsURLChild(_bbd._pidlCur, FALSE)) ||
(pidlNew && IsURLChild(pidlNew, FALSE)))
_MayTrackClickStream(pidlNew);
// nuke the old stuff
_pbsOuter->ReleaseShellView();
ASSERT(!_bbd._psv && !_bbd._psf && !_bbd._hwndView);
// activate the new stuff
if (_grfHLNFPending != (DWORD)-1)
{
_OnNavigateComplete(pidlNew, _grfHLNFPending);
}
VALIDATEPENDINGSTATE();
// now do the actual switch
// no need to addref because we're keeping the pointer and just chaning
// it from the pending to the current member variables
_bbd._psf = psfNew;
_bbd._psv = psvNew;
ILFree(_bbd._pidlCur);
_bbd._pidlCur = pidlNew;
DEBUG_CODE(TCHAR szPath[INTERNET_MAX_URL_LENGTH + 1];)
DEBUG_CODE(IEGetDisplayName(_bbd._pidlCur, szPath, SHGDN_FORPARSING);)
DEBUG_CODE(TraceMsg(DM_TRACE, "CBB::ActivatePendingView (TRAVELLOG): _UpdateTravelLog called from shdocvw for %ws", szPath);)
_bbd._hwndView = hwndViewNew;
_dwReadyStateCur = _dwReadyStatePending;
if (_bbd._pszTitleCur)
LocalFree(_bbd._pszTitleCur);
_bbd._pszTitleCur = _bbd._pszTitlePending;
_bbd._pszTitlePending = NULL;
if (_eSecureLockIconPending != SECURELOCK_NOCHANGE)
{
_bbd._eSecureLockIcon = _eSecureLockIconPending;
_eSecureLockIconPending = SECURELOCK_NOCHANGE;
}
//
// This is the best time to resize the newone.
//
_pbsOuter->_UpdateViewRectSize();
SetWindowPos(_bbd._hwndView, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
// WARNING: Not all shellview supports IOleCommandTarget!!!
_fUsesPaletteCommands = FALSE;
if ( _bbd._psv )
{
_bbd._psv->QueryInterface(IID_PPV_ARG(IOleCommandTarget, &_bbd._pctView));
// PALETTE: Exec down to see if they support the colors changes Command so that we don't have to
// PALETTE: wire ourselves into the OnViewChange mechanism just to get palette changes...
if ( _bbd._pctView &&
SUCCEEDED(_bbd._pctView->Exec( &CGID_ShellDocView, SHDVID_CANDOCOLORSCHANGE, 0, NULL, NULL)))
{
_fUsesPaletteCommands = TRUE;
// force a colors dirty to make sure that we check for a new palette for each page...
_ColorsDirty( BPT_UnknownPalette );
}
}
// PALETTE: only register for the OnViewChange stuff if the above exec failed...
if (SUCCEEDED(_bbd._psv->QueryInterface(IID_PPV_ARG(IViewObject, &_pvo))) && !_fUsesPaletteCommands )
_pvo->SetAdvise(DVASPECT_CONTENT, ADVF_PRIMEFIRST, this);
_Exec_psbMixedZone();
if (_bbd._pctView != NULL)
{
_bbd._pctView->Exec(&CGID_ShellDocView, SHDVID_RESETSTATUSBAR, 0, NULL, NULL);
}
return S_OK;
}
// This member is called when we about to destroy the current shell view.
// Returning S_FALSE indicate that the user hit CANCEL when it is prompted
// to save the changes (if any).
HRESULT CBaseBrowser2::_MaySaveChanges(void)
{
HRESULT hres = S_OK;
if (_bbd._pctView) // we must check!
{
hres = _bbd._pctView->Exec(&CGID_Explorer, SBCMDID_MAYSAVECHANGES,
OLECMDEXECOPT_PROMPTUSER, NULL, NULL);
}
return hres;
}
HRESULT CBaseBrowser2::_DisableModeless(void)
{
if (_cRefCannotNavigate == 0)
{
OLECMD rgCmd;
BOOL fPendingInScript = FALSE;
// if pending shell view supports it, give it a chance to tell us it's not ready
// to deactivate [eg executing a script]. normally scripts should not be run
// before inplace activation, but TRIDENT sometimes has to do this when parsing.
//
rgCmd.cmdID = SHDVID_CANDEACTIVATENOW;
rgCmd.cmdf = 0;
if (SUCCEEDED(IUnknown_QueryStatus(_bbd._psvPending, &CGID_ShellDocView, 1, &rgCmd, NULL)) &&
(rgCmd.cmdf & MSOCMDF_SUPPORTED) &&
!(rgCmd.cmdf & MSOCMDF_ENABLED))
{
fPendingInScript = TRUE;
}
if (!fPendingInScript)
{
return S_FALSE;
}
}
return S_OK;
}
BOOL CBaseBrowser2::_CanNavigate(void)
{
return !((_DisableModeless() == S_OK) || (! IsWindowEnabled(_bbd._hwnd)));
}
HRESULT CBaseBrowser2::CanNavigateNow(void)
{
return _CanNavigate() ? S_OK : S_FALSE;
}
HRESULT CBaseBrowser2::_PauseOrResumeView(BOOL fPaused)
{
// If fPaused (it's minimized or the parent is minimized) or
// _bbd._psvPending is non-NULL, we need to pause.
if (_bbd._pctView)
{
VARIANT var = { 0 };
var.vt = VT_I4;
var.lVal = (_bbd._psvPending || fPaused) ? FALSE : TRUE;
_bbd._pctView->Exec(NULL, OLECMDID_ENABLE_INTERACTION, OLECMDEXECOPT_DONTPROMPTUSER, &var, NULL);
}
return S_OK;
}
HRESULT CBaseBrowser2::CreateViewWindow(IShellView* psvNew, IShellView* psvOld, LPRECT prcView, HWND* phwnd)
{
_fCreateViewWindowPending = TRUE;
_pbsOuter->GetFolderSetData(&(_fldBase._fld)); // it's okay to stomp on this every time
HRESULT hres = FileCabinet_CreateViewWindow2(_psbOuter, &_fldBase, psvNew, psvOld, prcView, phwnd);
_fCreateViewWindowPending = FALSE;
return hres;
}
//
// grfHLNF == (DWORD)-1 means don't touch the history at all.
//
// NOTE:
// if _fCreateViewWindowPending == TRUE, it means we came through here once
// already, but we are activating a synchronous view and the previous view would
// not deactivate immediately...
// It is used to delay calling IShellView::CreateViewWindow() for shell views until we know that
// we can substitute psvNew for _bbd._psv.
//
HRESULT CBaseBrowser2::_CreateNewShellView(IShellFolder* psf, LPCITEMIDLIST pidl, DWORD grfHLNF)
{
BOOL fActivatePendingView = FALSE;
IShellView *psvNew = NULL;
// Bail Out of Navigation if modal windows are up from our view
// Should we restart navigation on next EnableModeless(TRUE)?
if (!_CanNavigate())
{
TraceMsg(DM_ENABLEMODELESS, "CSB::_CreateNewShellView returning ERROR_BUSY");
return HRESULT_FROM_WIN32(ERROR_BUSY);
}
HRESULT hres = _MaySaveChanges();
if (hres == S_FALSE)
{
TraceMsg(DM_WARNING, "CBB::_CreateNewShellView _MaySaveChanges returned S_FALSE. Navigation canceled");
return HRESULT_FROM_WIN32(ERROR_CANCELLED);
}
TraceMsg(DM_WARNING, "CBB::_CNSV - Cur View MSHTML? %d Pending View MSHTML? %d",
_IsViewMSHTML(_bbd._psv), _IsViewMSHTML(_bbd._psvPending));
VALIDATEPENDINGSTATE();
#ifndef NON_NATIVE_FRAMES
// The navigation has been interrupted.
//
if ( _bbd._psv
&& _bbd._psvPending
&& _IsViewMSHTML(_bbd._psvPending))
{
_fHtmlNavCanceled = TRUE;
}
#endif
_CancelPendingView();
ASSERT (_fCreateViewWindowPending == FALSE);
VALIDATEPENDINGSTATE();
if (_bbd._psv && _IsViewMSHTML(_bbd._psv))
{
ATOMICRELEASE(_pphHistory);
SafeGetItemObject(_bbd._psv, SVGIO_BACKGROUND, IID_PPV_ARG(IPersistHistory, &_pphHistory));
}
hres = psf->CreateViewObject(_bbd._hwnd, IID_PPV_ARG(IShellView, &psvNew));
if (SUCCEEDED(hres))
{
_bbd._fCreatingViewWindow = TRUE;
IUnknown_SetSite(psvNew, _psbOuter); // SetSite for the view
_psbOuter->EnableModelessSB(FALSE);
HWND hwndViewNew = NULL;
RECT rcView;
//
// NOTES: SatoNa
//
// Notice that we explicitly call _GetViewBorderRect (non-virtual)
// instead of virtual _GetShellView, which CShellBrowser override.
// We now call thru (virtual) _pbsOuter, is this o.k.?
//
_pbsOuter->_GetViewBorderRect(&rcView);
// It is ncecessary for _bbd._pidlPending and _bbd._psvPending to both be set together.
// they're a pair
// previously _bbd._pidlPending was being set after the call to
// FileCabinet_CreateViewWindow and when messages were pumped there [NOTE: messages should not have been pumped there...]
// a redirect would be nofied in the bind status callback.
// this meant that a valid _bbd._pidlPending was actually available BUT
// then we would return and blow away that _bbd._pidlPending
//
ASSERT(_bbd._psvPending == NULL );
_bbd._psvPending = psvNew;
psvNew->AddRef();
ASSERT(_bbd._psfPending == NULL);
ASSERT(_bbd._pidlPending == NULL);
_bbd._psfPending = psf;
psf->AddRef();
_bbd._pidlPending = ILClone(pidl);
// Initialize _bbd._pidlNewShellView which will be used by GetViewStateStream
_bbd._pidlNewShellView = pidl;
_grfHLNFPending = grfHLNF;
// Start at _COMPLETE just in case the object we connect
// to doesn't notify us of ReadyState changes
//
_dwReadyStatePending = READYSTATE_COMPLETE;
// We need to cache this information here because the _dwDocFlags
// can change during the call to CreateViewWindow. This information
// is needed to determine whether or not we should stop the current
// view. If the document does not know how to navigate, then we
// stop the current view. The is needed in order to stop the
// navigation in the current view when a new navigation has started.
//
BOOL fDocCanNavigate = _dwDocFlags & DOCFLAG_DOCCANNAVIGATE;
hres = _pbsOuter->CreateViewWindow(psvNew, _bbd._psv, &rcView, &hwndViewNew);
IUnknown_SetSite(psvNew, NULL); // The view by now must have psb
_bbd._pidlNewShellView = NULL;
TraceMsg(DM_NAV, "ief NAV::%s %x %x",TEXT("_CreateNewShellView(3) Called CreateViewWindow"), psvNew, hres);
if (SUCCEEDED(hres))
{
// we defer the _PauseOrResumeView until here when we have enough
// info to know if it's a new page or not. o.w. we end up (e.g.)
// stopping bgsounds etc. on local links (nash:32270).
#ifdef NON_NATIVE_FRAMES
//
// Note (scotrobe): This was a no-op in IE5.
//
_PauseOrResumeView(_fPausedByParent);
// We stop the current view because we need to flush away any image stuff that
// is in queue so that the actual html file can get downloaded
//
_StopCurrentView();
#endif
// We can't stop the current view if the doc knows how
// to navigate. This is because in that case, the document
// in current view is the same document as the pending view.
//
if (!fDocCanNavigate)
{
_StopCurrentView();
}
_bbd._hwndViewPending = hwndViewNew;
// chrisfra - if hres == S_FALSE this (calling ActivatePendingViewAsync
// when _bbd._psv==NULL) will break async URL download
// as it will cause _bbd._psvPending to be set to NULL prematurely. this should
// be deferred until CDocObjectView::CBindStatusCallback::OnObjectAvailable
//if (hres==S_OK || _bbd._psv==NULL)
ASSERT(( hres == S_OK ) || ( hres == S_FALSE ));
if (hres == S_OK)
{
// We should activate synchronously.
//
// NOTE: This used to be ActivatePendingViewAsyc(), but that causes a
// fault if you navigated to C:\ and click A:\ as soon as it appears. This
// puts the WM_LBUTTONDOWN in FRONT of the WMC_ASYNCOPERATION message. If
// there's no disk in drive A: then a message box appears while in the
// middle of the above FileCabinet_CreateViewWindow call and we pull off
// the async activate and activate the view we're in the middle of
// creating! Don't do that.
//
fActivatePendingView = TRUE;
}
else
{
// Activation is pending.
// since the back button works as a stop on pending navigations, we
// should check that here as well.
_pbsOuter->UpdateBackForwardState();
}
}
else
{
if ( _bbd._psvPending
&& !(_dwDocFlags & DOCFLAG_NAVIGATEFROMDOC)
&& _IsViewMSHTML(_bbd._psvPending))
{
_fHtmlNavCanceled = TRUE;
}
else
{
_fHtmlNavCanceled = FALSE;
}
TraceMsg(DM_WARNING, "ief _CreateNewShellView psvNew->CreateViewWindow failed %x", hres);
_CancelPendingView();
}
psvNew->Release();
if (_psbOuter)
_psbOuter->EnableModelessSB(TRUE);
}
else
{
TraceMsg(TF_WARNING, "ief _BrowseTo psf->CreateViewObject failed %x", hres);
}
_fHtmlNavCanceled = FALSE;
//
// If there is any blocked async operation AND we can navigate now,
// unblock it now.
//
_MayUnblockAsyncOperation();
_bbd._fCreatingViewWindow = FALSE;
VALIDATEPENDINGSTATE();
TraceMsg(DM_WARNING, "CBB::_CNSV - Cur View MSHTML? %d Pending View MSHTML? %d",
_IsViewMSHTML(_bbd._psv), _IsViewMSHTML(_bbd._psvPending));
if (fActivatePendingView && !_IsViewMSHTML(_bbd._psvPending))
{
//
// Since _IsViewMSHTML can delegate to a marshalled interface,
// we can get re-entrancy. On re-entrancy, we can do a
// _CancelPendingView in which case _bbd._psvPending is
// no longer valid.
//
// So, we need to see if we still have _hbd._psvPending here.
//
if (_bbd._psvPending)
{
_PreActivatePendingViewAsync(); // so we match old code
hres = _pbsOuter->ActivatePendingView();
if (FAILED(hres))
TraceMsg(DM_WARNING, "CBB::_CNSV ActivatePendingView failed");
}
}
TraceMsg(DM_STARTUP, "ief _CreateNewShellView returning %x", hres);
return hres;
}
// private bind that is very loose in its bind semantics.
HRESULT IEBindToObjectForNavigate(LPCITEMIDLIST pidl, IBindCtx * pbc, IShellFolder **ppsfOut);
// this binds to the pidl folder then hands off to CreateNewShellView
// if you have anything you need to do like checking before we allow the navigate, it
// should go into _NavigateToPidl
HRESULT CBaseBrowser2::_CreateNewShellViewPidl(LPCITEMIDLIST pidl, DWORD grfHLNF, DWORD fSBSP)
{
SetNavigateState(BNS_BEGIN_NAVIGATE);
TraceMsg(DM_NAV, "ief NAV::%s %x %x",TEXT("_CreateNewShellViewPidl not same pidl"), pidl, _bbd._pidlCur);
// Check for URL-pidl?
// We will allow UI to be displayed by passing this IBindCtx to IShellFolder::BindToObject().
IBindCtx * pbc = NULL;
IShellFolder* psf;
HRESULT hres;
pbc = CreateBindCtxForUI(SAFECAST(this, IShellBrowser*)); // I'm safecasting to IUnknown. IShellBrowser is only for disambiguation.
hres = IEBindToObjectForNavigate(pidl, pbc, &psf); // If pbc is NULL, we will survive.
if (SUCCEEDED(hres))
{
hres = _CreateNewShellView(psf, pidl, grfHLNF);
TraceMsg(DM_STARTUP, "CSB::_CreateNewShellViewPidl _CreateNewShellView(3) returned %x", hres);
psf->Release();
}
else
{
// This will happen when a user tries to navigate to a directory past
// MAX_PATH by double clicking on a subdirectory in the shell.
TraceMsg(DM_TRACE, "CSB::_CreateNSVP BindToOject failed %x", hres);
}
// If _CreateNewShellView (or IEBindToObject) fails or the user cancels
// the MayOpen dialog (hres==S_FALSE), we should restore the navigation
// state to NORMAL (to stop animation).
if (FAILED(hres))
{
TraceMsg(TF_SHDNAVIGATE, "CSB::_CreateNSVP _CreateNewShellView FAILED (%x). SetNavigateState to NORMAL", hres);
SetNavigateState(BNS_NORMAL);
}
ATOMICRELEASE(pbc);
TraceMsg(DM_STARTUP, "CSB::_CreateNewShellViewPidl returning %x", hres);
return hres;
}
//
// Returns the border rectangle for the shell view.
//
HRESULT CBaseBrowser2::_GetViewBorderRect(RECT* prc)
{
_pbsOuter->_GetEffectiveClientArea(prc, NULL); // hmon?
// (derived class subtracts off border taken by all "frame" toolbars)
return S_OK;
}
//
// Returns the window rectangle for the shell view window.
//
HRESULT CBaseBrowser2::GetViewRect(RECT* prc)
{
//
// By default (when _rcBorderDoc is empty), ShellView's window
// rectangle is the same as its border rectangle.
//
_pbsOuter->_GetViewBorderRect(prc);
// Subtract document toolbar margin
prc->left += _rcBorderDoc.left;
prc->top += _rcBorderDoc.top;
prc->right -= _rcBorderDoc.right;
prc->bottom -= _rcBorderDoc.bottom;
TraceMsg(DM_UIWINDOW, "ief GetViewRect _rcBorderDoc=%x,%x,%x,%x",
_rcBorderDoc.left, _rcBorderDoc.top, _rcBorderDoc.right, _rcBorderDoc.bottom);
TraceMsg(DM_UIWINDOW, "ief GetViewRect prc=%x,%x,%x,%x",
prc->left, prc->top, prc->right, prc->bottom);
return S_OK;
}
HRESULT CBaseBrowser2::_PositionViewWindow(HWND hwnd, LPRECT prc)
{
SetWindowPos(hwnd, NULL,
prc->left, prc->top,
prc->right - prc->left,
prc->bottom - prc->top,
SWP_NOZORDER | SWP_NOACTIVATE);
return S_OK;
}
void CBaseBrowser2::_PositionViewWindowHelper(HWND hwnd, LPRECT prc)
{
if (_pbsOuter3)
_pbsOuter3->_PositionViewWindow(hwnd, prc);
else
_PositionViewWindow(hwnd, prc);
}
HRESULT CBaseBrowser2::_UpdateViewRectSize(void)
{
RECT rc;
TraceMsg(TF_SHDUIACTIVATE, "CSB::_UpdateViewRectSize called when _fDontReszeView=%d, _bbd._hwndV=%x, _bbd._hwndVP=%x",
_fDontResizeView, _bbd._hwndView, _bbd._hwndViewPending);
_pbsOuter->GetViewRect(&rc);
if (_bbd._hwndView && !_fDontResizeView)
{
TraceMsg(TF_SHDUIACTIVATE, "CSB::_UpdateViewRectSize resizing _bbd._hwndView(%x)", _bbd._hwndView);
_PositionViewWindowHelper(_bbd._hwndView, &rc);
}
if (_bbd._hwndViewPending)
{
TraceMsg(TF_SHDUIACTIVATE, "CSB::_UpdateViewRectSize resizing _bbd._hwndViewPending(%x)", _bbd._hwndViewPending);
_PositionViewWindowHelper(_bbd._hwndViewPending, &rc);
}
return S_OK;
}
UINT g_idMsgGetAuto = 0;
// this stays in shdocvw because the OC requires drop target registration
void CBaseBrowser2::_RegisterAsDropTarget()
{
// if it's okay to register and we haven't registered already
// and we've processed WM_CREATE
if (!_fNoDragDrop && !_fRegisteredDragDrop && _bbd._hwnd)
{
BOOL fAttemptRegister = _fTopBrowser ? TRUE : FALSE;
// if we're not toplevel, we still try to register
// if we have a proxy browser
if (!fAttemptRegister)
{
IShellBrowser* psb;
HRESULT hres = _pspOuter->QueryService(SID_SProxyBrowser, IID_PPV_ARG(IShellBrowser, &psb));
if (SUCCEEDED(hres))
{
fAttemptRegister = TRUE;
psb->Release();
}
}
if (fAttemptRegister)
{
HRESULT hr;
IDropTarget *pdt;
// SAFECAST(this, IDropTarget*), the hard way
hr = THR(QueryInterface(IID_PPV_ARG(IDropTarget, &pdt)));
if (SUCCEEDED(hr))
{
hr = THR(RegisterDragDrop(_bbd._hwnd, pdt));
if (SUCCEEDED(hr))
{
_fRegisteredDragDrop = TRUE;
}
pdt->Release();
}
}
}
}
void CBaseBrowser2::_UnregisterAsDropTarget()
{
if (_fRegisteredDragDrop)
{
_fRegisteredDragDrop = FALSE;
THR(RevokeDragDrop(_bbd._hwnd));
}
}
HRESULT CBaseBrowser2::OnCreate(LPCREATESTRUCT pcs)
{
HRESULT hres;
TraceMsg(DM_STARTUP, "_OnCreate called");
if (g_idMsgGetAuto == 0)
g_idMsgGetAuto = RegisterWindowMessage(TEXT("GetAutomationObject"));
hres = InitPSFInternet();
// do stuff that depends on window creation
if (SUCCEEDED(hres))
{
// this must be done AFTER the ctor so that we get virtuals right
// NOTE: only do this if we're actually creating the window, because
// the only time we SetOwner(NULL) is OnDestroy.
//
_bbd._pautoSS->SetOwner(SAFECAST(this, IShellBrowser*));
_RegisterAsDropTarget();
}
TraceMsg(DM_STARTUP, "ief OnCreate returning %d (SUCCEEDED(%x))", SUCCEEDED(hres), hres);
return SUCCEEDED(hres) ? S_OK : E_FAIL;
}
HRESULT CBaseBrowser2::OnDestroy()
{
// We're seeing some reentrancy here. If _cRefCannotNavigate is non-zero, it means we're
// in the middle of something and shouldn't destroy ourselves.
//
// Also check reentrant calls to OnDestroy().
if (_fInDestroy)
{
// Already being destroyed -- bail out.
return S_OK;
}
_fInDestroy = TRUE;
if (_cRefUIActivateSV)
{
TraceMsg(TF_WARNING,
"CBB(%x)::OnDestroy _cRefUIActivateSV(%d)!=0",
this, _cRefUIActivateSV);
// I need to defer my self-destruction.
//
_fDeferredSelfDestruction = TRUE;
return S_OK;
}
_CancelPendingView();
_pbsOuter->ReleaseShellView();
#ifdef DEBUG
// It is valid for _cRefCannotNavigate > 0 if we the system is shutting down. The reason
// for this is that we can still be processing a call to ::CreateNewShellView() when we
// the desktop recieves the WM_ENDSESSION and destroys us. In this case its ok to proceed
// with the destroy in this case, since we are logging off or rebooting anyway.
AssertMsg(_fMightBeShuttingDown || (S_FALSE == _DisableModeless()),
TEXT("CBB::OnDestroy _cRefCannotNavigate!=0 (%d)"),
_cRefCannotNavigate);
#endif
ATOMICRELEASE(_bbd._ptl);
// This should always be successful because the IDropTarget is registered
// in _OnCreate() and is the default one.
// _pdtView should have already been released in ReleaseShellView
ASSERT(_pdtView == NULL);
_UnregisterAsDropTarget();
//
// It is very important to call _bbd._pauto->SetOwner(NULL) here, which will
// remove any reference from the automation object to us. Before doing
// it, we always has cycled references and we never be released.
//
_bbd._pautoSS->SetOwner(NULL);
_bbd._hwnd = NULL;
#ifdef DEBUG
_fProcessed_WM_CLOSE = TRUE;
#endif
_DLMDestroy();
IUnknown_SetSite(_pToolbarExt, NULL); // destroy the toolbar extensions
if (_pauto)
{
IWebBrowserPriv * pWBPriv;
HRESULT hr = _pauto->QueryInterface(IID_PPV_ARG(IWebBrowserPriv, &pWBPriv));
if (SUCCEEDED(hr))
{
pWBPriv->OnClose();
pWBPriv->Release();
}
}
ATOMICRELEASE(_pHTMLDocument);
ATOMICRELEASE(_pphHistory);
return S_OK;
}
HRESULT CBaseBrowser2::NavigateToPidl(LPCITEMIDLIST pidl, DWORD grfHLNF)
{
HRESULT hr = S_OK;
LPITEMIDLIST pidlNew = (LPITEMIDLIST)pidl;
//
// Need to handle going back to an outside app - zekel 7MAY97
// i have dumped the code that did this, so now i need to put it
// into the CTravelLog implementation, so that it will be done properly
// without us. but it shouldnt be done here regardless.
//
// Remove? - with the old Travellog code
// special case hack for telling us to use the local history, not
// the global history
if (pidl && pidl != PIDL_LOCALHISTORY)
pidlNew = ILClone(pidl);
//
// Fortunately the only callers of NavigateToPidl use HLNF_NAVIGATINGBACK/FORWARD
// so that's the only mapping we need to do here.
//
DWORD dwSBSP = 0;
if (grfHLNF != (DWORD)-1)
{
if (grfHLNF & SHHLNF_WRITENOHISTORY)
dwSBSP |= SBSP_WRITENOHISTORY;
if (grfHLNF & SHHLNF_NOAUTOSELECT)
dwSBSP |= SBSP_NOAUTOSELECT;
}
if (grfHLNF & HLNF_NAVIGATINGBACK)
dwSBSP = SBSP_NAVIGATEBACK;
else if (grfHLNF & HLNF_NAVIGATINGFORWARD)
dwSBSP = SBSP_NAVIGATEFORWARD;
if (dwSBSP)
{
if (_psbOuter)
{
hr = _psbOuter->BrowseObject(pidlNew, dwSBSP); // browse will do the nav here.
}
ILFree(pidlNew);
}
else
_NavigateToPidlAsync(pidlNew, dwSBSP, FALSE); // takes ownership of the pidl
return hr;
}
// S_OK means we found at least one valid connection point
//
HRESULT GetWBConnectionPoints(IUnknown* punk, IConnectionPoint **ppcp1, IConnectionPoint **ppcp2)
{
HRESULT hres = E_FAIL;
IExpDispSupport* peds;
CConnectionPoint* pccp1 = NULL;
CConnectionPoint* pccp2 = NULL;
if (ppcp1)
*ppcp1 = NULL;
if (ppcp2)
*ppcp2 = NULL;
if (punk && SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IExpDispSupport, &peds))))
{
if (ppcp1 && SUCCEEDED(peds->FindCIE4ConnectionPoint(DIID_DWebBrowserEvents,
reinterpret_cast<CIE4ConnectionPoint**>(&pccp1))))
{
*ppcp1 = pccp1->CastToIConnectionPoint();
hres = S_OK;
}
if (ppcp2 && SUCCEEDED(peds->FindCIE4ConnectionPoint(DIID_DWebBrowserEvents2,
reinterpret_cast<CIE4ConnectionPoint**>(&pccp2))))
{
*ppcp2 = pccp2->CastToIConnectionPoint();
hres = S_OK;
}
peds->Release();
}
return hres;
}
void CBaseBrowser2::_UpdateBackForwardState()
{
if (_fTopBrowser && !_fNoTopLevelBrowser)
{
IConnectionPoint *pccp1;
IConnectionPoint *pccp2;
if (S_OK == GetWBConnectionPoints(_bbd._pautoEDS, &pccp1, &pccp2))
{
HRESULT hresT;
VARIANTARG va[2];
DISPPARAMS dp;
ITravelLog *ptl;
GetTravelLog(&ptl);
// if we've got a site or if we're trying to get to a site,
// enable the back button
BOOL fEnable = (ptl ? S_OK == ptl->GetTravelEntry(SAFECAST(this, IShellBrowser *), TLOG_BACK, NULL) : FALSE);
VARIANT_BOOL bEnable = fEnable ? VARIANT_TRUE : VARIANT_FALSE;
TraceMsg(TF_TRAVELLOG, "CBB::UpdateBackForward BACK = %d", fEnable);
// We use SHPackDispParams once instead of calling DoInvokeParams multiple times...
//
hresT = SHPackDispParams(&dp, va, 2, VT_I4, CSC_NAVIGATEBACK, VT_BOOL, bEnable);
ASSERT(S_OK==hresT);
// Removed the following EnableModelessSB(FALSE) because VB5 won't run the event handler if
// we're modal.
// _psbOuter->EnableModelessSB(FALSE);
IConnectionPoint_SimpleInvoke(pccp1, DISPID_COMMANDSTATECHANGE, &dp);
IConnectionPoint_SimpleInvoke(pccp2, DISPID_COMMANDSTATECHANGE, &dp);
fEnable = (ptl ? S_OK == ptl->GetTravelEntry(SAFECAST(this, IShellBrowser *), TLOG_FORE, NULL) : FALSE);
bEnable = fEnable ? VARIANT_TRUE : VARIANT_FALSE;
TraceMsg(TF_TRAVELLOG, "CBB::UpdateBackForward FORE = %d", fEnable);
ATOMICRELEASE(ptl);
// We know how SHPackDispParams fills in va[]
ASSERT(VT_BOOL == va[0].vt);
va[0].boolVal = bEnable;
ASSERT(VT_I4 == va[1].vt);
va[1].lVal = CSC_NAVIGATEFORWARD;
IConnectionPoint_SimpleInvoke(pccp1, DISPID_COMMANDSTATECHANGE, &dp);
IConnectionPoint_SimpleInvoke(pccp2, DISPID_COMMANDSTATECHANGE, &dp);
ATOMICRELEASE(pccp1);
ATOMICRELEASE(pccp2);
// Removed the following _psbOuter->EnableModelessSB(TRUE) because VB5 won't run the event handler if
// we're modal.
// _psbOuter->EnableModelessSB(TRUE);
}
}
}
void CBaseBrowser2::_NotifyCommandStateChange()
{
HRESULT hr;
// I'm only firing these in the toplevel case
// Why? Who cares about the frameset case
// since nobody listens to these events on
// the frameset.
//
if (_fTopBrowser && !_fNoTopLevelBrowser)
{
IConnectionPoint * pccp1;
IConnectionPoint * pccp2;
if (S_OK == GetWBConnectionPoints(_bbd._pautoEDS, &pccp1, &pccp2))
{
ASSERT(pccp1 || pccp2); // Should've gotten at least one
VARIANTARG args[2];
DISPPARAMS dp;
hr = SHPackDispParams(&dp, args, 2,
VT_I4, CSC_UPDATECOMMANDS,
VT_BOOL, FALSE);
IConnectionPoint_SimpleInvoke(pccp1, DISPID_COMMANDSTATECHANGE, &dp);
IConnectionPoint_SimpleInvoke(pccp2, DISPID_COMMANDSTATECHANGE, &dp);
ATOMICRELEASE(pccp1);
ATOMICRELEASE(pccp2);
}
}
}
LRESULT CBaseBrowser2::OnCommand(WPARAM wParam, LPARAM lParam)
{
UINT idCmd = GET_WM_COMMAND_ID(wParam, lParam);
HWND hwndControl = GET_WM_COMMAND_HWND(wParam, lParam);
if (IsInRange(idCmd, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST))
{
if (_bbd._hwndView)
SendMessage(_bbd._hwndView, WM_COMMAND, wParam, lParam);
else
TraceMsg(0, "view cmd id with NULL view");
/// REVIEW - how can we get FCIDM_FAVORITECMD... range if we're NOT toplevelapp?
/// REVIEW - should RecentOnCommand be done this way too?
}
return S_OK;
}
LRESULT CBaseBrowser2::OnNotify(LPNMHDR pnm)
{
// the id is from the view, probably one of the toolbar items
if (IsInRange(pnm->idFrom, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST))
{
if (_bbd._hwndView)
SendMessage(_bbd._hwndView, WM_NOTIFY, pnm->idFrom, (LPARAM)pnm);
}
return 0;
}
HRESULT CBaseBrowser2::OnSetFocus()
{
if (_bbd._hwndView)
{
SetFocus(_bbd._hwndView);
}
return 0;
}
#define ABOUT_HOME L"about:home"
// This function is VERY focused on achieving what the
// caller wants. That's why it has a very specific
// meaning to the return value.
BOOL IsAboutHomeOrNonAboutURL(LPITEMIDLIST pidl)
{
BOOL fIsAboutHomeOrNonAboutURL = TRUE;
WCHAR wzCur[MAX_URL_STRING];
if (pidl && SUCCEEDED(IEGetDisplayName(pidl, wzCur, SHGDN_FORPARSING)))
{
// Is it "about:home"?
if (0 != StrCmpNICW(ABOUT_HOME, wzCur, ARRAYSIZE(ABOUT_HOME) - 1))
{
// No. We also want to return TRUE if the scheme was NOT an ABOUT URL.
fIsAboutHomeOrNonAboutURL = (URL_SCHEME_ABOUT != GetUrlSchemeW(wzCur));
}
}
return fIsAboutHomeOrNonAboutURL;
}
//
// This function activate the pending view synchronously.
//
HRESULT CBaseBrowser2::ActivatePendingView(void)
{
HRESULT hres = E_FAIL;
BOOL bHadFocus;
TraceMsg(TF_SHDNAVIGATE, "CBB::ActivatePendingView called");
if (!_bbd._psvPending || !_bbd._psfPending)
{
#ifdef DEBUG
// it is valid for these to be null if we are shutting down b/c the desktop
// could have destroyed us, which would have called ::OnDestroy which calls _CancelPendingView
// which releases and nulls out _bbd._psvPending and _bbd._psfPending
ASSERT(_fMightBeShuttingDown);
#endif
goto Done;
}
#ifndef NON_NATIVE_FRAMES
IUnknown_Exec(_bbd._psvPending, &CGID_ShellDocView, SHDVID_COMPLETEDOCHOSTPASSING, 0, NULL, NULL);
#endif
#ifdef FEATURE_PICS
if (S_FALSE == IUnknown_Exec(_bbd._psvPending, &CGID_ShellDocView, SHDVID_CANACTIVATENOW, NULL, NULL, NULL))
{
hres = S_OK; // still waiting . . . but no failure.
goto DoneWait;
}
#endif
// if we are in modal loop, don't activate now
if (_cRefCannotNavigate > 0)
{
goto Done;
}
// if _cRefCannotNavigate > 0 it is possible that _hwndViewPending has not been created so this assert
// should go after the check above
ASSERT(_bbd._hwndViewPending);
// if shell view supports it, give it a chance to tell us it's not ready
// to deactivate [eg executing a script]
//
OLECMD rgCmd;
rgCmd.cmdID = SHDVID_CANDEACTIVATENOW;
rgCmd.cmdf = 0;
if (_bbd._pctView &&
SUCCEEDED(_bbd._pctView->QueryStatus(&CGID_ShellDocView, 1, &rgCmd, NULL)) &&
(rgCmd.cmdf & MSOCMDF_SUPPORTED) &&
!(rgCmd.cmdf & MSOCMDF_ENABLED))
{
//
// The DocObject that reported MSOCMDF_SUPPORTED must send
// SHDVID_DEACTIVATEMENOW when we're out of scripts or whatever so that
// we retry the activate
//
TraceMsg(DM_WARNING, "CBB::ActivatePendingView DocObject says I can't deactivate it now");
goto Done;
}
ASSERT(_bbd._psvPending);
// Prevent any navigation while we have the pointers swapped and we're in
// delicate state
_psbOuter->EnableModelessSB(FALSE);
//
// Don't play sound for the first navigation (to avoid multiple
// sounds to be played for a frame-set creation).
//
if (_bbd._psv && IsWindowVisible(_bbd._hwnd) && !(_dwSBSPQueued & SBSP_WRITENOHISTORY))
{
IEPlaySound(TEXT("ActivatingDocument"), FALSE);
}
ASSERT(_bbd._psvPending);
// NOTE: if there are any other protocols that need to not be in
// the travel log, it should probably implemented through UrlIs(URLIS_NOTRAVELLOG)
// right now, About: is the only one we care about
//
// Note that with the native frames changes, if we don't have
// a psv, we will want to call _UpdateTravelLog because that is
// where the first travel entry is added.
//
if (!(_grfHLNFPending & HLNF_CREATENOHISTORY) &&
(!_bbd._psv || IsAboutHomeOrNonAboutURL(_bbd._pidlCur))
&& !_fDontUpdateTravelLog)
{
DEBUG_CODE(TCHAR szPath[INTERNET_MAX_URL_LENGTH + 1];)
DEBUG_CODE(IEGetDisplayName(_bbd._pidlCur, szPath, SHGDN_FORPARSING);)
DEBUG_CODE(TraceMsg(DM_TRACE, "CBB::ActivatePendingView (TRAVELLOG): _UpdateTravelLog called from shdocvw for %ws", szPath);)
_UpdateTravelLog();
}
// WARNING - these will only fail if the UpdateTravelLog() - zekel - 7-AUG-97
// was skipped and these bits are set.
// alanau 5-may-98 -- I still hit this assert on a script-based navigate to the same page.
// iedisp.cpp sees StrCmpW("about:blank","about:blank?http://www.microsoft.com/ie/ie40/gallery/_main.htm")
// (for example), but basesb.cpp sees two equal pidls (both "about:blank?http://...").
// Killing this assert.
// ASSERT(!_fDontAddTravelEntry);
//
// scotrobe 11-Aug-99 If the hosted document is able to
// navigate itself, _UpdateTravelLog() will never be called.
//
ASSERT((_dwDocFlags & DOCFLAG_DOCCANNAVIGATE) || !_fIsLocalAnchor);
// before we destroy the window check if it or any of its childern has focus
bHadFocus = _bbd._hwndView && (IsChildOrSelf(_bbd._hwndView, GetFocus()) == S_OK)
|| _bbd._hwndViewPending && (IsChildOrSelf(_bbd._hwndViewPending, GetFocus()) == S_OK);
_pbsOuter->_SwitchActivationNow();
_psbOuter->EnableModelessSB(TRUE);
TraceMsg(DM_NAV, "CBaseBrowser2(%x)::ActivatePendingView(%x)", this, _bbd._psv);
// if some other app has focus, then don't uiactivate this navigate
// or we'll steal focus away. we'll uiactivate when we next get activated
//
// ie4.01, bug#64630 and 64329
// _fActive only gets set by WM_ACTIVATE on the TopBrowser. so for subframes
// we always defer setting the focus if they didnt have focus before navigation.
// the parent frame should set the subframe as necessary when it gets
// its UIActivate. - ReljaI 4-NOV-97
if (SVUIA_ACTIVATE_FOCUS == _bbd._uActivateState && !(_fActive || bHadFocus))
{
_bbd._uActivateState = SVUIA_INPLACEACTIVATE;
_fUIActivateOnActive = TRUE;
}
_UIActivateView(_bbd._uActivateState);
// Tell the shell's HTML window we have a new document.
if (_phtmlWS)
{
_phtmlWS->ViewActivated();
}
// this matches the _bbd._psvPending = NULL above.
// we don't put this right beside there because the
// _SwitchActivationNow could take some time, as well as the DoInvokePidl
SetNavigateState(BNS_NORMAL);
_pbsOuter->UpdateBackForwardState();
_NotifyCommandStateChange();
if (!_fNoDragDrop && _fTopBrowser)
{
ASSERT(_bbd._psv);
// _SwitchActivationNow should have already released the old _pdtView and set it to NULL
ASSERT(_pdtView == NULL);
_bbd._psv->QueryInterface(IID_PPV_ARG(IDropTarget, &_pdtView));
}
// The pending view may have a title change stored up, so fire the TitleChange.
// Also the pending view may not tell us about title changes, so simulate one.
//
if (_bbd._pszTitleCur)
{
if (_dwDocFlags & DOCFLAG_DOCCANNAVIGATE)
{
VARIANTARG varTitle;
HRESULT hrExec;
V_VT(&varTitle) = VT_BSTR;
V_BSTR(&varTitle) = SysAllocString(_bbd._pszTitleCur);
ASSERT(V_BSTR(&varTitle));
hrExec = IUnknown_Exec( _psbOuter, NULL, OLECMDID_SETTITLE, NULL, &varTitle, NULL);
VariantClear(&varTitle);
}
else
{
FireEvent_DoInvokeStringW(_bbd._pautoEDS, DISPID_TITLECHANGE, _bbd._pszTitleCur);
}
}
else if (_bbd._pidlCur)
{
WCHAR wzFullName[MAX_URL_STRING];
hres = ::IEGetNameAndFlags(_bbd._pidlCur, SHGDN_NORMAL, wzFullName, SIZECHARS(wzFullName), NULL);
if (SUCCEEDED(hres))
FireEvent_DoInvokeStringW(_bbd._pautoEDS, DISPID_TITLECHANGE, wzFullName);
}
// We must fire this event LAST because the app can shut us down
// in response to this event.
//
//
// MSWorks printing bug 104242 - Do NOT fire the NC2 event when the state is
// interactive. doing this will cause WorksCalender to print a partial document.
// Instead, we have the SetReadyState explicitly directly call FireEvent_NaviagateComplete
// and so the event will fire once the whole document has been parsed in. This
// code is matched by a bunch of event blockers in formkrnl.cxx
//
if ( GetModuleHandle(TEXT("WKSCAL.EXE")))
{
LBSTR::CString strPath;
LPTSTR pstrPath = strPath.GetBuffer( MAX_URL_STRING );
if ( strPath.GetAllocLength() < MAX_URL_STRING )
{
TraceMsg( TF_WARNING, "CBaseBrowser2::ActivatePendingView() - strPath Allocation Failed!" );
hres = E_OUTOFMEMORY;
}
else
{
hres = IEGetDisplayName( _bbd._pidlCur, pstrPath, SHGDN_FORPARSING );
// Let CString class own the buffer again.
strPath.ReleaseBuffer();
}
if ( FAILED(hres) )
{
strPath.Empty();
}
if ( GetUrlSchemeW( strPath ) == URL_SCHEME_ABOUT
|| GetUrlSchemeW( strPath ) == URL_SCHEME_FILE
|| GetUrlSchemeW( strPath ) == URL_SCHEME_INVALID
)
{
goto Done;
}
}
// fire the event!
FireEvent_NavigateComplete(_bbd._pautoEDS, _bbd._pautoWB2, _bbd._pidlCur, _bbd._hwnd);
// Sync up the lock icon state with CDocObjectHost
if (S_OK != IUnknown_Exec(_bbd._psv, &CGID_ShellDocView, SHDVID_FORWARDSECURELOCK, NULL, NULL, NULL))
{
// No CDocObjectHost, so we're not secure
CComVariant varLock((long) SECURELOCK_SET_UNSECURE);
if (!IsTopFrameBrowser(SAFECAST(this, IServiceProvider *), SAFECAST(this, IShellBrowser *)))
{
// we should suggest if we are not the top frame
IOleCommandTarget *pct;
if (SUCCEEDED(QueryService(SID_STopFrameBrowser, IID_PPV_ARG(IOleCommandTarget, &pct))))
{
varLock.lVal += SECURELOCK_FIRSTSUGGEST;
pct->Exec(&CGID_Explorer, SBCMDID_SETSECURELOCKICON, 0, &varLock, NULL);
pct->Release();
}
}
else
{
Exec(&CGID_Explorer, SBCMDID_SETSECURELOCKICON, 0, &varLock, NULL);
}
}
hres = S_OK;
Done:
OnReadyStateChange(NULL, READYSTATE_COMPLETE);
DoneWait:
return hres;
}
LRESULT CBaseBrowser2::_DefWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
//
// call the UNICODE/ANSI aware DefWindowProc
//
return ::SHDefWindowProc(hwnd, uMsg, wParam, lParam);
}
void CBaseBrowser2::_ViewChange(DWORD dwAspect, LONG lindex)
{
//
// we are interested in content changes only
//
// NOTE: if we are registed for separate palette commands, then do not invalidate the colours here...
if (dwAspect & DVASPECT_CONTENT && !_fUsesPaletteCommands )
{
//
// recompute our palette
//
_ColorsDirty(BPT_UnknownPalette);
}
else
{
TraceMsg(DM_PALETTE, "cbb::_vc not interested in aspect(s) %08X", dwAspect);
}
}
void CBaseBrowser2::_ColorsDirty(BrowserPaletteType bptNew)
{
//
// if we are not currently handling palette messages then get out
//
if (_bptBrowser == BPT_DeferPaletteSupport)
{
TraceMsg(DM_PALETTE, "cbb::_cd deferring palette support");
return;
}
//
// we only handle palette changes and display changes
//
if ((bptNew != BPT_UnknownPalette) && (bptNew != BPT_UnknownDisplay))
{
AssertMsg(FALSE, TEXT("CBaseBrowser2::_ColorsDirty: invalid BPT_ constant"));
bptNew = BPT_UnknownPalette;
}
//
// if we aren't on a palettized display we don't care about palette changes
//
if ((bptNew != BPT_UnknownDisplay) && (_bptBrowser == BPT_NotPalettized))
{
TraceMsg(DM_PALETTE, "cbb::_cd not on palettized display");
return;
}
//
// if we are already handling one of these then we're done
//
if ((_bptBrowser == BPT_PaletteViewChanged) ||
(_bptBrowser == BPT_DisplayViewChanged))
{
TraceMsg(DM_PALETTE, "cbb::_cd coalesced");
return;
}
//
// unknown display implies unknown palette when the display is palettized
//
if (_bptBrowser == BPT_UnknownDisplay)
bptNew = BPT_UnknownDisplay;
//
// post ourselves a WM_QUERYNEWPALETTE so we can pile up multiple advises
// and handle them at once (we can see a lot of them sometimes...)
// NOTE: the lParam is -1 so we can tell that WE posted it and that
// NOTE: it doesn't necessarily mean we onw the foreground palette...
//
if (PostMessage(_bbd._hwnd, WM_QUERYNEWPALETTE, 0, (LPARAM) -1))
{
TraceMsg(DM_PALETTE, "cbb::_cd queued update");
//
// remember that we have already posted a WM_QUERYNEWPALETTE
//
_bptBrowser = (bptNew == BPT_UnknownPalette)?
BPT_PaletteViewChanged : BPT_DisplayViewChanged;
}
else
{
TraceMsg(DM_PALETTE, "cbb::_cd FAILED!");
//
// at least remember that the palette is stale
//
_bptBrowser = bptNew;
}
}
void CBaseBrowser2::v_PropagateMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL fSend)
{
if (_bbd._hwnd)
PropagateMessage(_bbd._hwnd, uMsg, wParam, lParam, fSend);
}
void CBaseBrowser2::_DisplayChanged(WPARAM wParam, LPARAM lParam)
{
//
// forward this on to our children
//
v_PropagateMessage(WM_DISPLAYCHANGE, wParam, lParam, TRUE);
//
// and mark our colors as dirty
//
_ColorsDirty(BPT_UnknownDisplay);
}
//
// return results for _UpdateBrowserPaletteInPlace()
// S_OK : BrowserPalette was successfully updated in place
// S_FALSE : BrowserPalette is exactly the same, no need to update
// E_FAIL : Unable to update palette in place at all, caller needs to create new palette
//
HRESULT CBaseBrowser2::_UpdateBrowserPaletteInPlace(LOGPALETTE *plp)
{
if (!_hpalBrowser)
return E_FAIL;
WORD w;
if (GetObject(_hpalBrowser, sizeof(w), &w) != sizeof(w))
return E_FAIL;
if (w != plp->palNumEntries)
return E_FAIL;
if (w > 256)
return E_FAIL;
//
// GDI marks a palette as dirty if you update its colors
// only replace the entries if the colors are actually different
// this prevents excessive flashing
//
PALETTEENTRY ape[256];
if (GetPaletteEntries(_hpalBrowser, 0, w, ape) != w)
return E_FAIL;
if (memcmp(ape, plp->palPalEntry, w * sizeof(PALETTEENTRY)) == 0)
{
TraceMsg(DM_PALETTE, "cbb::_ubpip %08x already had view object's colors", _hpalBrowser);
return S_FALSE;
}
// make sure we don't reuse the global halftone palette that we are reusing across shdocvw....
// do this after we've done the colour match
if ( _hpalBrowser == g_hpalHalftone )
{
return E_FAIL;
}
//
// actually set up the colors
//
if (SetPaletteEntries(_hpalBrowser, 0, plp->palNumEntries,
plp->palPalEntry) != plp->palNumEntries)
{
return E_FAIL;
}
TraceMsg(DM_PALETTE, "cbb::_ubpip updated %08x with view object's colors", _hpalBrowser);
return S_OK;
}
void CBaseBrowser2::_RealizeBrowserPalette(BOOL fBackground)
{
HPALETTE hpalRealize;
//
// get a palette to realize
//
if (_hpalBrowser)
{
TraceMsg(DM_PALETTE, "cbb::_rbp realizing %08x", _hpalBrowser);
hpalRealize = _hpalBrowser;
}
else
{
TraceMsg(DM_PALETTE, "cbb::_rbp realizing DEFAULT_PALETTE");
hpalRealize = (HPALETTE) GetStockObject(DEFAULT_PALETTE);
}
if ( !_fOwnsPalette && !fBackground )
{
// NOTE: if we don't think we own the foreground palette, and we
// NOTE: are being told to realize in the foreground, then ignore
// NOTE: it because they are wrong...
fBackground = TRUE;
}
//
// get a DC to realize on and select our palette
//
HDC hdc = GetDC(_bbd._hwnd);
if (hdc)
{
HPALETTE hpalOld = SelectPalette(hdc, hpalRealize, fBackground);
if (hpalOld)
{
//
// we don't paint any palettized stuff ourselves we're just a frame
// eg. we don't need to repaint here if the realize returns nonzero
//
RealizePalette(hdc);
//
// since we create and delete our palette alot, don't leave it selected
//
SelectPalette(hdc, hpalOld, TRUE);
}
ReleaseDC(_bbd._hwnd, hdc);
}
}
void CBaseBrowser2::_PaletteChanged(WPARAM wParam, LPARAM lParam)
{
TraceMsg(DM_PALETTE, "cbb::_pc (%08X, %08X, %08X) begins -----------------------", this, wParam, lParam);
//
// cdturner: 08/03/97
// we think that we currently own the foregorund palette, we need to make sure that
// the window that just realized in the foreground (and thus caused the system
// to generate the WM_PALETTECHANGED) was us, otherwise, we no longer own the
// palette
//
if ( _fOwnsPalette )
{
// by default we do not own it.
_fOwnsPalette = FALSE;
// the wParam hwnd we get is the top-level window that cause it, so we need to walk the window
// chain to find out if it is one of our parents...
// start at _bbd._hwnd (incase we are the top-level :-))
HWND hwndParent = _bbd._hwnd;
while ( hwndParent != NULL )
{
if ( hwndParent == (HWND) wParam )
{
// we caused it, so therefore we must still own it...
_fOwnsPalette = TRUE;
break;
}
hwndParent = GetParent( hwndParent );
}
}
//
// should we realize now? (see _QueryNewPalette to understand _bptBrowser)
//
// NOTE: we realize in the background here on purpose! This helps us be
// compatible with Netscape plugins etc that think they can own the
// palette from inside the browser.
//
if (((HWND)wParam != _bbd._hwnd) && (_bptBrowser == BPT_Normal))
_RealizeBrowserPalette(TRUE);
//
// always forward the changes to the current view
// let the toolbars know too
//
if (_bbd._hwndView)
TraceMsg(DM_PALETTE, "cbb::_pc forwarding to view window %08x", _bbd._hwndView);
_pbsOuter->_SendChildren(_bbd._hwndView, TRUE, WM_PALETTECHANGED, wParam, lParam); // SendMessage
TraceMsg(DM_PALETTE, "cbb::_pc (%08X) ends -------------------------", this);
}
BOOL CBaseBrowser2::_QueryNewPalette()
{
BrowserPaletteType bptNew;
HPALETTE hpalNew = NULL;
BOOL fResult = TRUE;
BOOL fSkipRealize = FALSE;
HDC hdc;
TraceMsg(DM_PALETTE, "cbb::_qnp (%08X) begins ==================================", this);
TryAgain:
switch (_bptBrowser)
{
case BPT_Normal:
TraceMsg(DM_PALETTE, "cbb::_qnp - normal realization");
//
// Normal Realization: realize _hpalBrowser in the foreground
//
// avoid realzing the palette into the display if we've been asked not to...
if ( !fSkipRealize )
_RealizeBrowserPalette(FALSE);
break;
case BPT_ShellView:
TraceMsg(DM_PALETTE, "cbb::_qnp - forwarding to shell view");
//
// Win95 Explorer-compatible: forward the query to the shell view
//
if (_bbd._hwndView && SendMessage(_bbd._hwndView, WM_QUERYNEWPALETTE, 0, 0))
break;
TraceMsg(DM_PALETTE, "cbb::_qnp - no shell view or view didn't answer");
//
// we only manage our palette as a toplevel app
//
//
// the view didn't handle it; fall through to use a generic palette
//
UseGenericPalette:
TraceMsg(DM_PALETTE, "cbb::_qnp - using generic palette");
//
// Use a Halftone Palette for the device
//
hpalNew = g_hpalHalftone;
bptNew = BPT_Normal;
goto UseThisPalette;
case BPT_UnknownPalette:
case BPT_PaletteViewChanged:
TraceMsg(DM_PALETTE, "cbb::_qnp - computing palette");
//
// Undecided: try to use IViewObject::GetColorSet to compose a palette
//
LOGPALETTE *plp;
HRESULT hres;
// default to forwarding to the view if something fails along the way
hpalNew = NULL;
bptNew = BPT_ShellView;
//
// if we have a view object then try to get its color set
//
if (!_pvo)
{
TraceMsg(DM_PALETTE, "cbb::_qnp - no view object");
goto UseGenericPalette;
}
plp = NULL;
hres = _pvo->GetColorSet(DVASPECT_CONTENT, -1, NULL, NULL, NULL, &plp);
if (FAILED(hres))
{
TraceMsg(DM_PALETTE, "cbb::_qnp - view object's GetColorSet failed");
goto UseThisPalette;
}
//
// either a null color set or S_FALSE mean the view object doesn't care
//
if (!plp)
hres = S_FALSE;
if (hres != S_FALSE)
{
//
// can we reuse the current palette object?
//
HRESULT hrLocal = _UpdateBrowserPaletteInPlace(plp);
if (FAILED( hrLocal ))
{
TraceMsg(DM_PALETTE, "cbb::_qnp - creating new palette for view object's colors");
hpalNew = CreatePalette(plp);
}
else
{
hpalNew = _hpalBrowser;
// NOTE: if we got back the same palette, don't bother realizing it into the foreground.
// NOTE: this has the (desirable) side effect of stops us flashing the display when a
// NOTE: control on a page has (wrongly) realized its own palette...
if ( hrLocal == S_FALSE )
{
// ASSERT( GetActiveWindow() == _bbd._hwnd );
fSkipRealize = TRUE;
}
}
//
// did we succeed at setting up a palette?
//
if (hpalNew)
{
TraceMsg(DM_PALETTE, "cbb::_qnp - palette is ready to use");
bptNew = BPT_Normal;
}
else
{
TraceMsg(DM_PALETTE, "cbb::_qnp - failed to create palette");
}
}
//
// free the logical palette from the GetColorSet above
//
if (plp)
CoTaskMemFree(plp);
//
// if the view object responded that it didn't care then pick a palette
//
if (hres == S_FALSE)
{
TraceMsg(DM_PALETTE, "cbb::_qnp - view object doesn't care");
goto UseGenericPalette;
}
//
// fall through to use the palette we decided on
//
UseThisPalette:
//
// we get here when we've decided on a new palette strategy
//
TraceMsg(DM_PALETTE, "cbb::_qnp - chose palette %08x", hpalNew);
//
// do we have a new palette object to use?
//
if (hpalNew != _hpalBrowser)
{
if (_hpalBrowser && _hpalBrowser != g_hpalHalftone)
{
TraceMsg(DM_PALETTE, "cbb::_qnp - deleting old palette %08x", _hpalBrowser);
DeletePalette(_hpalBrowser);
}
_hpalBrowser = hpalNew;
}
//
// notify the hosted object that we just changed the palette......
//
if ( _bbd._pctView )
{
VARIANTARG varIn = {0};
varIn.vt = VT_I4;
varIn.intVal = DISPID_AMBIENT_PALETTE;
_bbd._pctView->Exec( &CGID_ShellDocView, SHDVID_AMBIENTPROPCHANGE, 0, &varIn, NULL );
}
//
// now loop back and use this new palette strategy
//
_bptBrowser = bptNew;
goto TryAgain;
case BPT_UnknownDisplay:
case BPT_DisplayViewChanged:
case BPT_DeferPaletteSupport:
TraceMsg(DM_PALETTE, "cbb::_qnp - unknown display");
//
// Unknown Display: decide whether we need palette support or not
//
hdc = GetDC(NULL);
if (hdc)
{
bptNew = (GetDeviceCaps(hdc, RASTERCAPS) & RC_PALETTE)?
BPT_UnknownPalette : BPT_NotPalettized;
ReleaseDC(NULL, hdc);
}
//
// Set the new mode and branch accordingly
// NOTE: we don't do a UseThisPalette here because it is still unknown
//
if (hdc && (_bptBrowser = bptNew) == BPT_UnknownPalette)
goto TryAgain;
TraceMsg(DM_PALETTE, "cbb::_qnp - not in palettized display mode");
//
// fall through to non-palette case
//
case BPT_NotPalettized:
//
// Not in Palettized Mode: do nothing
//
// if we just switched from a palettized mode then free our palette
//
if (_hpalBrowser)
{
TraceMsg(DM_PALETTE, "cbb::_qnp - old palette still lying around");
hpalNew = NULL;
bptNew = BPT_NotPalettized;
goto UseThisPalette;
}
//
// and don't do anything else
//
fResult = FALSE;
break;
default:
TraceMsg(DM_PALETTE, "cbb::_qnp - invalid BPT_ state!");
//
// we should never get here
//
ASSERT(FALSE);
_bptBrowser = BPT_UnknownDisplay;
goto TryAgain;
}
TraceMsg(DM_PALETTE, "cbb::_qnp (%08X) ends ====================================", this);
return fResult;
}
HRESULT CBaseBrowser2::_TryShell2Rename(IShellView* psv, LPCITEMIDLIST pidlNew)
{
HRESULT hres = E_FAIL;
if (EVAL(psv)) // Winstone once found it to be NULL.
{
// ? - overloading the semantics of IShellExtInit
IPersistFolder* ppf;
hres = psv->QueryInterface(IID_PPV_ARG(IPersistFolder, &ppf));
if (SUCCEEDED(hres))
{
hres = ppf->Initialize(pidlNew);
if (SUCCEEDED(hres))
{
// we need to update what we're pointing to
LPITEMIDLIST pidl = ILClone(pidlNew);
if (pidl)
{
if (IsSameObject(psv, _bbd._psv))
{
ASSERT(_bbd._pidlCur);
ILFree(_bbd._pidlCur);
_bbd._pidlCur = pidl;
// If the current pidl is renamed, we need to fire a
// TITLECHANGE event. We don't need to do this in the
// pending case because the NavigateComplete provides
// a way to get the title.
//
WCHAR wzFullName[MAX_URL_STRING];
::IEGetNameAndFlags(_bbd._pidlCur, SHGDN_NORMAL, wzFullName, SIZECHARS(wzFullName), NULL);
FireEvent_DoInvokeStringW(_bbd._pautoEDS, DISPID_TITLECHANGE, wzFullName);
}
else if (IsSameObject(psv, _bbd._psvPending))
{
ASSERT(_bbd._pidlPending);
ILFree(_bbd._pidlPending);
_bbd._pidlPending = pidl;
}
else
{
// It may be possible to get here during _MayPlayTransition!
//
ASSERT(!_bbd._psvPending); // this should be the case if we get here
ASSERT(FALSE); // we should never get here or we have a problem
}
}
}
ppf->Release();
}
}
return hres;
}
HRESULT CBaseBrowser2::OnSize(WPARAM wParam)
{
if (wParam != SIZE_MINIMIZED)
{
_pbsOuter->v_ShowHideChildWindows(FALSE);
}
return S_OK;
}
BOOL CBaseBrowser2::v_OnSetCursor(LPARAM lParam)
{
if (_fNavigate || _fDescendentNavigate)
{
switch (LOWORD(lParam))
{
case HTBOTTOM:
case HTTOP:
case HTLEFT:
case HTRIGHT:
case HTBOTTOMLEFT:
case HTBOTTOMRIGHT:
case HTTOPLEFT:
case HTTOPRIGHT:
break;
default:
SetCursor(LoadCursor(NULL, IDC_APPSTARTING));
return TRUE;
}
}
else
{
if (GetTickCount() < _dwStartingAppTick + STARTING_APP_DURATION)
{
SetCursor(LoadCursor(NULL, IDC_APPSTARTING));
return TRUE;
}
}
return FALSE;
}
const SA_BSTRGUID s_sstrSearchFlags = {
38 * SIZEOF(WCHAR),
L"{265b75c1-4158-11d0-90f6-00c04fd497ea}"
};
#define PROPERTY_VALUE_SEARCHFLAGS ((BSTR)s_sstrSearchFlags.wsz)
LRESULT CBaseBrowser2::_OnGoto(void)
{
TraceMsg(TF_SHDNAVIGATE, "CBB::_OnGoto called");
//
// If we can't navigate right now, postpone it by restoring _uAction
// and don't free pidlQueued. Subsequent _MayUnblockAsyncOperation call
// will post WMC_ASYNCOPERATION (if we can navigate) and we come here
// again.
//
if (!_CanNavigate())
{
TraceMsg(TF_SHDNAVIGATE, "CBB::_OnGoto can't do it now. Postpone!");
_uActionQueued = ASYNCOP_GOTO;
return S_FALSE;
}
LPITEMIDLIST pidl = _pidlQueued;
DWORD dwSBSP = _dwSBSPQueued;
_dwSBSPQueued = 0;
_pidlQueued = NULL;
if (pidl && PIDL_NOTHING != pidl)
{
DWORD grfHLNF = 0;
if (dwSBSP & SBSP_WRITENOHISTORY)
{
grfHLNF |= SHHLNF_WRITENOHISTORY;
}
if (dwSBSP & SBSP_NOAUTOSELECT)
{
grfHLNF |= SHHLNF_NOAUTOSELECT;
}
if (PIDL_LOCALHISTORY == pidl)
{
pidl = NULL;
// For beta2 we need to do a better job mapping SBSP to HLNF values.
// For beta1, this is the only case that's busted.
//
// This problem stems from converting _NavigateToPidl in ::NavigateToPidl
// into a call to the Async version
//
if (dwSBSP & SBSP_NAVIGATEBACK)
grfHLNF = HLNF_NAVIGATINGBACK;
else if (dwSBSP & SBSP_NAVIGATEFORWARD)
grfHLNF = HLNF_NAVIGATINGFORWARD;
}
else if (dwSBSP == (DWORD)-1)
{
// Same problem as above
//
// This problem stems from converting _NavigateToPidl in ::NavigateToTLItem
// into a call to the Async version
//
grfHLNF = (DWORD)-1;
}
else
{
if (dwSBSP & SBSP_REDIRECT)
grfHLNF |= HLNF_CREATENOHISTORY;
{
IWebBrowser2 *pWB2;
BOOL bAllow = ((dwSBSP & SBSP_ALLOW_AUTONAVIGATE) ? TRUE : FALSE);
if (bAllow)
grfHLNF |= HLNF_ALLOW_AUTONAVIGATE;
if (SUCCEEDED(_pspOuter->QueryService(SID_SHlinkFrame, IID_PPV_ARG(IWebBrowser2, &pWB2))))
{
if (pWB2)
{
VARIANT v = {0}; // for failure of below call
pWB2->GetProperty(PROPERTY_VALUE_SEARCHFLAGS, &v);
if (v.vt == VT_I4)
{
v.lVal &= (~ 0x00000001); // Clear the allow flag before we try to set it.
v.lVal |= (bAllow ? 0x01 : 0x00);
}
else
{
VariantClear(&v);
v.vt = VT_I4;
v.lVal = (bAllow ? 0x01 : 0x00);
}
pWB2->PutProperty(PROPERTY_VALUE_SEARCHFLAGS, v);
pWB2->Release();
}
}
}
}
TraceMsg(DM_NAV, "ief NAV::%s %x %x",TEXT("_OnGoto called calling _NavigateToPidl"), pidl, _bbd._pidlCur);
_pbsOuter->_NavigateToPidl(pidl, (DWORD)grfHLNF, dwSBSP);
}
else
{
// wParam=NULL means canceling the navigation.
TraceMsg(DM_NAV, "NAV::_OnGoto calling _CancelPendingView");
_CancelPendingView();
if (PIDL_NOTHING == pidl)
{
// If we're being told to navigate to nothing, go there
//
// What should we do with the history??
//
_pbsOuter->ReleaseShellView();
}
else if (!_bbd._pidlCur)
{
//
// If the very first navigation failed, navigate to
// a local html file so that the user will be able
// to View->Options dialog.
//
TCHAR szPath[MAX_PATH]; // This is always local
HRESULT hresT = _GetStdLocation(szPath, ARRAYSIZE(szPath), DVIDM_GOLOCALPAGE);
if (FAILED(hresT) || !PathFileExists(szPath))
{
StrCpyN(szPath, TEXT("shell:Desktop"), ARRAYSIZE(szPath));
}
BSTR bstr = SysAllocStringT(szPath);
if (bstr)
{
TraceMsg(TF_SHDNAVIGATE, "CBB::_OnGoto Calling _bbd._pauto->Navigate(%s)", szPath);
_bbd._pautoWB2->Navigate(bstr,NULL,NULL,NULL,NULL);
SysFreeString(bstr);
}
}
}
_FreeQueuedPidl(&pidl);
return 0;
}
void CBaseBrowser2::_FreeQueuedPidl(LPITEMIDLIST* ppidl)
{
if (*ppidl && PIDL_NOTHING != *ppidl)
{
ILFree(*ppidl);
}
*ppidl = NULL;
}
HRESULT CBaseBrowser2::OnFrameWindowActivateBS(BOOL fActive)
{
BOOL fOldActive = _fActive;
if (_pact)
{
TraceMsg(TF_SHDUIACTIVATE, "OnFrameWindowActivateBS(%d)", fActive);
_pact->OnFrameWindowActivate(fActive);
}
_fActive = fActive;
if (fActive && !fOldActive && _fUIActivateOnActive)
{
_fUIActivateOnActive = FALSE;
_UIActivateView(SVUIA_ACTIVATE_FOCUS);
}
return S_OK;
}
LRESULT CBaseBrowser2::WndProcBS(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == g_uMsgFileOpened)
{
AppStarted();
return 0;
}
switch(uMsg)
{
#ifdef DEBUG
// compile time assert to make sure we don't use these msgs
// here since we must allow these to go to the subclasses
case CWM_GLOBALSTATECHANGE:
case CWM_FSNOTIFY:
case WMC_ACTIVATE:
break;
case WM_QUERYENDSESSION:
// assume we are going to be shutting down (if we aren't, we unset this during the
// WM_ENDSESSION message)
_fMightBeShuttingDown = TRUE;
goto DoDefault; // act like we didn't handle this msg
break;
case WM_ENDSESSION:
// the wParam tells us if the session is being ended or not
_fMightBeShuttingDown = (BOOL)wParam;
return 0;
break;
#endif
// UGLY: Win95/NT4 shell DefView code sends this msg and does not deal
// with the failure case. other ISVs do the same so this needs to stay forever
case CWM_GETISHELLBROWSER:
return (LRESULT)_psbOuter; // not ref counted!
// WM_COPYDATA is used to implement inter-window target'ed navigation
// Copy data contains target, URL, postdata and referring URL
case WM_COPYDATA:
return (LRESULT)FALSE;
case WMC_ASYNCOPERATION:
{
UINT uAction = _uActionQueued;
_uActionQueued = ASYNCOP_NIL;
switch(uAction)
{
case ASYNCOP_GOTO:
_OnGoto();
break;
case ASYNCOP_ACTIVATEPENDING:
VALIDATEPENDINGSTATE();
if (_bbd._psvPending) // paranoia
{
if (FAILED(_pbsOuter->ActivatePendingView()) && _cRefCannotNavigate > 0)
{
_uActionQueued = ASYNCOP_ACTIVATEPENDING; // retry activation
}
}
break;
case ASYNCOP_CANCELNAVIGATION:
_CancelPendingNavigation();
break;
case ASYNCOP_NIL:
break;
default:
ASSERT(0);
break;
}
}
break;
case WMC_DELAYEDDDEEXEC:
return IEDDE_RunDelayedExecute();
break;
case WM_SIZE:
_pbsOuter->OnSize(wParam);
break;
#ifdef PAINTINGOPTS
case WM_WINDOWPOSCHANGING:
// Let's not waste any time blitting bits around, the viewer window
// is really the guy that has the content so when it resizes itself
// it can decide if it needs to blt or not. This also makes resizing
// look nicer.
((LPWINDOWPOS)lParam)->flags |= SWP_NOCOPYBITS;
goto DoDefault;
#endif
case WM_ERASEBKGND:
if (!_bbd._hwndView)
goto DoDefault;
goto DoDefault;
case WM_SETFOCUS:
return _pbsOuter->OnSetFocus();
case WM_DISPLAYCHANGE:
_DisplayChanged(wParam, lParam);
break;
case WM_PALETTECHANGED:
_PaletteChanged(wParam, lParam);
break;
case WM_QUERYNEWPALETTE:
// we always pass -1 as the LParam to show that we posted it to ourselves...
if ( lParam != 0xffffffff )
{
// otherwise, it looks like the system or our parent has just sent a real honest to God,
// system WM_QUERYNEWPALETTE, so we now own the Foreground palette and we have a license to
// to SelectPalette( hpal, FALSE );
_fOwnsPalette = TRUE;
}
return _QueryNewPalette();
case WM_SYSCOLORCHANGE:
case WM_ENTERSIZEMOVE:
case WM_EXITSIZEMOVE:
case WM_WININICHANGE:
case WM_FONTCHANGE:
v_PropagateMessage(uMsg, wParam, lParam, TRUE);
break;
case WM_PRINT:
// Win95 explorer did this
if (_bbd._hwndView)
SendMessage(_bbd._hwndView, uMsg, wParam, lParam);
break;
#ifdef DEBUG
case WM_ACTIVATE:
// do *not* do any toolbar stuff here. it will mess up the desktop.
// override does that in shbrows2.cpp
TraceMsg(DM_FOCUS, "cbb.wpbs(WM_ACT): => default");
goto DoDefault;
#endif
case WM_SETCURSOR:
if (v_OnSetCursor(lParam))
return TRUE;
goto DoDefault;
case WM_TIMER:
if (wParam == IDT_STARTING_APP_TIMER)
{
AppStarted();
return 0;
}
goto DoDefault;
case WM_CREATE:
if (S_OK != _pbsOuter->OnCreate((LPCREATESTRUCT)lParam))
{
_pbsOuter->OnDestroy();
return -1;
}
g_uMsgFileOpened = RegisterWindowMessage(SH_FILEOPENED);
return 0;
case WM_NOTIFY:
return _pbsOuter->OnNotify((LPNMHDR)lParam);
case WM_COMMAND:
_pbsOuter->OnCommand(wParam, lParam);
break;
case WM_DESTROY:
_pbsOuter->OnDestroy();
break;
case WMC_IEHARD_NAVWARNING:
_ShowIEHardNavWarning();
break;
default:
if (uMsg == g_idMsgGetAuto)
{
//
// According to LauraBu, using WM_GETOBJECT for our private
// purpose will work, but will dramatically slow down
// accessibility apps unless we actually implement one of
// accessibility interfaces. Therefore, we use a registered
// message to get the automation/frame interface out of
// IE/Nashvile frame. (SatoNa)
//
IUnknown* punk;
if (SUCCEEDED(_bbd._pautoSS->QueryInterface(*(IID*)wParam, (void **)&punk)))
return (LRESULT)punk; // Note that it's AddRef'ed by QI.
return 0;
}
else if (uMsg == GetWheelMsg())
{
// Forward the mouse wheel message on to the view window
if (_bbd._hwndView)
{
PostMessage(_bbd._hwndView, uMsg, wParam, lParam);
return 1;
}
// Fall through...
}
DoDefault:
return _DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
// *** IOleWindow methods ***
HRESULT CBaseBrowser2::GetWindow(HWND * lphwnd)
{
*lphwnd = _bbd._hwnd;
return S_OK;
}
HRESULT CBaseBrowser2::GetViewWindow(HWND * lphwnd)
{
*lphwnd = _bbd._hwndView;
return S_OK;
}
HRESULT CBaseBrowser2::ContextSensitiveHelp(BOOL fEnterMode)
{
return E_NOTIMPL;
}
// *** IShellBrowser methods *** (same as IOleInPlaceFrame)
HRESULT CBaseBrowser2::InsertMenusSB(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths)
{
return S_OK;
}
HRESULT CBaseBrowser2::SetMenuSB(HMENU hmenuShared, HOLEMENU holemenuReserved, HWND hwndActiveObject)
{
return S_OK;
}
/*----------------------------------------------------------
Purpose: Remove menus that are shared with other menus from
the given browser menu.
Returns:
Cond: --
*/
HRESULT CBaseBrowser2::RemoveMenusSB(HMENU hmenuShared)
{
return S_OK;
}
HRESULT CBaseBrowser2::SetStatusTextSB(LPCOLESTR lpszStatusText)
{
// Even if we're not toplevel, send this to SendControlMsg
// so events get notified. (Also simplifies CVOCBrowser.)
//
HRESULT hres;
// If we are asked to put some text into the status bar, first save off what is already in pane 0
if (lpszStatusText)
{
LRESULT lIsSimple = FALSE;
// If we have a menu down, then we are already in simple mode. So send the
// text to pane 255 (simple)
_psbOuter->SendControlMsg(FCW_STATUS, SB_ISSIMPLE, 0, 0L, &lIsSimple);
if (!_fHaveOldStatusText && !lIsSimple)
{
WCHAR wzStatusText[MAX_URL_STRING];
LRESULT ret;
// TODO: Put this into a wrapper function because iedisp.cpp does something similar.
// Great when we convert to UNICODE
if (SUCCEEDED(_psbOuter->SendControlMsg(FCW_STATUS, SB_GETTEXTLENGTHW, 0, 0, &ret)) &&
LOWORD(ret) < ARRAYSIZE(wzStatusText))
{
// SB_GETTEXTW is not supported by the status bar control in Win95. Hence, the thunk here.
_psbOuter->SendControlMsg(FCW_STATUS, SB_GETTEXTW, STATUS_PANE_NAVIGATION, (LPARAM)wzStatusText, NULL);
StrCpyNW(_szwOldStatusText, wzStatusText, ARRAYSIZE(_szwOldStatusText));
_fHaveOldStatusText = TRUE;
}
}
hres = _psbOuter->SendControlMsg(FCW_STATUS, SB_SETTEXTW, lIsSimple ? 255 | SBT_NOBORDERS : STATUS_PANE_NAVIGATION | SBT_NOTABPARSING, (LPARAM)lpszStatusText, NULL);
}
else if (_fHaveOldStatusText)
{
VARIANTARG var = {0};
if (_bbd._pctView && SUCCEEDED(_bbd._pctView->Exec(&CGID_Explorer, SBCMDID_GETPANE, PANE_NAVIGATION, NULL, &var))
&& V_UI4(&var) != PANE_NONE)
{
hres = _psbOuter->SendControlMsg(FCW_STATUS, SB_SETTEXTW, V_UI4(&var),(LPARAM)_szwOldStatusText, NULL);
}
else
{
hres = E_FAIL;
}
_fHaveOldStatusText = FALSE;
}
else
{
// No message, and no old status text, so clear what's there.
hres = _psbOuter->SendControlMsg(FCW_STATUS, SB_SETTEXTW, STATUS_PANE_NAVIGATION | SBT_NOTABPARSING , (LPARAM)lpszStatusText, NULL);
}
return hres;
}
HRESULT CBaseBrowser2::EnableModelessSB(BOOL fEnable)
{
// We no longer call _CancelNavigation here, which causes some problems
// when the object calls EnableModeless when we are navigating away
// (see IE bug 4581). Instead, we either cancel or postpone asynchronous
// event while _DisableModeless(). (SatoNa)
//
// If we're NOT top level, assume virtual EnableModelessSB
// handled this request and forwarded it to us. (See CVOCBrowser.)
//
if (fEnable)
{
// Robust against random calls
//
// If this EVAL rips, somebody is calling EMSB(TRUE) without a
// (preceeding) matching EMSB(FALSE). Find and fix!
//
if (EVAL(_cRefCannotNavigate > 0))
{
_cRefCannotNavigate--;
}
// Tell the shell's HTML window to retry pending navigation.
if (_cRefCannotNavigate == 0 && _phtmlWS)
{
_phtmlWS->CanNavigate();
}
}
else
{
_cRefCannotNavigate++;
}
//
// If there is any blocked async operation AND we can navigate now,
// unblock it now.
//
_MayUnblockAsyncOperation();
return S_OK;
}
HRESULT CBaseBrowser2::TranslateAcceleratorSB(LPMSG lpmsg, WORD wID)
{
return S_FALSE;
}
//
// This function starts the navigation to the navigation to the specified
// pidl asynchronously. It cancels the pending navigation synchronously
// if any.
//
// NOTE: This function takes ownership of the pidl -- caller does NOT free pidl!!!
//
void CBaseBrowser2::_NavigateToPidlAsync(LPITEMIDLIST pidl, DWORD dwSBSP, BOOL fDontCallCancel)
{
BOOL fCanSend = FALSE;
TraceMsg(TF_SHDNAVIGATE, "CBB::_NavigateToPidlAsync called");
// _StopAsyncOperation();
if (!fDontCallCancel)
_CancelPendingNavigation(); // which calls _StopAsyncOperation too
else
{
//
// I'm removing this assert because _ShowBlankPage calls this funcion
// with fDontCallCancel==TRUE -- callin _CancelPendingNavigation here
// causes GPF in CDocHostObject::_CancelPendingNavigation. (SatoNa)
//
// ASSERT(_bbd._pidlPending == NULL);
}
ASSERT(!_pidlQueued);
_pidlQueued = pidl;
_dwSBSPQueued = dwSBSP;
// Technically a navigate must be async or we have problems such as:
// 1> object initiating the navigate (mshtml or an object on the page
// or script) gets destroyed when _bbd._psv is removed and then we return
// from this call into the just freed object.
// 2> object initiating the navigate gets called back by an event
//
// In order for Netscape OM compatibility, we must ALWAY have a _bbd._psv or
// _bbd._psvPending, so we go SYNC when we have neither. This avoids problem
// <1> but not problem <2>. As we find faults, we'll work around them.
//
// Check _fAsyncNavigate to avoid navigate when persisting the WebBrowserOC
// This avoids faults in Word97 and MSDN's new InfoViewer -- neither like
// being reentered by an object they are in the middle of initializing.
//
if (_bbd._psv || _bbd._psvPending || _fAsyncNavigate)
{
_PostAsyncOperation(ASYNCOP_GOTO);
}
else
{
// if we are just starting out, we can do this synchronously and
// reduce the window where the IHTMLWindow2 for the frame is undefined
fCanSend = TRUE;
}
// Starting a navigate means we are loading someing...
//
OnReadyStateChange(NULL, READYSTATE_LOADING);
//
// Don't play sound for the first navigation (to avoid multiple
// sounds to be played for a frame-set creation).
//
if ( _bbd._psv
&& IsWindowVisible(_bbd._hwnd)
&& !(_dwSBSPQueued & SBSP_WRITENOHISTORY)
&& !(_dwDocFlags & DOCFLAG_NAVIGATEFROMDOC))
{
IEPlaySound(TEXT("Navigating"), FALSE);
}
if (fCanSend)
{
_SendAsyncOperation(ASYNCOP_GOTO);
}
}
// Now that all navigation paths go through
// _NavigateToPidlAsync we probably don't need to activate async.
// Remove this code...
//
BOOL CBaseBrowser2::_ActivatePendingViewAsync(void)
{
TraceMsg(TF_SHDNAVIGATE, "CBB::_ActivatePendingViewAsync called");
_PreActivatePendingViewAsync();
//
// _bbd._psvPending is for debugging purpose.
//
return _PostAsyncOperation(ASYNCOP_ACTIVATEPENDING);
}
HRESULT _TryActivateOpenWindow(LPCITEMIDLIST pidl)
{
HWND hwnd;
IWebBrowserApp *pwb;
HRESULT hr = WinList_FindFolderWindow(pidl, NULL, &hwnd, &pwb);
if (S_OK == hr)
{
CoAllowSetForegroundWindow(pwb, NULL);
SetForegroundWindow(hwnd);
ShowWindow(hwnd, SW_SHOWNORMAL);
pwb->Release();
}
return hr;
}
HRESULT CBaseBrowser2::BrowseObject(LPCITEMIDLIST pidl, UINT wFlags)
{
HRESULT hr;
BOOL fNewWindow = FALSE;
if (PIDL_NOTHING == pidl)
{
if (!_CanNavigate())
return HRESULT_FROM_WIN32(ERROR_BUSY);
_NavigateToPidlAsync((LPITEMIDLIST)PIDL_NOTHING, wFlags);
return S_OK;
}
if (!_CanNavigate())
return HRESULT_FROM_WIN32(ERROR_BUSY);
LPITEMIDLIST pidlNew = NULL;
int iTravel = 0;
switch (wFlags & (SBSP_RELATIVE | SBSP_ABSOLUTE | SBSP_PARENT | SBSP_NAVIGATEBACK | SBSP_NAVIGATEFORWARD))
{
case SBSP_NAVIGATEBACK:
ASSERT(pidl==NULL || pidl==PIDL_LOCALHISTORY);
iTravel = TLOG_BACK;
break;
case SBSP_NAVIGATEFORWARD:
ASSERT(pidl==NULL || pidl==PIDL_LOCALHISTORY);
iTravel = TLOG_FORE;
break;
case SBSP_RELATIVE:
if (ILIsEmpty(pidl) && (wFlags & SBSP_NEWBROWSER))
fNewWindow = TRUE;
else if (_bbd._pidlCur)
pidlNew = ILCombine(_bbd._pidlCur, pidl);
break;
case SBSP_PARENT:
pidlNew = ILCloneParent(_bbd._pidlCur);
break;
default:
ASSERT(FALSE);
// fall through
case SBSP_ABSOLUTE:
pidlNew = ILClone(pidl);
break;
}
if (iTravel)
{
ITravelLog *ptl;
hr = GetTravelLog(&ptl);
if (SUCCEEDED(hr))
{
hr = ptl->Travel(SAFECAST(this, IShellBrowser*), iTravel);
ptl->Release();
}
_pbsOuter->UpdateBackForwardState();
return hr;
}
// if block is needed for multi-window open. if we're called to open a new
// window, but we're in the middle of navigating, we say we're busy.
if (wFlags & SBSP_SAMEBROWSER)
{
if (wFlags & (SBSP_EXPLOREMODE | SBSP_OPENMODE))
{
// fail this if we're already navigating
if (!_CanNavigate() || (_uActionQueued == ASYNCOP_GOTO))
{
return HRESULT_FROM_WIN32(ERROR_BUSY);
}
}
}
if (pidlNew || fNewWindow)
{
if ((wFlags & (SBSP_NEWBROWSER | SBSP_SAMEBROWSER)) == SBSP_NEWBROWSER)
{
// SBSP_NEWBROWSER + SBSP_EXPLOREMODE
// means never reuse windows, always create a new explorer
if (wFlags & SBSP_EXPLOREMODE)
{
_OpenNewFrame(pidlNew, wFlags); // takes ownership of pidl
}
else
{
hr = _TryActivateOpenWindow(pidlNew);
if ((S_OK == hr) ||
(E_PENDING == hr)) // it will come up eventually
{
hr = S_OK;
ILFree(pidlNew);
}
else
_OpenNewFrame(pidlNew, wFlags); // takes ownership of pidl
}
}
else
{
// NOTE: we assume SBSP_SAMEBROWSER if SBSP_NEWBROWSER is not set
_NavigateToPidlAsync(pidlNew, wFlags /* grfSBSP */); // takes ownership of pidl
}
hr = S_OK;
}
else
{
hr = E_OUTOFMEMORY;
}
return hr;
}
HRESULT CBaseBrowser2::GetControlWindow(UINT id, HWND * lphwnd)
{
return E_FAIL;
}
HRESULT CBaseBrowser2::SendControlMsg(UINT id, UINT uMsg, WPARAM wParam,
LPARAM lParam, LRESULT *pret)
{
HWND hwndControl = NULL;
if (pret)
{
*pret = 0;
}
// If this is statusbar and set text then signal event change.
if ((id == FCW_STATUS) && (uMsg == SB_SETTEXT || uMsg == SB_SETTEXTW) && // trying to set status text
(!(wParam & SBT_OWNERDRAW))) // we don't own the window -- this can't work
{
// When browser or java perf timing mode is enabled, use "Done" or "Applet Started"
// in the status bar to get load time.
if(g_dwStopWatchMode && (g_dwStopWatchMode & (SPMODE_BROWSER | SPMODE_JAVA)))
{
StopWatch_MarkJavaStop((LPSTR)lParam, _bbd._hwnd, (uMsg == SB_SETTEXTW));
}
if (uMsg == SB_SETTEXTW)
{
FireEvent_DoInvokeStringW(_bbd._pautoEDS, DISPID_STATUSTEXTCHANGE, (LPWSTR)lParam);
}
else
{
FireEvent_DoInvokeString(_bbd._pautoEDS, DISPID_STATUSTEXTCHANGE, (LPSTR)lParam);
}
}
HRESULT hres = _psbOuter->GetControlWindow(id, &hwndControl);
if (SUCCEEDED(hres))
{
LRESULT ret = SendMessage(hwndControl, uMsg, wParam, lParam);
if (pret)
{
*pret = ret;
}
}
return hres;
}
HRESULT CBaseBrowser2::QueryActiveShellView(struct IShellView ** ppshv)
{
IShellView * psvRet = _bbd._psv;
if ( _fCreateViewWindowPending )
{
ASSERT( _bbd._psvPending );
psvRet = _bbd._psvPending;
}
//
// We have both psv and hwndView after the completion of view creation.
//
*ppshv = psvRet;
if (psvRet)
{
psvRet->AddRef();
return NOERROR;
}
return E_FAIL;
}
HRESULT CBaseBrowser2::OnViewWindowActive(struct IShellView * psv)
{
AssertMsg((!_bbd._psv || IsSameObject(_bbd._psv, psv)),
TEXT("CBB::OnViewWindowActive _bbd._psv(%x)!=psv(%x)"),
psv, _bbd._psv);
_pbsOuter->_OnFocusChange(ITB_VIEW);
return S_OK;
}
HRESULT CBaseBrowser2::SetToolbarItems(LPTBBUTTON pViewButtons, UINT nButtons, UINT uFlags)
{
return NOERROR;
}
//
// Notes: pidlNew will be freed
//
HRESULT CBaseBrowser2::_OpenNewFrame(LPITEMIDLIST pidlNew, UINT wFlags)
{
UINT uFlags = COF_CREATENEWWINDOW;
if (wFlags & SBSP_EXPLOREMODE)
uFlags |= COF_EXPLORE;
else
{
// maintain the same class if possible
if (IsNamedWindow(_bbd._hwnd, TEXT("IEFrame")))
uFlags |= COF_IEXPLORE;
}
IBrowserService *pbs;
ITravelLog *ptlClone = NULL;
DWORD bid = BID_TOPFRAMEBROWSER;
if (!(wFlags & SBSP_NOTRANSFERHIST))
{
if (SUCCEEDED(_pspOuter->QueryService(SID_STopFrameBrowser, IID_PPV_ARG(IBrowserService, &pbs))))
{
ITravelLog *ptl;
if (SUCCEEDED(pbs->GetTravelLog(&ptl)))
{
if (SUCCEEDED(ptl->Clone(&ptlClone)))
{
ptlClone->UpdateEntry(pbs, FALSE);
bid = pbs->GetBrowserIndex();
}
ptl->Release();
}
pbs->Release();
}
}
INotifyAppStart * pnasTop;
HRESULT hr = _pspOuter->QueryService(SID_STopLevelBrowser, IID_PPV_ARG(INotifyAppStart, &pnasTop));
if (SUCCEEDED(hr))
{
pnasTop->AppStarting();
pnasTop->Release();
}
hr = SHOpenNewFrame(pidlNew, ptlClone, bid, uFlags);
if (ptlClone)
ptlClone->Release();
return hr;
}
//
// This is a helper member of CBaseBroaser class (non-virtual), which
// returns the effective client area. We get this rectangle by subtracting
// the status bar area from the real client area.
//
HRESULT CBaseBrowser2::_GetEffectiveClientArea(LPRECT lprectBorder, HMONITOR hmon)
{
// (derived class overrides w/ GetEffectiveClientRect for FCIDM_STATUS etc.)
//
// This code should only be hit in the WebBrowserOC case, but I don't
// have a convenient assert for that... [mikesh]
//
ASSERT(hmon == NULL);
GetClientRect(_bbd._hwnd, lprectBorder);
return NOERROR;
}
HRESULT CBaseBrowser2::RequestBorderSpace(LPCBORDERWIDTHS pborderwidths)
{
TraceMsg(TF_SHDUIACTIVATE, "UIW::ReqestBorderSpace pborderwidths=%x,%x,%x,%x",
pborderwidths->left, pborderwidths->top, pborderwidths->right, pborderwidths->bottom);
return S_OK;
}
//
// This is an implementation of IOleInPlaceUIWindow::GetBorder.
//
// This function returns the bounding rectangle for the active object.
// It gets the effective client area, then subtract border area taken by
// all "frame" toolbars.
//
HRESULT CBaseBrowser2::GetBorder(LPRECT lprectBorder)
{
_pbsOuter->_GetViewBorderRect(lprectBorder);
return S_OK;
}
//
// NOTES: We used to handle the border space negotiation in CShellBrowser
// and block it for OC (in Beta-1 of IE4), but I've changed it so that
// CBaseBrowser2 always handles it. It simplifies our implementation and
// also allows a DocObject to put toolbars within the frameset, which is
// requested by the Excel team. (SatoNa)
//
HRESULT CBaseBrowser2::SetBorderSpace(LPCBORDERWIDTHS pborderwidths)
{
if (pborderwidths)
{
TraceMsg(TF_SHDUIACTIVATE, "UIW::SetBorderSpace pborderwidths=%x,%x,%x,%x",
pborderwidths->left, pborderwidths->top, pborderwidths->right, pborderwidths->bottom);
_rcBorderDoc = *pborderwidths;
}
else
{
TraceMsg(TF_SHDUIACTIVATE, "UIW::SetBorderSpace pborderwidths=NULL");
SetRect(&_rcBorderDoc, 0, 0, 0, 0);
}
_pbsOuter->_UpdateViewRectSize();
return S_OK;
}
HRESULT CBaseBrowser2::SetActiveObject(IOleInPlaceActiveObject *pActiveObject, LPCOLESTR pszObjName)
{
TraceMsg(TF_SHDUIACTIVATE, "UIW::SetActiveObject called %x", pActiveObject);
ATOMICRELEASE(_pact);
if (pActiveObject)
{
_pact = pActiveObject;
_pact->AddRef();
}
return S_OK;
}
/***********************************************************************\
FUNCTION: _AddFolderOptionsSheets
DESCRIPTION:
Add the sheets for the "Folder Options" dialog. These sheets
come from the IShelLView object.
\***********************************************************************/
HRESULT CBaseBrowser2::_AddFolderOptionsSheets(DWORD dwReserved, LPFNADDPROPSHEETPAGE pfnAddPropSheetPage, LPPROPSHEETHEADER ppsh)
{
// Add the normal Folder Option sheets.
IShellPropSheetExt * ppsx;
HRESULT hr = _pbsOuter->CreateBrowserPropSheetExt(IID_PPV_ARG(IShellPropSheetExt, &ppsx));
if (SUCCEEDED(hr))
{
hr = ppsx->AddPages(AddPropSheetPage, (LPARAM)ppsh);
ppsx->Release();
}
// Let the view add additional pages. The exception will be FTP Folders because it exists to add
// internet pages and we don't want them here. However, if the above failed, then
// we also want to fall back to this. One of the cases this fixes if if the
// browser fell back to legacy FTP support (web browser), then the above call will
// fail on browser only, and we want to fall thru here to add the internet options. Which
// is appropriate for the fallback legacy FTP case because the menu will only have "Internet Options"
// on it.
if (FAILED(hr) || !IsBrowserFrameOptionsSet(_bbd._psf, BFO_BOTH_OPTIONS))
{
EVAL(SUCCEEDED(hr = _bbd._psv->AddPropertySheetPages(dwReserved, pfnAddPropSheetPage, (LPARAM)ppsh)));
}
return hr;
}
/***********************************************************************\
FUNCTION: _AddInternetOptionsSheets
DESCRIPTION:
Add the sheets for the "Internet Options" dialog. These sheets
come from the browser.
\***********************************************************************/
HRESULT CBaseBrowser2::_AddInternetOptionsSheets(DWORD dwReserved, LPFNADDPROPSHEETPAGE pfnAddPropSheetPage, LPPROPSHEETHEADER ppsh)
{
HRESULT hr;
// Add the normal Internet Control Panel sheets. (This won't work when viewing FTP)
if (_bbd._psvPending)
hr = _bbd._psvPending->AddPropertySheetPages(dwReserved, pfnAddPropSheetPage, (LPARAM)ppsh);
else
hr = _bbd._psv->AddPropertySheetPages(dwReserved, pfnAddPropSheetPage, (LPARAM)ppsh);
return hr;
}
/***********************************************************************\
FUNCTION: _DoOptions
DESCRIPTION:
The user selected either "Folder Options" or "Internet Options" from
the View or Tools menu (or where ever it lives this week). The logic
in this function is a little strange because sometimes the caller doesn't
tell us which we need to display in the pvar. If not, we need to calculate
what to use.
1. If it's a URL pidl (HTTP, GOPHER, etc) then we assume it's the
"Internet Options" dialog. We then use psv->AddPropertySheetPages()
to create the "Internet Options" property sheets.
2. If it's in the shell (or FTP because it needs folder options), then
we assume it's "Folder Options" the user selected. In that case,
we get the property sheets using _pbsOuter->CreateBrowserPropSheetExt().
Now it gets weird. The PMs want FTP to have both "Internet Options" and
"Folder Options". If the pvar param is NULL, assume it's "Folder Options".
If it was "Internet Options" in the internet case, then I will pass an
pvar forcing Internet Options.
NOTE: SBO_NOBROWSERPAGES means "Folder Options". I'm guessing browser refers
to the original explorer browser.
\***********************************************************************/
HDPA CBaseBrowser2::s_hdpaOptionsHwnd = NULL;
void CBaseBrowser2::_DoOptions(VARIANT* pvar)
{
// Step 1. Determine what sheets to use.
DWORD dwFlags = SBO_DEFAULT;
TCHAR szCaption[MAX_PATH];
if (!_bbd._psv)
return;
// Did the caller explicitly tell us which to use?
if (pvar && pvar->vt == VT_I4)
dwFlags = pvar->lVal;
else if (_bbd._pidlCur)
{
// don't show the Folder Option pages if
// 1. we're browsing the internet (excluding FTP), or
// 2. if we're browsing a local file (not a folder), like a local .htm file.
if (IsBrowserFrameOptionsSet(_bbd._psf, BFO_RENAME_FOLDER_OPTIONS_TOINTERNET))
{
// SBO_NOBROWSERPAGES means don't add the "Folder Options" pages.
dwFlags = SBO_NOBROWSERPAGES;
}
}
PROPSHEETHEADER psh;
HPROPSHEETPAGE rPages[MAX_PAGES];
psh.dwSize = SIZEOF(psh);
psh.dwFlags = PSH_DEFAULT | PSH_USECALLBACK;
psh.hInstance = MLGetHinst();
psh.hwndParent = _bbd._hwnd;
psh.pszCaption = szCaption;
psh.nPages = 0;
psh.nStartPage = 0;
psh.phpage = rPages;
psh.pfnCallback = _OptionsPropSheetCallback;
// Step 2. Now add "Internet Options" or "Folder Options" sheets.
if (dwFlags == SBO_NOBROWSERPAGES)
{
// They don't want folder pages. (The used to refer to it as browser)
EVAL(SUCCEEDED(_AddInternetOptionsSheets(0, AddPropSheetPage, &psh)));
MLLoadString(IDS_INTERNETOPTIONS, szCaption, ARRAYSIZE(szCaption));
}
else
{
EVAL(SUCCEEDED(_AddFolderOptionsSheets(0, AddPropSheetPage, &psh)));
MLLoadString(IDS_FOLDEROPTIONS, szCaption, ARRAYSIZE(szCaption));
}
ULONG_PTR uCookie = 0;
SHActivateContext(&uCookie);
if (psh.nPages == 0)
{
SHRestrictedMessageBox(_bbd._hwnd);
}
else
{
// Step 3. Display the dialog
_bbd._psv->EnableModelessSV(FALSE);
INT_PTR iPsResult = PropertySheet(&psh);
_SyncDPA();
_bbd._psv->EnableModelessSV(TRUE);
if (ID_PSREBOOTSYSTEM == iPsResult)
{
// The "offline folders" prop page will request a reboot if the user
// has enabled or disabled client-side-caching.
RestartDialog(_bbd._hwnd, NULL, EWX_REBOOT);
}
}
if (uCookie)
{
SHDeactivateContext(uCookie);
}
}
// we're here because our prop sheet just closed
// we need to purge it from the hwnd list
// check all the hwnds because 1) there's probably
// only one anyway, 2) paranoia.
void CBaseBrowser2::_SyncDPA()
{
ENTERCRITICAL;
if (s_hdpaOptionsHwnd != NULL)
{
int i, cPtr = DPA_GetPtrCount(s_hdpaOptionsHwnd);
ASSERT(cPtr >= 0);
// remove handles for windows which aren't there anymore
for (i = cPtr - 1; i >= 0; i--)
{
HWND hwnd = (HWND)DPA_GetPtr(s_hdpaOptionsHwnd, i);
if (!IsWindow(hwnd))
{
DPA_DeletePtr(s_hdpaOptionsHwnd, i);
cPtr--;
}
}
// if there aren't any windows left then clean up the hdpa
if (cPtr == 0)
{
DPA_Destroy(s_hdpaOptionsHwnd);
s_hdpaOptionsHwnd = NULL;
}
}
LEAVECRITICAL;
}
int CALLBACK
CBaseBrowser2::_OptionsPropSheetCallback(HWND hwndDlg, UINT uMsg, LPARAM lParam)
{
switch (uMsg)
{
case PSCB_INITIALIZED:
{
ENTERCRITICAL;
if (s_hdpaOptionsHwnd == NULL)
{
// low mem -> Create failure -> don't track hwnd
s_hdpaOptionsHwnd = DPA_Create(1);
}
if (s_hdpaOptionsHwnd != NULL)
{
// low mem -> AppendPtr array expansion failure -> don't track hwnd
DPA_AppendPtr(s_hdpaOptionsHwnd, hwndDlg);
}
LEAVECRITICAL;
}
break;
}
return 0;
}
HRESULT CBaseBrowser2::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds,
OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
{
if (rgCmds == NULL)
return E_INVALIDARG;
if (pguidCmdGroup == NULL)
{
for (ULONG i = 0 ; i < cCmds; i++)
{
rgCmds[i].cmdf = 0;
switch (rgCmds[i].cmdID)
{
case OLECMDID_SETDOWNLOADSTATE:
case OLECMDID_UPDATECOMMANDS:
rgCmds[i].cmdf = OLECMDF_ENABLED;
break;
case OLECMDID_STOP:
case OLECMDID_STOPDOWNLOAD:
if (_bbd._psvPending) // pending views are stoppable
{
rgCmds[i].cmdf = OLECMDF_ENABLED;
}
else if (_bbd._pctView) // current views may support stop also
{
_bbd._pctView->QueryStatus(NULL, 1, &rgCmds[i], pcmdtext);
}
break;
default:
// set to zero above
if (_bbd._pctView)
{
// Recursion check. Avoid looping for those command IDs where Trident bounces
// back up to us.
//
if (_fInQueryStatus)
break;
_fInQueryStatus = TRUE;
_bbd._pctView->QueryStatus(NULL, 1, &rgCmds[i], pcmdtext);
_fInQueryStatus = FALSE;
}
break;
}
}
}
else if (IsEqualGUID(CGID_Explorer, *pguidCmdGroup))
{
for (ULONG i=0 ; i < cCmds ; i++)
{
switch (rgCmds[i].cmdID)
{
case SBCMDID_ADDTOFAVORITES:
case SBCMDID_CREATESHORTCUT:
rgCmds[i].cmdf = OLECMDF_ENABLED; // support these unconditionally
break;
case SBCMDID_CANCELNAVIGATION:
rgCmds[i].cmdf = _bbd._psvPending ? OLECMDF_ENABLED : 0;
break;
case SBCMDID_OPTIONS:
rgCmds[i].cmdf = OLECMDF_ENABLED;
break;
default:
rgCmds[i].cmdf = 0;
break;
}
}
}
else if (IsEqualGUID(CGID_ShellDocView, *pguidCmdGroup))
{
for (ULONG i=0 ; i < cCmds ; i++)
{
ITravelLog *ptl;
switch (rgCmds[i].cmdID)
{
case SHDVID_CANGOBACK:
rgCmds[i].cmdf = FALSE; // Assume False
if (SUCCEEDED(GetTravelLog(&ptl)))
{
ASSERT(ptl);
if (S_OK == ptl->GetTravelEntry(SAFECAST(this, IShellBrowser *), TLOG_BACK, NULL))
rgCmds[i].cmdf = TRUE;
ptl->Release();
}
break;
case SHDVID_CANGOFORWARD:
rgCmds[i].cmdf = FALSE; // Assume False
if (SUCCEEDED(GetTravelLog(&ptl)))
{
ASSERT(ptl);
if (S_OK == ptl->GetTravelEntry(SAFECAST(this, IShellBrowser *), TLOG_FORE, NULL))
rgCmds[i].cmdf = TRUE;
ptl->Release();
}
break;
case SHDVID_PRINTFRAME:
case SHDVID_MIMECSETMENUOPEN:
case SHDVID_FONTMENUOPEN:
if (_bbd._pctView)
_bbd._pctView->QueryStatus(pguidCmdGroup, 1, &rgCmds[i], pcmdtext);
break;
default:
rgCmds[i].cmdf = 0;
break;
}
}
}
else
{
return OLECMDERR_E_UNKNOWNGROUP;
}
return S_OK;
}
HRESULT CBaseBrowser2::_ShowBlankPage(LPCTSTR pszAboutUrl, LPCITEMIDLIST pidlIntended)
{
// Never execute this twice.
if (_fNavigatedToBlank)
{
TraceMsg(TF_WARNING, "Re-entered CBaseBrowser2::_ShowBlankPage");
return E_FAIL;
}
_fNavigatedToBlank = TRUE;
BSTR bstrURL;
TCHAR szPendingURL[MAX_URL_STRING + 1];
TCHAR *pszOldUrl = NULL;
szPendingURL[0] = TEXT('#');
HRESULT hres;
if (pidlIntended)
{
hres = ::IEGetNameAndFlags(pidlIntended, SHGDN_FORPARSING, szPendingURL + 1, SIZECHARS(szPendingURL)-1, NULL);
if (S_OK == hres)
pszOldUrl = szPendingURL;
}
hres = CreateBlankURL(&bstrURL, pszAboutUrl, pszOldUrl);
if (SUCCEEDED(hres))
{
LPITEMIDLIST pidlTemp;
hres = IECreateFromPathW(bstrURL, &pidlTemp);
if (SUCCEEDED(hres))
{
//
// Note that we pass TRUE as fDontCallCancel to asynchronously
// cancel the current view. Otherwise, we hit GPF in CDocHostObject::
// _CancelPendingNavigation.
//
_NavigateToPidlAsync(pidlTemp, 0, TRUE); // takes ownership of pidl
}
SysFreeString(bstrURL);
}
return hres;
}
int CALLBACK _PunkRelease(void * p, void * pData)
{
IUnknown* punk = (IUnknown*)p;
punk->Release();
return 1;
}
void CBaseBrowser2::_DLMDestroy(void)
{
if (_hdpaDLM)
{
DPA_DestroyCallback(_hdpaDLM, _PunkRelease, NULL);
_hdpaDLM = NULL;
}
}
HRESULT CBaseBrowser2::InitializeDownloadManager()
{
_hdpaDLM = DPA_Create(4);
return S_OK;
}
//
// DLM = DownLoad Manager
//
void CBaseBrowser2::_DLMUpdate(MSOCMD* prgCmd)
{
ASSERT(prgCmd->cmdID == OLECMDID_STOPDOWNLOAD);
for (int i = DPA_GetPtrCount(_hdpaDLM) - 1; i >= 0; i--)
{
IOleCommandTarget* pcmdt = (IOleCommandTarget*)DPA_GetPtr(_hdpaDLM, i);
prgCmd->cmdf = 0;
pcmdt->QueryStatus(NULL, 1, prgCmd, NULL);
if (prgCmd->cmdf & MSOCMDF_ENABLED)
{
// We found one downloading guy, skip others.
break;
}
else
{
// This guy is no longer busy, remove it from the list,
// and continue.
DPA_DeletePtr(_hdpaDLM, i);
pcmdt->Release();
}
}
}
void CBaseBrowser2::_DLMRegister(IUnknown* punk)
{
// Check if it's already registered.
for (int i = 0; i < DPA_GetPtrCount(_hdpaDLM); i++)
{
IOleCommandTarget* pcmdt = (IOleCommandTarget*)DPA_GetPtr(_hdpaDLM, i);
if (IsSameObject(pcmdt, punk))
{
// Already registered, don't register.
return;
}
}
IOleCommandTarget* pcmdt;
HRESULT hres = punk->QueryInterface(IID_PPV_ARG(IOleCommandTarget, &pcmdt));
if (SUCCEEDED(hres))
{
if (DPA_AppendPtr(_hdpaDLM, pcmdt) == -1)
{
pcmdt->Release();
}
}
}
//
// This function updates the _fDescendentNavigate flag.
//
// ALGORITHM:
// If pvaragIn->lVal has some non-zero value, we set _fDescendentNavigate.
// Otherwise, we ask the current view to see if it has something to stop.
//
HRESULT CBaseBrowser2::_setDescendentNavigate(VARIANTARG *pvarargIn)
{
ASSERT(!pvarargIn || pvarargIn->vt == VT_I4 || pvarargIn->vt == VT_BOOL || pvarargIn->vt == VT_UNKNOWN);
if (!pvarargIn || !pvarargIn->lVal)
{
MSOCMD rgCmd;
rgCmd.cmdID = OLECMDID_STOPDOWNLOAD;
rgCmd.cmdf = 0;
if (_bbd._pctView)
_bbd._pctView->QueryStatus(NULL, 1, &rgCmd, NULL);
//
// If and only if the view says "I'm not navigating any more",
// we'll ask the same question to each registered objects.
//
if (_hdpaDLM && !(rgCmd.cmdf & MSOCMDF_ENABLED))
{
_DLMUpdate(&rgCmd);
}
_fDescendentNavigate = (rgCmd.cmdf & MSOCMDF_ENABLED) ? TRUE:FALSE;
}
else
{
if (_hdpaDLM && pvarargIn->vt == VT_UNKNOWN)
{
ASSERT(pvarargIn->punkVal);
_DLMRegister(pvarargIn->punkVal);
}
_fDescendentNavigate = TRUE;
}
return S_OK;
}
void CBaseBrowser2::_CreateShortcutOnDesktop(IUnknown *pUnk, BOOL fUI)
{
ISHCUT_PARAMS ShCutParams = {0};
IWebBrowser *pwb = NULL;
IDispatch *pdisp = NULL;
IHTMLDocument2 *pDoc = NULL;
LPITEMIDLIST pidlCur = NULL;
BSTR bstrTitle = NULL;
BSTR bstrURL = NULL;
if (!fUI || (MLShellMessageBox(_bbd._hwnd,
MAKEINTRESOURCE(IDS_CREATE_SHORTCUT_MSG),
MAKEINTRESOURCE(IDS_TITLE),
MB_OKCANCEL) == IDOK))
{
TCHAR szPath[MAX_PATH];
if (SHGetSpecialFolderPath(NULL, szPath, CSIDL_DESKTOPDIRECTORY, TRUE))
{
TCHAR szName[MAX_URL_STRING];
HRESULT hr;
if (pUnk)
{
hr = IUnknown_QueryService(pUnk, SID_SHlinkFrame, IID_PPV_ARG(IWebBrowser, &pwb));
if (hr)
goto Cleanup;
hr = pUnk->QueryInterface(IID_PPV_ARG(IHTMLDocument2, &pDoc));
if (hr)
goto Cleanup;
hr = pDoc->get_URL(&bstrURL);
if (hr)
goto Cleanup;
hr = pDoc->get_title(&bstrTitle);
if (hr)
goto Cleanup;
if (SysStringLen(bstrTitle) == 0)
{
StrCpyNW(szName, bstrURL, ARRAYSIZE(szName));
ShCutParams.pszTitle = PathFindFileName(szName);
}
else
{
StrCpyNW(szName, bstrTitle, ARRAYSIZE(szName));
ShCutParams.pszTitle = szName;
}
pidlCur = PidlFromUrl(bstrURL);
if (!pidlCur)
goto Cleanup;
ShCutParams.pidlTarget = pidlCur;
}
else
{
hr = QueryService(SID_SHlinkFrame, IID_PPV_ARG(IWebBrowser, &pwb));
if (hr)
goto Cleanup;
hr = pwb->get_Document(&pdisp);
if (hr)
goto Cleanup;
hr = pdisp->QueryInterface(IID_PPV_ARG(IHTMLDocument2, &pDoc));
if (hr)
goto Cleanup;
ShCutParams.pidlTarget = _bbd._pidlCur;
if(_bbd._pszTitleCur)
{
StrCpyNW(szName, _bbd._pszTitleCur, ARRAYSIZE(szName));
ShCutParams.pszTitle = szName;
}
else
{
::IEGetNameAndFlags(_bbd._pidlCur, SHGDN_INFOLDER, szName, SIZECHARS(szName), NULL);
ShCutParams.pszTitle = PathFindFileName(szName);
}
}
ShCutParams.pszDir = szPath;
ShCutParams.pszOut = NULL;
ShCutParams.bUpdateProperties = FALSE;
ShCutParams.bUniqueName = TRUE;
ShCutParams.bUpdateIcon = TRUE;
hr = pwb->QueryInterface(IID_PPV_ARG(IOleCommandTarget, &ShCutParams.pCommand));
ASSERT((S_OK == hr) && (BOOLIFY(ShCutParams.pCommand)));
if (hr)
goto Cleanup;
ShCutParams.pDoc = pDoc;
ShCutParams.pDoc->AddRef();
hr = CreateShortcutInDirEx(&ShCutParams);
AssertMsg(SUCCEEDED(hr), TEXT("CDOH::_CSOD CreateShortcutInDir failed %x"), hr);
if (hr)
goto Cleanup;
}
else
{
TraceMsg(DM_ERROR, "CSB::_CSOD SHGetSFP(DESKTOP) failed");
}
}
Cleanup:
SysFreeString(bstrTitle);
SysFreeString(bstrURL);
ILFree(pidlCur);
SAFERELEASE(ShCutParams.pDoc);
SAFERELEASE(ShCutParams.pCommand);
SAFERELEASE(pwb);
SAFERELEASE(pdisp);
SAFERELEASE(pDoc);
}
void CBaseBrowser2::_AddToFavorites(LPCITEMIDLIST pidl, LPCTSTR pszTitle, BOOL fDisplayUI)
{
HRESULT hr;
IWebBrowser *pwb = NULL;
IOleCommandTarget *pcmdt = NULL;
if (SHIsRestricted2W(_bbd._hwnd, REST_NoFavorites, NULL, 0))
return;
hr = QueryService(SID_SHlinkFrame, IID_PPV_ARG(IWebBrowser, &pwb));
if (S_OK == hr)
{
hr = pwb->QueryInterface(IID_PPV_ARG(IOleCommandTarget, &pcmdt));
ASSERT((S_OK == hr) && (BOOLIFY(pcmdt)));
pwb->Release();
}
//there's a small window where _pidlCur can be freed while AddToFavorites is coming up,
// so use a local copy instead
LPITEMIDLIST pidlCur = NULL;
if (!pidl)
pidlCur = ILClone(_bbd._pidlCur);
if (pidl || pidlCur)
AddToFavorites(_bbd._hwnd, pidl ? pidl : pidlCur, pszTitle, fDisplayUI, pcmdt, NULL);
ILFree(pidlCur);
SAFERELEASE(pcmdt);
}
HRESULT CBaseBrowser2::_OnCoCreateDocument(VARIANTARG *pvarargOut)
{
HRESULT hres;
//
// Cache the class factory object and lock it (leave it loaded)
//
if (_pcfHTML == NULL)
{
TraceMsg(DM_COCREATEHTML, "CBB::_OnCoCreateDoc called first time (this=%x)", this);
hres = CoGetClassObject(CLSID_HTMLDocument, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
0, IID_PPV_ARG(IClassFactory, &_pcfHTML));
if (SUCCEEDED(hres))
{
hres = _pcfHTML->LockServer(TRUE);
if (FAILED(hres))
{
_pcfHTML->Release();
_pcfHTML = NULL;
return hres;
}
}
else
{
return hres;
}
}
TraceMsg(DM_COCREATEHTML, "CBB::_OnCoCreateDoc creating an instance (this=%x)", this);
hres = _pcfHTML->CreateInstance(NULL, IID_PPV_ARG(IUnknown, &pvarargOut->punkVal));
if (SUCCEEDED(hres))
{
pvarargOut->vt = VT_UNKNOWN;
}
else
{
pvarargOut->vt = VT_EMPTY;
}
return hres;
}
// fill a buffer with a variant, return a pointer to that buffer on success of the conversion
LPCTSTR VariantToString(const VARIANT *pv, LPTSTR pszBuf, UINT cch)
{
*pszBuf = 0;
if (pv && pv->vt == VT_BSTR && pv->bstrVal)
{
StrCpyN(pszBuf, pv->bstrVal, cch);
if (*pszBuf)
return pszBuf;
}
return NULL;
}
HRESULT CBaseBrowser2::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt,
VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
{
HRESULT hres = OLECMDERR_E_NOTSUPPORTED;
if (pguidCmdGroup == NULL)
{
switch(nCmdID)
{
case OLECMDID_CLOSE:
HWND hwnd;
GetWindow(&hwnd);
PostMessage(hwnd, WM_CLOSE, 0, 0);
hres = S_OK;
break;
// CBaseBrowser2 doesn't actually do the toolbar -- itbar does, forward this
case OLECMDID_UPDATECOMMANDS:
_NotifyCommandStateChange();
hres = S_OK;
break;
case OLECMDID_SETDOWNLOADSTATE:
ASSERT(pvarargIn);
if (pvarargIn)
{
_setDescendentNavigate(pvarargIn);
hres = _updateNavigationUI();
}
else
{
hres = E_INVALIDARG;
}
break;
case OLECMDID_REFRESH:
if (_bbd._pctView) // we must check!
hres = _bbd._pctView->Exec(NULL, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
else if (_bbd._psv)
{
_bbd._psv->Refresh();
hres = S_OK;
}
break;
//
// When Exec(OLECMDID_STOP) is called either by the containee (the
// current document) or the automation service object, we cancel
// the pending navigation (if any), then tell the current document
// to stop the go-going download in that page.
//
case OLECMDID_STOP:
// cant stop if we are modeless
if (S_FALSE == _DisableModeless())
{
LPITEMIDLIST pidlIntended = (_bbd._pidlPending) ? ILClone(_bbd._pidlPending) : NULL;
_CancelPendingNavigation();
// the _bbd._pctView gives us a _StopCurrentView()
_pbsOuter->_ExecChildren(_bbd._pctView, TRUE, NULL, OLECMDID_STOP, OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL); // Exec
if (!_bbd._pidlCur)
{
TCHAR szResURL[MAX_URL_STRING];
hres = MLBuildResURLWrap(TEXT("shdoclc.dll"),
HINST_THISDLL,
ML_CROSSCODEPAGE,
TEXT("navcancl.htm"),
szResURL,
ARRAYSIZE(szResURL),
TEXT("shdocvw.dll"));
if (SUCCEEDED(hres))
{
_ShowBlankPage(szResURL, pidlIntended);
}
}
if (pidlIntended)
{
ILFree(pidlIntended);
}
}
hres = S_OK;
break;
// handled in basesb so IWebBrowser::ExecWB gets this
// since this used to be in shbrowse, make sure we do
// it only if _fTopBrowser
case OLECMDID_FIND:
#define TBIDM_SEARCH 0x123 // defined in browseui\itbdrop.h
// Check restriction here cuz Win95 didn't check in SHFindFiles like it now does.
if (!SHRestricted(REST_NOFIND) && _fTopBrowser)
{
if (!_bbd._pctView || FAILED(_bbd._pctView->Exec(NULL, nCmdID, nCmdexecopt, pvarargIn, pvarargOut)))
{
if (pvarargIn && pvarargIn->vt == VT_UNKNOWN)
{
ASSERT(pvarargIn->punkVal);
VARIANT var = {0};
var.vt = VT_I4;
var.lVal = -1;
if (SUCCEEDED(IUnknown_Exec(pvarargIn->punkVal, &CLSID_CommonButtons, TBIDM_SEARCH, 0, NULL, &var)))
break;
}
SHFindFiles(_bbd._pidlCur, NULL);
}
}
break;
case OLECMDID_HTTPEQUIV_DONE:
case OLECMDID_HTTPEQUIV:
hres = OnHttpEquiv(_bbd._psv, (OLECMDID_HTTPEQUIV_DONE == nCmdID), pvarargIn, pvarargOut);
break;
case OLECMDID_PREREFRESH:
// Tell the shell's HTML window we have a new document
// Fall through default
if (_phtmlWS)
{
_phtmlWS->ViewActivated();
}
// Binder prints by reflecting the print back down. do the same here
// Note: we may want to do the same for _PRINTPREVIEW, _PROPERTIES, _STOP, etc.
// The null command group should all go down, no need to stop these at the pass.
default:
if (_bbd._pctView) // we must check!
hres = _bbd._pctView->Exec(NULL, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
else
hres = OLECMDERR_E_NOTSUPPORTED;
break;
}
}
else if (IsEqualGUID(CGID_MSHTML, *pguidCmdGroup))
{
if (_bbd._pctView) // we must check!
hres = _bbd._pctView->Exec(&CGID_MSHTML, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
else
hres = OLECMDERR_E_NOTSUPPORTED;
}
else if (IsEqualGUID(CGID_Explorer, *pguidCmdGroup))
{
switch(nCmdID)
{
case SBCMDID_CREATESHORTCUT:
if ( pvarargIn
&& (V_VT(pvarargIn) == VT_UNKNOWN)
)
{
_CreateShortcutOnDesktop(V_UNKNOWN(pvarargIn), nCmdexecopt & OLECMDEXECOPT_PROMPTUSER);
}
else
{
_CreateShortcutOnDesktop(NULL, nCmdexecopt & OLECMDEXECOPT_PROMPTUSER);
}
hres = S_OK;
break;
case SBCMDID_ADDTOFAVORITES:
{
LPITEMIDLIST pidl = NULL;
//if someone doesn't pass a path in, _AddToFavorites will use the current page
if ((pvarargIn != NULL) && (pvarargIn->vt == VT_BSTR))
IECreateFromPath(pvarargIn->bstrVal, &pidl);
TCHAR szTitle[128];
LPTSTR pszTitle = NULL;
if (pvarargOut)
pszTitle = (LPTSTR)VariantToString(pvarargOut, szTitle, ARRAYSIZE(szTitle)); // may be NULL
else
{
if (_bbd._pszTitleCur)
pszTitle = StrCpyNW(szTitle, _bbd._pszTitleCur, ARRAYSIZE(szTitle));
}
_AddToFavorites(pidl, pszTitle, nCmdexecopt & OLECMDEXECOPT_PROMPTUSER);
if (pidl)
ILFree(pidl);
hres = S_OK;
}
break;
case SBCMDID_OPTIONS:
_DoOptions(pvarargIn);
break;
case SBCMDID_CANCELNAVIGATION:
TraceMsg(DM_NAV, "ief NAV::%s called when _bbd._pidlCur==%x, _bbd._psvPending==%x",
TEXT("Exec(SBCMDID_CANCELNAV) called"),
_bbd._pidlCur, _bbd._psvPending);
// Check if this is sync or async
if (pvarargIn && pvarargIn->vt == VT_I4 && pvarargIn->lVal)
{
TraceMsg(DM_WEBCHECKDRT, "CBB::Exec calling _CancelPendingNavigation");
_CancelPendingNavigation();
}
else
{
//
// We must call ASYNC version in this case because this call
// is from the pending view itself.
//
LPITEMIDLIST pidlIntended = (_bbd._pidlPending) ? ILClone(_bbd._pidlPending) : NULL;
_CancelPendingNavigationAsync();
if (!_bbd._pidlCur)
{
if (!_fDontShowNavCancelPage)
{
TCHAR szResURL[MAX_URL_STRING];
if (IsGlobalOffline())
{
hres = MLBuildResURLWrap(TEXT("shdoclc.dll"),
HINST_THISDLL,
ML_CROSSCODEPAGE,
TEXT("offcancl.htm"),
szResURL,
ARRAYSIZE(szResURL),
TEXT("shdocvw.dll"));
if (SUCCEEDED(hres))
{
_ShowBlankPage(szResURL, pidlIntended);
}
}
else
{
hres = MLBuildResURLWrap(TEXT("shdoclc.dll"),
HINST_THISDLL,
ML_CROSSCODEPAGE,
TEXT("navcancl.htm"),
szResURL,
ARRAYSIZE(szResURL),
TEXT("shdocvw.dll"));
if (SUCCEEDED(hres))
{
_ShowBlankPage(szResURL, pidlIntended);
}
}
}
else
{
_fDontShowNavCancelPage = FALSE;
}
}
if (pidlIntended)
ILFree(pidlIntended);
}
hres = S_OK;
break;
case SBCMDID_ASYNCNAVIGATION:
TraceMsg(DM_NAV, "ief NAV::%s called when _bbd._pidlCur==%x, _bbd._psvPending==%x",
TEXT("Exec(SBCMDID_ASYNCNAV) called"),
_bbd._pidlCur, _bbd._psvPending);
//
// We must call ASYNC version in this case because this call
// is from the pending view itself.
//
_SendAsyncNavigationMsg(pvarargIn);
hres = S_OK;
break;
case SBCMDID_COCREATEDOCUMENT:
hres = _OnCoCreateDocument(pvarargOut);
break;
case SBCMDID_HISTSFOLDER:
if (pvarargOut)
{
VariantClearLazy(pvarargOut);
if (NULL == _punkSFHistory)
{
IHistSFPrivate *phsfHistory;
hres = LoadHistoryShellFolder(NULL, &phsfHistory);
if (SUCCEEDED(hres))
{
hres = phsfHistory->QueryInterface(IID_PPV_ARG(IUnknown, &_punkSFHistory));
phsfHistory->Release();
}
}
if (NULL != _punkSFHistory)
{
pvarargOut->vt = VT_UNKNOWN;
pvarargOut->punkVal = _punkSFHistory;
_punkSFHistory->AddRef();
}
}
break;
case SBCMDID_UPDATETRAVELLOG:
{
BOOL fForceUpdate = FALSE;
if (pvarargIn && (VT_I4 == V_VT(pvarargIn)))
{
_fIsLocalAnchor = !!(V_I4(pvarargIn) & TRAVELLOG_LOCALANCHOR);
fForceUpdate = !!(V_I4(pvarargIn) & TRAVELLOG_FORCEUPDATE);
}
_UpdateTravelLog(fForceUpdate);
}
// fall through
case SBCMDID_REPLACELOCATION:
if (pvarargIn && pvarargIn->vt == VT_BSTR)
{
WCHAR wzParsedUrl[MAX_URL_STRING];
LPWSTR pszUrl = pvarargIn->bstrVal;
LPITEMIDLIST pidl;
// BSTRs can be NULL.
if (!pszUrl)
pszUrl = L"";
// NOTE: This URL came from the user, so we need to clean it up.
// If the user entered "yahoo.com" or "Search Get Rich Quick",
// it will be turned into a search URL by ParseURLFromOutsideSourceW().
DWORD cchParsedUrl = ARRAYSIZE(wzParsedUrl);
if (!ParseURLFromOutsideSourceW(pszUrl, wzParsedUrl, &cchParsedUrl, NULL))
{
StrCpyN(wzParsedUrl, pszUrl, ARRAYSIZE(wzParsedUrl));
}
IEParseDisplayName(CP_ACP, wzParsedUrl, &pidl);
if (pidl)
{
NotifyRedirect(_bbd._psv, pidl, NULL);
ILFree(pidl);
}
}
// even if there was no url, still force no refresh.
_fGeneratedPage = TRUE;
// force updating the back and forward buttons
_pbsOuter->UpdateBackForwardState();
hres = S_OK;
break;
case SBCMDID_ONCLOSE:
hres = S_OK;
if (_bbd._pctView)
{
hres = _bbd._pctView->Exec(pguidCmdGroup, nCmdID, 0, NULL, NULL);
}
break;
case SBCMDID_SETSECURELOCKICON:
{
// if this is a SET, then just obey.
LONG lock = pvarargIn->lVal;
TraceMsg(DM_SSL, "SB::Exec() SETSECURELOCKICON lock = %d", lock);
if (lock >= SECURELOCK_FIRSTSUGGEST)
{
//
// if this was ever secure, then the lowest we can be
// suggested to is MIXED. otherwise we just choose the
// lowest level of security suggested.
//
if ((lock == SECURELOCK_SUGGEST_UNSECURE) &&
(_bbd._eSecureLockIcon != SECURELOCK_SET_UNSECURE))
{
lock = SECURELOCK_SET_MIXED;
}
else
{
lock = min(lock - SECURELOCK_FIRSTSUGGEST, _bbd._eSecureLockIcon);
}
}
UpdateSecureLockIcon(lock);
hres = S_OK;
}
break;
default:
hres = OLECMDERR_E_NOTSUPPORTED;
break;
}
}
else if (IsEqualGUID(CGID_ShellDocView, *pguidCmdGroup))
{
switch(nCmdID)
{
case SHDVID_GOBACK:
hres = _psbOuter->BrowseObject(NULL, SBSP_NAVIGATEBACK);
break;
case SHDVID_GOFORWARD:
hres = _psbOuter->BrowseObject(NULL, SBSP_NAVIGATEFORWARD);
break;
// we reflect AMBIENTPROPCHANGE down because this is how iedisp notifies dochost
// that an ambient property has changed. we don't need to reflect this down in
// cwebbrowsersb because only the top-level iwebbrowser2 is allowed to change props
case SHDVID_AMBIENTPROPCHANGE:
case SHDVID_PRINTFRAME:
case SHDVID_MIMECSETMENUOPEN:
case SHDVID_FONTMENUOPEN:
case SHDVID_DOCFAMILYCHARSET:
if (_bbd._pctView) // we must check!
{
hres = _bbd._pctView->Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
}
else
hres = E_FAIL;
break;
case SHDVID_DEACTIVATEMENOW:
if (!_bbd._psvPending)
{
hres = S_OK;
break;
}
// fall through to activate new view
#ifdef FEATURE_PICS
case SHDVID_ACTIVATEMENOW:
#endif
if ( pvarargIn
&& (VT_BOOL == V_VT(pvarargIn))
&& (VARIANT_TRUE == V_BOOL(pvarargIn))) // Synchronous
{
if (_bbd._psvPending)
{
ASSERT(_pbsOuter);
_pbsOuter->ActivatePendingView();
}
}
else // Asynchronous
{
_ActivatePendingViewAsync();
}
hres = S_OK;
break;
case SHDVID_GETPENDINGOBJECT:
ASSERT( pvarargOut);
if (_bbd._psvPending && ((pvarargIn && pvarargIn->vt == VT_BOOL && pvarargIn->boolVal) || !_bbd._psv))
{
VariantClearLazy(pvarargOut);
_bbd._psvPending->QueryInterface(IID_PPV_ARG(IUnknown, &pvarargOut->punkVal));
if (pvarargOut->punkVal) pvarargOut->vt = VT_UNKNOWN;
}
hres = (pvarargOut->punkVal == NULL) ? E_FAIL : S_OK;
break;
case SHDVID_SETPRINTSTATUS:
if (pvarargIn && pvarargIn->vt == VT_BOOL)
{
VARIANTARG var = {0};
if (_bbd._pctView && SUCCEEDED(_bbd._pctView->Exec(&CGID_Explorer, SBCMDID_GETPANE, PANE_PRINTER, NULL, &var))
&& V_UI4(&var) != PANE_NONE)
{
_psbOuter->SendControlMsg(FCW_STATUS, SB_SETICON, V_UI4(&var),
(LPARAM)(pvarargIn->boolVal ? g_hiconPrinter : NULL), NULL);
// we're putting the printer icon and the offline icon in the same
// slot on the status bar, so when we turn off the printer icon
// we have to check to see if we're offline so we can put the offline
// icon back
if (!pvarargIn->boolVal && IsGlobalOffline())
{
#ifdef DEBUG
VARIANTARG var2 = {0};
_bbd._pctView->Exec(&CGID_Explorer, SBCMDID_GETPANE, PANE_OFFLINE, NULL, &var2);
ASSERT(V_UI4(&var2) == V_UI4(&var));
#endif DEBUG
_psbOuter->SendControlMsg(FCW_STATUS, SB_SETICON, V_UI4(&var),
(LPARAM)(g_hiconOffline), NULL);
} // if (!pvarargIn->boolVal && IsGlobalOffline())
}
hres = S_OK;
}
else
hres = E_INVALIDARG;
break;
#ifdef FEATURE_PICS
/* Dochost sends up this command to have us put up the PICS access
* denied dialog. This is done so that all calls to this ratings
* API are modal to the top-level browser window; that in turn
* lets the ratings code coalesce denials for all subframes into
* a single dialog.
*/
case SHDVID_PICSBLOCKINGUI:
{
void * pDetails;
if (pvarargIn && pvarargIn->vt == VT_INT_PTR)
pDetails = pvarargIn->byref;
else
pDetails = NULL;
TraceMsg(DM_PICS, "CBaseBrowser2::Exec calling RatingAccessDeniedDialog2");
/**
* We QueryService for an SID_IRatingNotification which is
* implemented by our host, if we find it, instead of
* displaying the modal ratings dialog, we notify our host through
* the interface and allow it to make the decision
*/
IRatingNotification* pRatingNotify;
hres = QueryService(SID_SRatingNotification, IID_PPV_ARG(IRatingNotification, &pRatingNotify));
if (SUCCEEDED(hres))
{
RATINGBLOCKINGINFO* pRBInfo = NULL;
TraceMsg(DM_PICS, "CBaseBrowser2::Exec calling RatingMarsCrackData");
hres = RatingCustomCrackData(NULL, pDetails, &pRBInfo);
if (SUCCEEDED(hres))
{
hres = pRatingNotify->AccessDeniedNotify(pRBInfo);
RatingCustomDeleteCrackedData(pRBInfo);
}
pRatingNotify->Release();
} // if (SUCCEEDED(hres))
else {
hres = RatingAccessDeniedDialog2(_bbd._hwnd, NULL, pDetails);
}
}
break;
#endif
case SHDVID_ONCOLORSCHANGE:
// PALETTE:
// PALETTE: recompute our palette
// PALETTE:
_ColorsDirty(BPT_UnknownPalette);
break;
case SHDVID_GETOPTIONSHWND:
{
ASSERT(pvarargOut != NULL);
ASSERT(V_VT(pvarargOut) == VT_BYREF);
// return the hwnd for the inet options
// modal prop sheet. we're tracking
// this hwnd because if it's open
// and plugUI shutdown needs to happen,
// that dialog needs to receive a WM_CLOSE
// before we can nuke it
hres = E_FAIL;
// is there a list of window handles?
ENTERCRITICAL;
if (s_hdpaOptionsHwnd != NULL)
{
int cPtr = DPA_GetPtrCount(s_hdpaOptionsHwnd);
// is that list nonempty?
if (cPtr > 0)
{
HWND hwndOptions = (HWND)DPA_GetPtr(s_hdpaOptionsHwnd, 0);
ASSERT(hwndOptions != NULL);
pvarargOut->byref = hwndOptions;
// remove it from the list
// that hwnd is not our responsibility anymore
DPA_DeletePtr(s_hdpaOptionsHwnd, 0);
// successful hwnd retrieval
hres = S_OK;
}
}
LEAVECRITICAL;
}
break;
case SHDVID_DISPLAYSCRIPTERRORS:
{
HRESULT hr;
hr = _bbd._pctView->Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
return hr;
}
break;
case SHDVID_NAVIGATEFROMDOC: // The document called Navigate
_dwDocFlags |= DOCFLAG_NAVIGATEFROMDOC;
return S_OK;
case SHDVID_SETNAVIGATABLECODEPAGE:
_dwDocFlags |= DOCFLAG_SETNAVIGATABLECODEPAGE;
return S_OK;
case SHDVID_CHECKINCACHEIFOFFLINE:
{
if (pvarargIn && (VT_BSTR == V_VT(pvarargIn)) && V_BSTR(pvarargIn)
&& pvarargOut && (VT_BOOL == V_VT(pvarargOut)))
{
LPITEMIDLIST pidl = PidlFromUrl(V_BSTR(pvarargIn));
if (pidl)
{
// (scotrobe): We should be passing fIsPost
// into _CheckInCacheIfOffline.
//
V_BOOL(pvarargOut) = (S_OK == _CheckInCacheIfOffline(pidl, FALSE));
ILFree(pidl);
return S_OK;
}
}
return E_FAIL;
}
break;
case SHDVID_CHECKDONTUPDATETLOG:
{
if (pvarargOut)
{
V_VT(pvarargOut) = VT_BOOL;
V_BOOL(pvarargOut) = (_fDontAddTravelEntry ? VARIANT_TRUE : VARIANT_FALSE);
hres = S_OK;
}
break;
}
case SHDVID_FIREFILEDOWNLOAD:
if (pvarargOut)
{
BOOL fCancel = FALSE;
V_VT(pvarargOut) = VT_BOOL;
FireEvent_FileDownload(_bbd._pautoEDS, &fCancel, pvarargIn ? V_BOOL(pvarargIn):VARIANT_FALSE);
pvarargOut->boolVal = (fCancel ? VARIANT_TRUE : VARIANT_FALSE);
hres = S_OK;
}
break;
default:
hres = OLECMDERR_E_NOTSUPPORTED;
break;
}
}
else if (IsEqualGUID(CGID_ShortCut, *pguidCmdGroup))
{
if (_bbd._pctView) // we must check!
hres = _bbd._pctView->Exec(&CGID_ShortCut, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
else
hres = OLECMDERR_E_NOTSUPPORTED;
}
else if (IsEqualGUID(CGID_DocHostCmdPriv, *pguidCmdGroup))
{
switch(nCmdID)
{
case DOCHOST_DOCCANNAVIGATE:
if (pvarargIn && VT_UNKNOWN == V_VT(pvarargIn) && V_UNKNOWN(pvarargIn))
{
_dwDocFlags |= DOCFLAG_DOCCANNAVIGATE;
}
else
{
_dwDocFlags &= ~DOCFLAG_DOCCANNAVIGATE;
}
hres = S_OK;
break;
case DOCHOST_SETBROWSERINDEX:
if (pvarargIn && VT_I4 == V_VT(pvarargIn))
{
_dwBrowserIndex = V_I4(pvarargIn);
return S_OK;
}
return E_INVALIDARG;
default:
hres = OLECMDERR_E_UNKNOWNGROUP; // Backwards compatability
break;
}
}
else if (IsEqualGUID(CGID_InternetExplorer, *pguidCmdGroup))
{
switch(nCmdID)
{
case IECMDID_SET_INVOKE_DEFAULT_BROWSER_ON_NEW_WINDOW:
if ((NULL != pvarargIn) && (pvarargIn->vt == VT_BOOL))
{
_fInvokeDefBrowserOnNewWindow = pvarargIn->boolVal ? TRUE : FALSE;
hres = S_OK;
}
else
{
hres = E_INVALIDARG;
}
break;
case IECMDID_GET_INVOKE_DEFAULT_BROWSER_ON_NEW_WINDOW:
if ((NULL != pvarargOut) && (pvarargOut->vt == VT_EMPTY))
{
pvarargOut->vt = VT_BOOL;
pvarargOut->boolVal = _fInvokeDefBrowserOnNewWindow ? TRUE : FALSE;
hres = S_OK;
}
else
{
hres = E_INVALIDARG;
}
break;
case IECMDID_BEFORENAVIGATE_GETSHELLBROWSE:
{
// true if pending navigate is NOT a web navigation.
if (pvarargOut && _pidlBeforeNavigateEvent)
{
DWORD dwAttributes = SFGAO_FOLDER;
hres = IEGetAttributesOf(_pidlBeforeNavigateEvent, &dwAttributes);
V_VT(pvarargOut) = VT_BOOL;
V_BOOL(pvarargOut) = SUCCEEDED(hres) && (dwAttributes & SFGAO_FOLDER) ?
VARIANT_TRUE : VARIANT_FALSE;
hres = S_OK;
}
}
break;
case IECMDID_BEFORENAVIGATE_DOEXTERNALBROWSE:
{
if (_pidlBeforeNavigateEvent)
{
hres = _psbOuter->BrowseObject(_pidlBeforeNavigateEvent, SBSP_ABSOLUTE | SBSP_NEWBROWSER);
}
}
break;
case IECMDID_BEFORENAVIGATE_GETIDLIST:
{
if (pvarargOut && _pidlBeforeNavigateEvent)
{
hres = InitVariantFromIDList(pvarargOut, _pidlBeforeNavigateEvent);
}
}
break;
default:
hres = OLECMDERR_E_NOTSUPPORTED;
break;
}
}
else
{
hres = OLECMDERR_E_UNKNOWNGROUP;
}
return hres;
}
LPITEMIDLIST
CBaseBrowser2::PidlFromUrl(BSTR bstrUrl)
{
LPITEMIDLIST pidl = NULL;
ASSERT(bstrUrl);
IEParseDisplayNameWithBCW(CP_ACP, bstrUrl, NULL, &pidl);
// IEParseDisplayNameWithBCW will return a null pidl if
// the URL has any kind of fragment identifier at the
// end - #, ? =, etc.
//
if (!pidl)
{
TCHAR szPath[INTERNET_MAX_URL_LENGTH + 1];
DWORD cchBuf = ARRAYSIZE(szPath);
// If it's a FILE URL, convert it to a path.
//
if (IsFileUrlW(bstrUrl) && SUCCEEDED(PathCreateFromUrl(bstrUrl, 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, bstrUrl, ARRAYSIZE(szPath));
}
pidl = SHSimpleIDListFromPath(szPath);
}
return pidl;
}
BSTR
CBaseBrowser2::GetHTMLWindowUrl(IHTMLWindow2 * pHTMLWindow)
{
BSTR bstrUrl = NULL;
IHTMLPrivateWindow * pPrivateWindow;
HRESULT hr = pHTMLWindow->QueryInterface(IID_PPV_ARG(IHTMLPrivateWindow, &pPrivateWindow));
if (SUCCEEDED(hr))
{
pPrivateWindow->GetAddressBarUrl(&bstrUrl);
pPrivateWindow->Release();
}
return bstrUrl;
}
LPITEMIDLIST
CBaseBrowser2::_GetPidlForDisplay(BSTR bstrUrl, BOOL * pfIsErrorUrl /* = NULL */)
{
BOOL fIsErrorUrl = FALSE;
LPITEMIDLIST pidl = NULL;
if (bstrUrl)
{
fIsErrorUrl = ::IsErrorUrl(bstrUrl);
if (!fIsErrorUrl)
{
pidl = PidlFromUrl(bstrUrl);
}
else
{
// Only strip the anchor fragment if it's not JAVASCRIPT: or VBSCRIPT:, because a # could not an
// anchor but a string to be evaluated by a script engine like #00ff00 for an RGB color.
//
int nScheme = GetUrlSchemeW(bstrUrl);
if (nScheme != URL_SCHEME_JAVASCRIPT && nScheme != URL_SCHEME_VBSCRIPT)
{
// Locate local anchor fragment if possible
//
LPWSTR pszFragment = StrChr(bstrUrl, L'#');
// It is possible to have a fragment identifier
// with no corresponding fragment.
//
if (pszFragment && lstrlen(pszFragment) > 1)
{
BSTR bstrTemp = SysAllocString(pszFragment+1);
if (bstrTemp)
{
pidl = PidlFromUrl(bstrTemp);
SysFreeString(bstrTemp);
}
}
}
}
}
if (pfIsErrorUrl)
*pfIsErrorUrl = fIsErrorUrl;
return pidl;
}
HRESULT CBaseBrowser2::ParseDisplayName(IBindCtx *pbc, LPOLESTR pszDisplayName,
ULONG *pchEaten, IMoniker **ppmkOut)
{
TraceMsg(0, "sdv TR ::ParseDisplayName called");
*ppmkOut = NULL;
return E_NOTIMPL;
}
HRESULT CBaseBrowser2::EnumObjects( DWORD grfFlags, IEnumUnknown **ppenum)
{
TraceMsg(0, "sdv TR ::EnumObjects called");
*ppenum = NULL;
return E_NOTIMPL;
}
HRESULT CBaseBrowser2::LockContainer( BOOL fLock)
{
TraceMsg(0, "sdv TR ::LockContainer called");
return E_NOTIMPL;
}
HRESULT CBaseBrowser2::SetTitle(IShellView* psv, LPCWSTR lpszName)
{
LPWSTR *ppszName = NULL;
BOOL fFireEvent = FALSE;
LPITEMIDLIST pidl;
// We need to forward title changes on to the automation interface.
// But since title changes can technically occur at any time we need
// to distinguish between current and pending title changes and only
// pass on the current title change now. We'll pass on the pending
// title change at NavigateComplete time. (This also lets us identify
// when we navigate to non-SetTitle object (such as the shell) and
// simulate a TitleChange event.)
//
// Since the DocObjectHost needs to retrieve the title later, we
// hold onto the current view's title change so they don't have to.
//
// Figure out which object is changing.
//
if (IsSameObject(_bbd._psv, psv))
{
ppszName = &_bbd._pszTitleCur;
pidl = _bbd._pidlCur;
fFireEvent = TRUE;
}
else if (EVAL(IsSameObject(_bbd._psvPending, psv) || !_bbd._psvPending)) // no pending probably means we're in _MayPlayTransition
{
ppszName = &_bbd._pszTitlePending;
pidl = _bbd._pidlPending;
// If we have no current guy, might as well set the title early
fFireEvent = !_bbd._psv;
}
else
{
ppszName = NULL;
pidl = NULL; // init pidl to suppress bogus C4701 warning
}
if (ppszName)
{
UINT cchLen = lstrlenW(lpszName) + 1; // +1 for NULL
UINT cbAlloc;
// For some reason we cap the length of this string. We can't cap
// less than MAX_PATH because we need to handle filesys names.
//
if (cchLen > MAX_PATH)
cchLen = MAX_PATH;
// We want to allocate at least a medium size string because
// many web pages script the title one character at a time.
//
#define MIN_TITLE_ALLOC 64
if (cchLen < MIN_TITLE_ALLOC)
cbAlloc = MIN_TITLE_ALLOC * SIZEOF(*lpszName);
else
cbAlloc = cchLen * SIZEOF(*lpszName);
#undef MIN_TITLE_ALLOC
// Do we need to allocate?
if (!(*ppszName) || LocalSize((HLOCAL)(*ppszName)) < cbAlloc)
{
// Free up Old Title
if(*ppszName)
LocalFree((void *)(*ppszName));
*ppszName = (LPWSTR)LocalAlloc(LPTR, cbAlloc);
}
if (*ppszName)
{
StrCpyNW(*ppszName, lpszName, cchLen);
if (fFireEvent)
{
DWORD dwOptions;
FireEvent_DoInvokeStringW(_bbd._pautoEDS, DISPID_TITLECHANGE, *ppszName);
// If this is a desktop component, try to update the friendly name if necessary.
if (!_fCheckedDesktopComponentName)
{
_fCheckedDesktopComponentName = TRUE;
if (SUCCEEDED(GetTopFrameOptions(_pspOuter, &dwOptions)) && (dwOptions & FRAMEOPTIONS_DESKTOP))
{
WCHAR wszPath[MAX_URL_STRING];
if (SUCCEEDED(::IEGetDisplayName(pidl, wszPath, SHGDN_FORPARSING)))
{
UpdateDesktopComponentName(wszPath, lpszName);
}
}
}
}
}
}
return NOERROR;
}
HRESULT CBaseBrowser2::GetTitle(IShellView* psv, LPWSTR pszName, DWORD cchName)
{
LPWSTR psz;
if (!psv || IsSameObject(_bbd._psv, psv))
{
psz = _bbd._pszTitleCur;
}
else if (EVAL(IsSameObject(_bbd._psvPending, psv) || !_bbd._psvPending))
{
psz = _bbd._pszTitlePending;
}
else
{
psz = NULL;
}
if (psz)
{
StrCpyNW(pszName, psz, cchName);
return(S_OK);
}
else
{
*pszName = 0;
return(E_FAIL);
}
}
HRESULT CBaseBrowser2::GetParentSite(struct IOleInPlaceSite** ppipsite)
{
*ppipsite = NULL;
return E_NOTIMPL;
}
HRESULT CBaseBrowser2::GetOleObject(struct IOleObject** ppobjv)
{
*ppobjv = NULL;
return E_NOTIMPL;
}
HRESULT CBaseBrowser2::NotifyRedirect(IShellView * psv, LPCITEMIDLIST pidlNew, BOOL *pfDidBrowse)
{
HRESULT hres = E_FAIL;
if (pfDidBrowse)
*pfDidBrowse = FALSE;
if (IsSameObject(psv, _bbd._psv) ||
IsSameObject(psv, _bbd._psvPending))
{
hres = _pbsOuter->_TryShell2Rename(psv, pidlNew);
if (FAILED(hres))
{
// if we weren't able to just swap it, we've got to browse to it
// but pass redirect so that we don't add a navigation stack item
//
// NOTE: the above comment is a bit old since we don't pass
// redirect here. If we ever start passing redirect here,
// we'll confuse ISVs relying on the NavigateComplete event
// exactly mirroring when navigations enter the navigation stack.
//
hres = _psbOuter->BrowseObject(pidlNew, SBSP_WRITENOHISTORY | SBSP_SAMEBROWSER);
if(pfDidBrowse)
*pfDidBrowse = TRUE;
}
}
return hres;
}
HRESULT CBaseBrowser2::SetFlags(DWORD dwFlags, DWORD dwFlagMask)
{
if (dwFlagMask & BSF_REGISTERASDROPTARGET)
{
_fNoDragDrop = (!(dwFlags & BSF_REGISTERASDROPTARGET)) ? TRUE : FALSE;
if (!_fNoDragDrop)
_RegisterAsDropTarget();
else
_UnregisterAsDropTarget();
}
if (dwFlagMask & BSF_DONTSHOWNAVCANCELPAGE)
{
_fDontShowNavCancelPage = !!(dwFlags & BSF_DONTSHOWNAVCANCELPAGE);
}
if (dwFlagMask & BSF_HTMLNAVCANCELED)
{
_fHtmlNavCanceled = !!(dwFlags & BSF_HTMLNAVCANCELED);
}
return S_OK;
}
HRESULT CBaseBrowser2::GetFlags(DWORD *pdwFlags)
{
DWORD dwFlags = 0;
if (!_fNoDragDrop)
dwFlags |= BSF_REGISTERASDROPTARGET;
if (_fTopBrowser)
dwFlags |= BSF_TOPBROWSER;
if (_grfHLNFPending & HLNF_CREATENOHISTORY)
dwFlags |= BSF_NAVNOHISTORY;
if (_fHtmlNavCanceled)
dwFlags |= BSF_HTMLNAVCANCELED;
if (_dwDocFlags & DOCFLAG_SETNAVIGATABLECODEPAGE)
dwFlags |= BSF_SETNAVIGATABLECODEPAGE;
if (_dwDocFlags & DOCFLAG_NAVIGATEFROMDOC)
dwFlags |= BSF_DELEGATEDNAVIGATION;
*pdwFlags = dwFlags;
return S_OK;
}
HRESULT CBaseBrowser2::UpdateWindowList(void)
{
// code used to assert, but in WebBrowserOC cases we can get here.
return E_UNEXPECTED;
}
STDMETHODIMP CBaseBrowser2::IsControlWindowShown(UINT id, BOOL *pfShown)
{
if (pfShown)
*pfShown = FALSE;
return E_NOTIMPL;
}
STDMETHODIMP CBaseBrowser2::ShowControlWindow(UINT id, BOOL fShow)
{
return E_NOTIMPL;
}
HRESULT CBaseBrowser2::IEGetDisplayName(LPCITEMIDLIST pidl, LPWSTR pwszName, UINT uFlags)
{
return ::IEGetDisplayName(pidl, pwszName, uFlags);
}
HRESULT CBaseBrowser2::IEParseDisplayName(UINT uiCP, LPCWSTR pwszPath, LPITEMIDLIST * ppidlOut)
{
HRESULT hr;
IBindCtx * pbc = NULL;
WCHAR wzParsedUrl[MAX_URL_STRING];
//
// if we can find a search context living in a host somewhere,
// then we need to pass that into ParseUrlFromOutsideSource
// because it'll use it to customize the behavior of
// the search hooks if a search ends up happening
//
ISearchContext * pSC = NULL;
QueryService(SID_STopWindow, IID_PPV_ARG(ISearchContext, &pSC));
//
// NOTE: This URL came from the user, so we need to clean it up.
// If the user entered "yahoo.com" or "Search Get Rich Quick",
// it will be turned into a search URL by ParseURLFromOutsideSourceW().
//
DWORD cchParsedUrl = ARRAYSIZE(wzParsedUrl);
if (!ParseURLFromOutsideSourceWithContextW(pwszPath, wzParsedUrl, &cchParsedUrl, NULL, pSC))
{
StrCpyN(wzParsedUrl, pwszPath, ARRAYSIZE(wzParsedUrl));
}
if (pSC != NULL)
{
pSC->Release();
}
// This is currently used for FTP, so we only do it for FTP for perf reasons.
if (URL_SCHEME_FTP == GetUrlSchemeW(wzParsedUrl))
pbc = CreateBindCtxForUI(SAFECAST(this, IOleContainer *)); // We really want to cast to (IUnknown *) but that's ambiguous.
hr = IEParseDisplayNameWithBCW(uiCP, wzParsedUrl, pbc, ppidlOut);
ATOMICRELEASE(pbc);
return hr;
}
HRESULT _DisplayParseError(HWND hwnd, HRESULT hres, LPCWSTR pwszPath)
{
if (FAILED(hres)
&& hres != E_OUTOFMEMORY
&& hres != HRESULT_FROM_WIN32(ERROR_CANCELLED))
{
TCHAR szPath[MAX_URL_STRING];
SHUnicodeToTChar(pwszPath, szPath, ARRAYSIZE(szPath));
MLShellMessageBox(hwnd,
MAKEINTRESOURCE(IDS_ERROR_GOTO),
MAKEINTRESOURCE(IDS_TITLE),
MB_OK | MB_SETFOREGROUND | MB_ICONSTOP,
szPath);
hres = HRESULT_FROM_WIN32(ERROR_CANCELLED);
}
return hres;
}
HRESULT CBaseBrowser2::DisplayParseError(HRESULT hres, LPCWSTR pwszPath)
{
return _DisplayParseError(_bbd._hwnd, hres, pwszPath);
}
HRESULT CBaseBrowser2::_CheckZoneCrossing(LPCITEMIDLIST pidl)
{
if (!(_dwDocFlags & DOCFLAG_NAVIGATEFROMDOC))
{
return _pbsOuter->v_CheckZoneCrossing(pidl);
}
return S_OK;
}
// if in global offline mode and this item requires net access and it is
// not in the cache put up UI to go online.
//
// returns:
// S_OK URL is ready to be accessed
// E_ABORT user canceled the UI
HRESULT CBaseBrowser2::_CheckInCacheIfOffline(LPCITEMIDLIST pidl, BOOL fIsAPost)
{
HRESULT hr = S_OK; // assume it is
VARIANT_BOOL fFrameIsSilent;
VARIANT_BOOL fFrameHasAmbientOfflineMode;
EVAL(SUCCEEDED(_bbd._pautoWB2->get_Silent(&fFrameHasAmbientOfflineMode))); // should always work
EVAL(SUCCEEDED(_bbd._pautoWB2->get_Offline(&fFrameIsSilent)));
if ((fFrameIsSilent == VARIANT_FALSE) &&
(fFrameHasAmbientOfflineMode == VARIANT_FALSE)&&
pidl && (pidl != PIDL_NOTHING) && (pidl != PIDL_LOCALHISTORY) &&
IsBrowserFrameOptionsPidlSet(pidl, BFO_USE_IE_OFFLINE_SUPPORT) &&
IsGlobalOffline())
{
TCHAR szURL[MAX_URL_STRING];
EVAL(SUCCEEDED(::IEGetNameAndFlags(pidl, SHGDN_FORPARSING, szURL, SIZECHARS(szURL), NULL)));
if (UrlHitsNet(szURL) && ((!UrlIsMappedOrInCache(szURL)) || fIsAPost))
{
// UI to allow user to go on-line
HWND hParentWnd = NULL; // init to suppress bogus C4701 warning
hr = E_FAIL;
if(!_fTopBrowser)
{
IOleWindow *pOleWindow;
hr = _pspOuter->QueryService(SID_STopLevelBrowser, IID_PPV_ARG(IOleWindow, &pOleWindow));
if(SUCCEEDED(hr))
{
ASSERT(pOleWindow);
hr = pOleWindow->GetWindow(&hParentWnd);
pOleWindow->Release();
}
}
if (S_OK != hr)
{
hr = S_OK;
hParentWnd = _bbd._hwnd;
}
_psbOuter->EnableModelessSB(FALSE);
if (InternetGoOnline(szURL, hParentWnd, FALSE))
{
// Tell all browser windows to update their title and status pane
SendShellIEBroadcastMessage(WM_WININICHANGE,0,0, 1000);
}
else
hr = E_ABORT; // user abort case...
_psbOuter->EnableModelessSB(TRUE);
}
}
return hr;
}
// This function exists to prevent us from using the stack space too long.
// We will use it here and then free it when we return.
HRESULT CBaseBrowser2::_ReplaceWithGoHome(LPCITEMIDLIST * ppidl, LPITEMIDLIST * ppidlFree)
{
TCHAR szHome[MAX_URL_STRING];
HRESULT hres = _GetStdLocation(szHome, ARRAYSIZE(szHome), DVIDM_GOHOME);
if (SUCCEEDED(hres))
{
hres = IECreateFromPath(szHome, ppidlFree);
if (SUCCEEDED(hres))
{
*ppidl = *ppidlFree;
}
}
return hres;
}
// this does all the preliminary checking of whether we can navigate to pidl or not.
// then if all is ok, we do the navigate with CreateNewShellViewPidl
HRESULT CBaseBrowser2::_NavigateToPidl(LPCITEMIDLIST pidl, DWORD grfHLNF, DWORD fSBSP)
{
HRESULT hres;
BOOL fCanceledDueToOffline = FALSE;
BOOL fIsAPost = FALSE;
LPITEMIDLIST pidlFree = NULL;
//
// If we are processing a modal dialog, don't process it.
//
// NOTES: Checking _cRefCannotNavigate is supposed to be enough, but
// since we are dealing with random ActiveX objects, we'd better be
// robust. That's why we check IsWindowEnabled as well.
//
if ((S_OK ==_DisableModeless()) || !IsWindowEnabled(_bbd._hwnd))
{
TraceMsg(DM_ENABLEMODELESS, "CSB::_NavigateToPidl returning ERROR_BUSY");
hres = HRESULT_FROM_WIN32(ERROR_BUSY);
goto Done;
}
TraceMsg(DM_NAV, "ief NAV::%s %x %x",TEXT("_NavigateToPidl called"), pidl, grfHLNF);
// used to be we would pull a NULL out of the
// the TravelLog, but i dont think that happens anymore
ASSERT(pidl); // Get ZEKEL
// Sometimes we are navigated to the INTERNET shell folder
// if this is the case, we really want to goto the Start Page.
// This case only happens if you select "Internet Explorer" from the
// Folder Explorer Band.
if (IsBrowserFrameOptionsPidlSet(pidl, BFO_SUBSTITUE_INTERNET_START_PAGE))
{
hres = _ReplaceWithGoHome(&pidl, &pidlFree);
if (FAILED(hres))
goto Done;
}
// We should only fire the BeforeNavigate event
// if the document is not going to fire it.
// We know that the document will fire it if
// the document didn't call Navigate, the document
// knows how to navigate and the document isn't hyperlinking.
//
if (!(_dwDocFlags & DOCFLAG_NAVIGATEFROMDOC))
{
hres = _FireBeforeNavigateEvent(pidl, &fIsAPost);
if (hres == E_ABORT)
goto Done; // event handler told us to cancel
}
// if we can't go here (?), cancel the navigation
hres = _CheckZoneCrossing(pidl);
if (hres != S_OK)
goto Done;
TraceMsg(DM_NAV, "ief NAV::%s %x %x",TEXT("_CreateNewShellViewPidl called"), pidl, grfHLNF);
//
// Now that we are actually navigating...
//
// tell the frame to cancel the current navigation
// and tell it about history navigate options as it will not be getting it
// from subsequent call to Navigate
//
if (_bbd._phlf)
{
_bbd._phlf->Navigate(grfHLNF&(SHHLNF_WRITENOHISTORY|SHHLNF_NOAUTOSELECT), NULL, NULL, NULL);
}
hres = _CheckInCacheIfOffline(pidl, fIsAPost);
if (hres != S_OK)
{
fCanceledDueToOffline = TRUE;
goto Done;
}
//
// if we goto the current page, we still do a full navigate
// but we dont want to create a new entry.
//
// **EXCEPTIONS**
// if this was a generated page, ie Trident did doc.writes(),
// we need to always create a travel entry, because trident
// can rename the pidl, but it wont actually be that page.
//
// if this was a post then we need to create a travelentry.
// however if it was a travel back/fore, it will already have
// set the the bit, so we still wont create a new entry.
//
//
// NOTE: this is similar to a refresh, in that it reparses
// the entire page, but creates no travel entry.
//
if ( !_fDontAddTravelEntry // If the flag is already set, short circuit the rest
&& !fIsAPost // ...and not a Post
&& !_fGeneratedPage // ...and not a Generated Page
&& !(grfHLNF & HLNF_CREATENOHISTORY) // ...and the CREATENOHISTORY flag is NOT set
&& pidl // ...and we have a pidl to navigate to
&& _bbd._pidlCur // ...as well as a current pidl
&& ILIsEqual(pidl, _bbd._pidlCur) // ...and the pidls are equal
)
_fDontAddTravelEntry = TRUE; // Then set the DontAddTravelEntry flag.
TraceMsg(TF_TRAVELLOG, "CBB:_NavToPidl() _fDontAddTravelEntry = %d", _fDontAddTravelEntry);
_fGeneratedPage = FALSE;
hres = _CreateNewShellViewPidl(pidl, grfHLNF, fSBSP);
_dwDocFlags &= ~(DOCFLAG_NAVIGATEFROMDOC | DOCFLAG_SETNAVIGATABLECODEPAGE);
if (SUCCEEDED(hres))
{
ATOMICRELEASE(_pHTMLDocument);
}
Done:
if (FAILED(hres))
{
TraceMsg(DM_WARNING, "CSB::_NavigateToPidl _CreateNewShellViewPidl failed %x", hres);
// On failure we won't hit _ActivatePendingView
OnReadyStateChange(NULL, READYSTATE_COMPLETE);
// if this was navigation via ITravelLog,
// this will revert us to the original position
if (_fDontAddTravelEntry)
{
ITravelLog *ptl;
if(SUCCEEDED(GetTravelLog(&ptl)))
{
ptl->Revert();
ptl->Release();
}
_fDontAddTravelEntry = FALSE;
ASSERT(!_fIsLocalAnchor);
ATOMICRELEASE(_poleHistory);
ATOMICRELEASE(_pbcHistory);
ATOMICRELEASE(_pstmHistory);
}
if (_pbsOuter)
_pbsOuter->UpdateBackForwardState();
// we failed and have nothing to show for it...
if (!_bbd._pidlCur && !_fNavigatedToBlank)
{
TCHAR szResURL[MAX_URL_STRING];
if (fCanceledDueToOffline)
{
hres = MLBuildResURLWrap(TEXT("shdoclc.dll"),
HINST_THISDLL,
ML_CROSSCODEPAGE,
TEXT("offcancl.htm"),
szResURL,
ARRAYSIZE(szResURL),
TEXT("shdocvw.dll"));
if (SUCCEEDED(hres))
{
_ShowBlankPage(szResURL, pidl);
}
}
else
{
// NT #274562: We only want to navigate to the
// about:NavigationCancelled page if it wasn't
// a navigation to file path. (UNC or Drive).
// The main reason for this is that if the user
// enters "\\unc\share" into Start->Run and
// the window can't successfully navigate to the
// share because permissions don't allow it, we
// want to close the window after the user hits
// [Cancel] in the [Retry][Cancel] dialog. This
// is to prevent the shell from appearing to have
// shell integrated bugs and to be compatible with
// the old shell.
if ( IsBrowserFrameOptionsPidlSet(_bbd._pidlCur, BFO_SHOW_NAVIGATION_CANCELLED ) )
{
hres = MLBuildResURLWrap(TEXT("shdoclc.dll"),
HINST_THISDLL,
ML_CROSSCODEPAGE,
TEXT("navcancl.htm"),
szResURL,
ARRAYSIZE(szResURL),
TEXT("shdocvw.dll"));
if (SUCCEEDED(hres))
{
_ShowBlankPage(szResURL, pidl);
}
}
}
}
}
ILFree(pidlFree);
return hres;
}
HRESULT CBaseBrowser2::OnReadyStateChange(IShellView* psvSource, DWORD dwReadyState)
{
BOOL fChange = FALSE;
if (psvSource)
{
if (IsSameObject(psvSource, _bbd._psv))
{
TraceMsg(TF_SHDNAVIGATE, "basesb(%x)::OnReadyStateChange(Current, %d)", this, dwReadyState);
fChange = (_dwReadyStateCur != dwReadyState);
_dwReadyStateCur = dwReadyState;
if ((READYSTATE_COMPLETE == dwReadyState) && !_fReleasingShellView)
_Exec_psbMixedZone();
}
else if (IsSameObject(psvSource, _bbd._psvPending))
{
TraceMsg(TF_SHDNAVIGATE, "basesb(%x)::OnReadyStateChange(Pending, %d)", this, dwReadyState);
fChange = (_dwReadyStatePending != dwReadyState);
_dwReadyStatePending = dwReadyState;
}
else if (!_bbd._psvPending)
{
// Assume psvSource != _bbd._psv && NULL==_bbd._psvPending
// means that _SwitchActivationNow is in the middle
// of _MayPlayTransition's message loop and the
// _bbd._psvPending dude is updating us.
//
// NOTE: We don't fire the event because get_ReadyState
// can't figure this out. We know we will eventually
// fire the event because CBaseBrowser2 will go to _COMPLETE
// after _SwitchActivationNow.
//
TraceMsg(TF_SHDNAVIGATE, "basesb(%x)::OnReadyStateChange(ASSUMED Pending, %d)", this, dwReadyState);
_dwReadyStatePending = dwReadyState;
}
}
else
{
// We use this function when our own simulated
// ReadyState changes
//
TraceMsg(TF_SHDNAVIGATE, "basesb(%x)::OnReadyStateChange(Self, %d)", this, dwReadyState);
fChange = (_dwReadyState != dwReadyState);
_dwReadyState = dwReadyState;
}
// No sense in firing events if nothing actually changed...
//
if (fChange && _bbd._pautoEDS)
{
DWORD dw;
IUnknown_CPContainerOnChanged(_pauto, DISPID_READYSTATE);
// if we at Complete, fire the event
get_ReadyState(&dw);
if (READYSTATE_COMPLETE == dw)
{
if ( !(_dwDocFlags & DOCFLAG_DOCCANNAVIGATE)
|| !_bbd._fIsViewMSHTML)
{
FireEvent_DocumentComplete(_bbd._pautoEDS, _bbd._pautoWB2, _bbd._pidlCur);
}
// If we hit this, we have not picked up the history object we created.
//
AssertMsg(_fDontAddTravelEntry || !_poleHistory, TEXT("CBB::OnDocComplete: nobody picked up _poleHistory"));
if (g_dwProfileCAP & 0x00080000)
{
StopCAP();
}
ATOMICRELEASE(_pphHistory);
}
}
return S_OK;
}
HRESULT CBaseBrowser2::get_ReadyState(DWORD * pdwReadyState)
{
DWORD dw = _dwReadyState;
if (_bbd._psvPending && _dwReadyStatePending < dw)
{
dw = _dwReadyStatePending;
}
if (_bbd._psv && _dwReadyStateCur < dw)
{
dw = _dwReadyStateCur;
}
*pdwReadyState = dw;
return S_OK;
}
HRESULT CBaseBrowser2::_updateNavigationUI()
{
if (_fNavigate || _fDescendentNavigate)
{
SetCursor(LoadCursor(NULL, IDC_APPSTARTING));
if (!_fDownloadSet)
{
FireEvent_DoInvokeDispid(_bbd._pautoEDS, DISPID_DOWNLOADBEGIN);
_fDownloadSet = TRUE;
}
}
else
{
if (_fDownloadSet)
{
FireEvent_DoInvokeDispid(_bbd._pautoEDS, DISPID_DOWNLOADCOMPLETE);
_fDownloadSet = FALSE;
}
SetCursor(LoadCursor(NULL, IDC_ARROW));
}
return S_OK;
}
HRESULT CBaseBrowser2::SetNavigateState(BNSTATE bnstate)
{
switch (bnstate)
{
case BNS_BEGIN_NAVIGATE:
case BNS_NAVIGATE:
_fNavigate = TRUE;
_updateNavigationUI();
break;
case BNS_NORMAL:
_fNavigate = FALSE;
_updateNavigationUI();
break;
}
return S_OK;
}
HRESULT CBaseBrowser2::GetNavigateState(BNSTATE *pbnstate)
{
// Return Navigate if we are processing a navigation or if
// we are processing a modal dialog.
//
// NOTES: Checking _cRefCannotNavigate is supposed to be enough, but
// since we are dealing with random ActiveX objects, we'd better be
// robust. That's why we check IsWindowEnabled as well.
//
*pbnstate = (_fNavigate || (S_OK ==_DisableModeless()) || _fDescendentNavigate ||
!IsWindowEnabled(_bbd._hwnd)) ? BNS_NAVIGATE : BNS_NORMAL;
return S_OK;
}
HRESULT CBaseBrowser2::UpdateBackForwardState(void)
{
if (_fTopBrowser)
{
_UpdateBackForwardState();
}
else
{
// sigh, BrowserBand now makes this fire
//ASSERT(_fTopBrowser);
IBrowserService *pbs = NULL;
TraceMsg(TF_SHDNAVIGATE, "cbb.ohlfn: !_fTopBrowser (BrowserBand?)");
if (SUCCEEDED(_pspOuter->QueryService(SID_STopFrameBrowser, IID_PPV_ARG(IBrowserService, &pbs))))
{
BOOL fTopFrameBrowser = IsSameObject(pbs, SAFECAST(this, IShellBrowser *));
if (!fTopFrameBrowser)
pbs->UpdateBackForwardState();
else
_UpdateBackForwardState();
pbs->Release();
}
}
return S_OK;
}
HRESULT CBaseBrowser2::QueryService(REFGUID guidService, REFIID riid, void **ppv)
{
HRESULT hr;
*ppv = NULL;
//
// NOTES: Notice that CBaseBrowser2 directly expose the automation
// service object via QueryService. CWebBrowserSB will appropriately
// dispatch those. See comments on CWebBrowserSB::QueryService for
// detail. (SatoNa)
//
if (IsEqualGUID(guidService, SID_SWebBrowserApp) ||
IsEqualGUID(guidService, SID_SContainerDispatch) ||
IsEqualGUID(guidService, IID_IExpDispSupport))
{
hr = _bbd._pautoSS->QueryInterface(riid, ppv);
}
else if (IsEqualGUID(guidService, SID_SHlinkFrame) ||
IsEqualGUID(guidService, IID_ITargetFrame2) ||
IsEqualGUID(guidService, IID_ITargetFrame))
{
hr = _ptfrm->QueryInterface(riid, ppv);
}
else if (IsEqualGUID(guidService, SID_STopLevelBrowser) ||
IsEqualGUID(guidService, SID_STopFrameBrowser) ||
IsEqualGUID(guidService, SID_STopWindow) ||
IsEqualGUID(guidService, SID_SProfferService))
{
if (IsEqualGUID(riid, IID_IUrlHistoryStg))
{
ASSERT(_fTopBrowser);
if (!_pIUrlHistoryStg)
{
// create this object the first time it's asked for
CoCreateInstance(CLSID_CUrlHistory, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARG(IUrlHistoryStg, &_pIUrlHistoryStg));
}
if (_pIUrlHistoryStg)
hr = _pIUrlHistoryStg->QueryInterface(riid, ppv);
else
hr = E_NOINTERFACE;
}
else if (IsEqualGUID(riid, IID_IToolbarExt))
{
// This code should all migrate to a helper object after IE5B2. So this
// should be temporary (stevepro).
if (!_pToolbarExt)
{
IUnknown* punk;
if (SUCCEEDED(CBrowserExtension_CreateInstance(NULL, &punk, NULL)))
{
IUnknown_SetSite(punk, _psbOuter);
punk->QueryInterface(IID_PPV_ARG(IToolbarExt, &_pToolbarExt));
punk->Release();
}
}
if (_pToolbarExt)
hr = _pToolbarExt->QueryInterface(riid, ppv);
else
hr = E_NOINTERFACE;
}
else
hr = QueryInterface(riid, ppv);
}
else if (IsEqualGUID(guidService, SID_SUrlHistory))
{
if (!_pIUrlHistoryStg)
{
// Asking for it creates a copy in _pIUrlHistoryStg
IUrlHistoryStg *puhs;
if (SUCCEEDED(_pspOuter->QueryService(SID_STopLevelBrowser, IID_PPV_ARG(IUrlHistoryStg, &puhs))))
{
if (!_pIUrlHistoryStg)
_pIUrlHistoryStg = puhs;
else
puhs->Release();
}
}
if (_pIUrlHistoryStg)
hr = _pIUrlHistoryStg->QueryInterface(riid, ppv);
else
hr = E_NOINTERFACE;
}
else if (IsEqualGUID(guidService, SID_SShellBrowser) ||
IsEqualGUID(guidService, SID_SVersionHost))
{
if (IsEqualIID(riid, IID_IHlinkFrame))
{
// HACK: MSHTML uses IID_IShellBrowser instead of SID_SHlinkFrame
hr = _pspOuter->QueryService(SID_SHlinkFrame, riid, ppv);
}
else if (IsEqualIID(riid, IID_IBindCtx) && _bbd._phlf)
{
// HACK ALERT: Notice that we are using QueryService to the
// other direction here. We must make it absolutely sure
// that we'll never infinitly QueryService each other.
hr = IUnknown_QueryService(_bbd._phlf, IID_IHlinkFrame, riid, ppv);
}
else
{
hr = QueryInterface(riid, ppv);
}
}
else if (IsEqualGUID(guidService, SID_SOmWindow))
{
// HACK ALERT: Notice that we are using QueryService to the
// other direction here. We must make it absolutely sure
// that we'll never infinitly QueryService each other.
hr = IUnknown_QueryService(_ptfrm, SID_SOmWindow, riid, ppv);
}
else if (IsEqualGUID(guidService, IID_IElementNamespaceTable) && _bbd._psv)
{
hr = IUnknown_QueryService(_bbd._psv, IID_IElementNamespaceTable, riid, ppv);
}
else if (IsEqualGUID(guidService, SID_STravelLogCursor))
{
// exposed travel log object
if (!_pITravelLogStg)
{
// create this object the first time it's asked for
ITravelLog * ptl;
GetTravelLog(&ptl);
ITravelLogEx *ptlx;
if (ptl && SUCCEEDED(ptl->QueryInterface(IID_PPV_ARG(ITravelLogEx, &ptlx))))
{
hr = CreatePublicTravelLog(SAFECAST(this, IBrowserService *), ptlx, &_pITravelLogStg);
ptlx->Release();
}
SAFERELEASE(ptl);
}
if (_pITravelLogStg)
{
hr = _pITravelLogStg->QueryInterface(riid, ppv);
}
}
else if (IsEqualGUID(riid,IID_IEnumPrivacyRecords))
{
IHTMLWindow2 * pIW = NULL;
IServiceProvider * pISP = NULL;
hr = QueryService(IID_IHTMLWindow2, IID_PPV_ARG(IHTMLWindow2, &pIW));
if (SUCCEEDED(hr))
{
hr = pIW->QueryInterface(IID_PPV_ARG(IServiceProvider, &pISP));
if (SUCCEEDED(hr))
{
hr = pISP->QueryService(IID_IEnumPrivacyRecords, IID_IEnumPrivacyRecords, ppv);
pISP->Release();
}
pIW->Release();
}
}
else
{
hr = IProfferServiceImpl::QueryService(guidService, riid, ppv);
}
ASSERT(SUCCEEDED(hr) ? *ppv != NULL : *ppv == NULL); // COM rules
return hr;
}
void CBaseBrowser2::OnDataChange(FORMATETC *, STGMEDIUM *)
{
}
void CBaseBrowser2::OnViewChange(DWORD dwAspect, LONG lindex)
{
_ViewChange(dwAspect, lindex);
}
void CBaseBrowser2::OnRename(IMoniker *)
{
}
void CBaseBrowser2::OnSave()
{
}
void CBaseBrowser2::OnClose()
{
_ViewChange(DVASPECT_CONTENT, -1);
}
// *** IDropTarget ***
// These methods are defined in shdocvw.cpp
extern DWORD CommonDragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt);
// Use the ShellView IDropTarget functions whenever they are implemented
// IDropTarget::DragEnter
HRESULT CBaseBrowser2::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
{
if (_pdtView)
return _pdtView->DragEnter(pdtobj, grfKeyState, ptl, pdwEffect);
else
{
if (_fNoDragDrop)
{
_dwDropEffect = DROPEFFECT_NONE;
}
else
{
_dwDropEffect = CommonDragEnter(pdtobj, grfKeyState, ptl);
}
*pdwEffect &= _dwDropEffect;
}
return S_OK;
}
// IDropTarget::DragOver
HRESULT CBaseBrowser2::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect)
{
if (S_OK == _DisableModeless())
{
*pdwEffect = 0;
return S_OK;
}
if (_pdtView)
return _pdtView->DragOver(grfKeyState, ptl, pdwEffect);
*pdwEffect &= _dwDropEffect;
return S_OK;
}
// IDropTarget::DragLeave
HRESULT CBaseBrowser2::DragLeave(void)
{
if (_pdtView)
return _pdtView->DragLeave();
return S_OK;
}
// IDropTarget::DragDrop
HRESULT CBaseBrowser2::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
if (S_OK == _DisableModeless())
{
*pdwEffect = 0;
return S_OK;
}
BOOL fNavigateDone = FALSE;
HRESULT hr = E_FAIL;
// If this is a shortcut - we want it to go via _NavIEShortcut
// First check if it indeed a shortcut
STGMEDIUM medium;
if (_ptfrm && (DataObj_GetDataOfType(pdtobj, CF_HDROP, &medium)))
{
WCHAR wszPath[MAX_PATH];
if (DragQueryFileW((HDROP)medium.hGlobal, 0, wszPath, ARRAYSIZE(wszPath)))
{
LPWSTR pwszExtension = PathFindExtensionW(wszPath);
// Check the extension to see if it is a .URL file
if (0 == StrCmpIW(pwszExtension, L".url"))
{
// It is an internet shortcut
VARIANT varShortCutPath = {0};
VARIANT varFlag = {0};
varFlag.vt = VT_BOOL;
varFlag.boolVal = VARIANT_TRUE;
LBSTR::CString strPath( wszPath );
varShortCutPath.vt = VT_BSTR;
varShortCutPath.bstrVal = strPath;
hr = IUnknown_Exec(_ptfrm, &CGID_Explorer, SBCMDID_IESHORTCUT, 0, &varShortCutPath, &varFlag);
fNavigateDone = SUCCEEDED(hr);
if(fNavigateDone)
{
DragLeave();
}
}
}
// must call ReleaseStgMediumHGLOBAL since DataObj_GetDataOfType added an extra GlobalLock
ReleaseStgMediumHGLOBAL(NULL, &medium);
}
if (FALSE == fNavigateDone)
{
if (_pdtView)
{
hr = _pdtView->Drop(pdtobj, grfKeyState, pt, pdwEffect);
}
else
{
LPITEMIDLIST pidlTarget;
hr = SHPidlFromDataObject(pdtobj, &pidlTarget, NULL, 0);
if (SUCCEEDED(hr))
{
ASSERT(pidlTarget);
hr = _psbOuter->BrowseObject(pidlTarget, SBSP_SAMEBROWSER | SBSP_ABSOLUTE);
ILFree(pidlTarget);
}
}
}
return hr;
}
HRESULT CBaseBrowser2::_FireBeforeNavigateEvent(LPCITEMIDLIST pidl, BOOL* pfIsPost)
{
HRESULT hres = S_OK;
IBindStatusCallback * pBindStatusCallback;
LPTSTR pszHeaders = NULL;
LPBYTE pPostData = NULL;
DWORD cbPostData = 0;
BOOL fCancelled=FALSE;
STGMEDIUM stgPostData;
BOOL fHavePostStg = FALSE;
*pfIsPost = FALSE;
// If this is the first BeforeNavigateEvent of a ViewLinkedWebOC we do not need to fire.
// It is fired by the hosting Trident. Note that _fIsViewLinkedWebOC is only set by the
// host, so we need not worry about BeforeNavigateEvent not being fired for other in other
// instances of the WebOC.
if (_fIsViewLinkedWebOC && (!_fHadFirstBeforeNavigate))
{
_fHadFirstBeforeNavigate = TRUE;
return hres;
}
// get the bind status callback for this browser and ask it for
// headers and post data
if (SUCCEEDED(GetTopLevelPendingBindStatusCallback(this,&pBindStatusCallback)))
{
GetHeadersAndPostData(pBindStatusCallback,&pszHeaders,&stgPostData,&cbPostData, pfIsPost);
pBindStatusCallback->Release();
fHavePostStg = TRUE;
if (stgPostData.tymed == TYMED_HGLOBAL)
{
pPostData = (LPBYTE) stgPostData.hGlobal;
}
}
// Fire a BeforeNavigate event to inform container that we are about
// to navigate and to give it a chance to cancel. We have to ask
// for post data and headers to pass to event, so only do this if someone
// is actually hooked up to the event (HasSinks() is TRUE).
//if (_bbd._pautoEDS->HasSinks())
{
TCHAR szFrameName[MAX_URL_STRING];
SHSTR strHeaders;
szFrameName[0] = 0;
// get our frame name
ITargetFrame2 *pOurTargetFrame;
hres = TargetQueryService(SAFECAST(this, IShellBrowser *), IID_PPV_ARG(ITargetFrame2, &pOurTargetFrame));
if (SUCCEEDED(hres))
{
LPOLESTR pwzFrameName = NULL;
pOurTargetFrame->GetFrameName(&pwzFrameName);
pOurTargetFrame->Release();
if (pwzFrameName)
{
SHUnicodeToTChar(pwzFrameName, szFrameName, ARRAYSIZE(szFrameName));
CoTaskMemFree(pwzFrameName);
}
}
strHeaders.SetStr(pszHeaders);
_pidlBeforeNavigateEvent = (LPITEMIDLIST) pidl; // no need to copy
// This is a work around for view linked weboc. Setting the frame name has side effects.
TCHAR * pEffectiveName;
if (_fIsViewLinkedWebOC && !szFrameName[0])
{
pEffectiveName = _szViewLinkedWebOCFrameName;
}
else
{
pEffectiveName = szFrameName[0] ? szFrameName : NULL;
}
// fire the event!
FireEvent_BeforeNavigate(_bbd._pautoEDS, _bbd._hwnd, _bbd._pautoWB2,
pidl, NULL, 0, pEffectiveName,
pPostData, cbPostData, strHeaders.GetStr(), &fCancelled);
ASSERT(_pidlBeforeNavigateEvent == pidl);
_pidlBeforeNavigateEvent = NULL;
// free anything we alloc'd above
if (pszHeaders)
{
LocalFree(pszHeaders);
pszHeaders = NULL;
}
if (fCancelled)
{
// container told us to cancel
hres = E_ABORT;
}
}
if (fHavePostStg)
{
ReleaseStgMedium(&stgPostData);
}
return hres;
}
HRESULT CBaseBrowser2::SetTopBrowser()
{
_fTopBrowser = TRUE;
#ifdef MESSAGEFILTER
if (!_lpMF)
{
/*
* Create a message filter here to pull out WM_TIMER's while we are
* busy. The animation timer, along with other timers, can get
* piled up otherwise which can fill the message queue and thus USER's
* heap.
*/
_lpMF = new CMsgFilter();
if (_lpMF && !(((CMsgFilter *)_lpMF)->Initialize()))
{
ATOMICRELEASE(_lpMF);
}
}
#endif
return S_OK;
}
HRESULT CBaseBrowser2::_ResizeView()
{
_pbsOuter->_UpdateViewRectSize();
if (_pact)
{
RECT rc;
GetBorder(&rc);
TraceMsg(TF_SHDUIACTIVATE, "UIW::SetBorderSpaceDW calling _pact->ResizeBorder");
_pact->ResizeBorder(&rc, this, TRUE);
}
return S_OK;
}
HRESULT CBaseBrowser2::_ResizeNextBorder(UINT itb)
{
// (derived class resizes inner toolbar if any)
return _ResizeView();
}
HRESULT CBaseBrowser2::_OnFocusChange(UINT itb)
{
#if 0
// the OC *does* get here (it's not aggregated so _pbsOuter->_OnFocusChange
// ends up here not in commonsb). not sure if E_NOTIMPL is o.k., but
// for now it's what we'll do...
ASSERT(0);
#endif
return E_NOTIMPL;
}
HRESULT CBaseBrowser2::OnFocusChangeIS(IUnknown* punkSrc, BOOL fSetFocus)
{
ASSERT(0); // split: untested!
// do we need to do _UIActivateView?
ASSERT(fSetFocus); // i think?
return E_NOTIMPL;
}
HRESULT CBaseBrowser2::v_ShowHideChildWindows(BOOL fChildOnly)
{
// (derived class does ShowDW on all toolbars)
if (!fChildOnly)
{
_pbsOuter->_ResizeNextBorder(0);
// This is called in ResizeNextBorder
// _UpdateViewRectSize();
}
return S_OK;
}
//*** _ExecChildren -- broadcast Exec to view and toolbars
// NOTES
// we might do *both* punkBar and fBroadcast if we want to send stuff
// to both the view and to all toolbars, e.g. 'stop' or 'refresh'.
//
// Note: n.b. the tray isn't a real toolbar, so it won't get called (sigh...).
HRESULT CBaseBrowser2::_ExecChildren(IUnknown *punkBar, BOOL fBroadcast, const GUID *pguidCmdGroup,
DWORD nCmdID, DWORD nCmdexecopt,
VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
{
//ASSERT(!fBroadcast); // only derived class supports this
// alanau: but CWebBrowserSB doesn't override this method, so we hit this assert.
// 1st, send to specified guy (if requested)
if (punkBar != NULL)
{
// send to specified guy
IUnknown_Exec(punkBar, pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
}
// (derived class broadcasts to toolbars)
return S_OK;
}
HRESULT CBaseBrowser2::_SendChildren(HWND hwndBar, BOOL fBroadcast, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
#if 0
// the OC *does* get here (it's not aggregated so _pbsOuter->_SendChildren
// ends up here not in commonsb). since there are no kids i guess it's
// ok to just drop the fBroadcast on the floor.
ASSERT(!fBroadcast); // only derived class supports this
#endif
// 1st, send to specified guy (if requested)
if (hwndBar != NULL)
{
// send to specified guy
SendMessage(hwndBar, uMsg, wParam, lParam);
}
// (derived class broadcasts to toolbars)
return S_OK;
}
//
// Handle <META HTTP-EQUIV ...> headers for lower components
//
HRESULT CBaseBrowser2::OnHttpEquiv(IShellView* psv, BOOL fDone, VARIANT *pvarargIn, VARIANT *pvarargOut)
{
return OLECMDERR_E_NOTSUPPORTED;
} // _HandleHttpEquiv
STDMETHODIMP CBaseBrowser2::GetPalette( HPALETTE * phpal )
{
BOOL fRes = FALSE;
if ( _hpalBrowser )
{
*phpal = _hpalBrowser;
fRes = TRUE;
}
return fRes ? NOERROR : E_FAIL;
}
//
// IPersist
//
HRESULT
CBaseBrowser2::GetClassID(CLSID *pclsid)
{
return E_NOTIMPL;
}
//
// IPersistHistory
//
#ifdef DEBUG
#define c_szFrameMagic TEXT("IE4ViewStream")
#define c_cchFrameMagic SIZECHARS(c_szFrameMagic)
#endif
// NOTE this is almost the same kind of data that is
// stored in a TravelEntry.
typedef struct _PersistedFrame {
DWORD cbSize;
DWORD type;
DWORD lock;
DWORD bid;
CLSID clsid;
DWORD cbPidl;
} PERSISTEDFRAME, *PPERSISTEDFRAME;
#define PFTYPE_USEPIDL 1
#define PFTYPE_USECLSID 2
#define PFFLAG_SECURELOCK 0x00000001
HRESULT GetPersistedFrame(IStream *pstm, PPERSISTEDFRAME ppf, LPITEMIDLIST *ppidl)
{
HRESULT hr;
ASSERT(pstm);
ASSERT(ppf);
ASSERT(ppidl);
#ifdef DEBUG
TCHAR szMagic[SIZECHARS(c_szFrameMagic)];
DWORD cbMagic = CbFromCch(c_cchFrameMagic);
ASSERT(SUCCEEDED(IStream_Read(pstm, (void *) szMagic, cbMagic)));
ASSERT(!StrCmp(szMagic, c_szFrameMagic));
#endif //DEBUG
// This is pointing to the stack, make sure it starts NULL
*ppidl = NULL;
if(SUCCEEDED(hr = IStream_Read(pstm, (void *) ppf, SIZEOF(PERSISTEDFRAME))))
{
if(ppf->cbSize == SIZEOF(PERSISTEDFRAME) && (ppf->type == PFTYPE_USECLSID || ppf->type == PFTYPE_USEPIDL))
{
// i used SHAlloc() cuz its what all the IL functions use
if(ppf->cbPidl)
*ppidl = (LPITEMIDLIST) SHAlloc(ppf->cbPidl);
if(*ppidl)
{
hr = IStream_Read(pstm, (void *) *ppidl, ppf->cbPidl);
if(FAILED(hr))
{
ILFree(*ppidl);
*ppidl = NULL;
}
}
else
hr = E_OUTOFMEMORY;
}
else
hr = E_UNEXPECTED;
}
return hr;
}
HRESULT
CBaseBrowser2::LoadHistory(IStream *pstm, IBindCtx *pbc)
{
HRESULT hr = E_INVALIDARG;
ASSERT(pstm);
TraceMsg(TF_TRAVELLOG, "CBB::LoadHistory entered pstm = %X, pbc = %d", pstm, pbc);
ATOMICRELEASE(_poleHistory);
ATOMICRELEASE(_pstmHistory);
ATOMICRELEASE(_pbcHistory);
if (pstm)
{
PERSISTEDFRAME pf;
LPITEMIDLIST pidl;
hr = GetPersistedFrame(pstm, &pf, &pidl);
if (SUCCEEDED(hr))
{
// need to restore the previous bid
// if this is a new window
ASSERT(pf.bid == _dwBrowserIndex || !_bbd._pidlCur);
_dwBrowserIndex = pf.bid;
_eSecureLockIconPending = pf.lock;
if (pf.type == PFTYPE_USECLSID)
{
hr = E_FAIL;
if (_pHTMLDocument)
{
if ( (_dwDocFlags & DOCFLAG_DOCCANNAVIGATE)
&& IsEqualCLSID(pf.clsid, CLSID_HTMLDocument))
{
IPersistHistory * pph;
hr = _pHTMLDocument->QueryInterface(IID_PPV_ARG(IPersistHistory, &pph));
if (SUCCEEDED(hr))
{
_fDontAddTravelEntry = TRUE;
hr = pph->LoadHistory(pstm, pbc);
TraceMsg(TF_TRAVELLOG, "CBB::LoadHistory called pole->LoadHistory, hr =%X", hr);
pph->Release();
return hr;
}
}
else
{
ATOMICRELEASE(_pHTMLDocument); // We are going to cocreate a new document.
}
}
if (S_OK != hr)
{
// Get the class and instantiate
hr = CoCreateInstance(pf.clsid, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, IID_PPV_ARG(IOleObject, &_poleHistory));
}
if (SUCCEEDED(hr))
{
DWORD dwFlags;
hr = _poleHistory->GetMiscStatus(DVASPECT_CONTENT, &dwFlags);
if (SUCCEEDED(hr))
{
if (dwFlags & OLEMISC_SETCLIENTSITEFIRST)
{
pstm->AddRef();
if (pbc)
pbc->AddRef();
// we need to addref because we will use it async
// whoever uses it needs to release.
_pstmHistory = pstm;
_pbcHistory = pbc;
}
else
{
IPersistHistory * pph;
hr = _poleHistory->QueryInterface(IID_PPV_ARG(IPersistHistory, &pph));
if (SUCCEEDED(hr))
{
hr = pph->LoadHistory(pstm, pbc);
TraceMsg(TF_TRAVELLOG, "CBB::LoadHistory called pole->LoadHistory, hr =%X", hr);
pph->Release();
}
}
// if we made then set the prepared history object in
// _poleHistory
if (FAILED(hr))
{
ATOMICRELEASE(_poleHistory);
ATOMICRELEASE(_pstmHistory);
ATOMICRELEASE(_pbcHistory);
}
}
}
}
//
// just browse the object
// if poleHistory is set, then when the dochost is created
// it will pick up the object and use it.
// other wise we will do a normal navigate.
//
if (pidl)
{
DEBUG_CODE(TCHAR szPath[INTERNET_MAX_URL_LENGTH + 1];)
DEBUG_CODE(SHGetNameAndFlags(pidl, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath), NULL);)
DEBUG_CODE(TraceMsg(DM_TRACE, "CBB::LoadHistory: URL - %ws", szPath);)
_fDontAddTravelEntry = TRUE;
hr = _psbOuter->BrowseObject(pidl, SBSP_SAMEBROWSER | SBSP_ABSOLUTE);
ILFree(pidl);
}
else
hr = E_OUTOFMEMORY;
}
}
TraceMsg(TF_TRAVELLOG, "CBB::LoadHistory exiting, hr =%X", hr);
_pbsOuter->UpdateBackForwardState();
return hr;
}
// Save a stream represeting the history at this point.
// Be sure to keep this and GetDummyWindowData in sync.
HRESULT
CBaseBrowser2::SaveHistory(IStream *pstm)
{
HRESULT hr = E_UNEXPECTED;
TraceMsg(TF_TRAVELLOG, "CBB::SaveHistory entering, pstm =%X", pstm);
ASSERT(pstm);
if(_bbd._pidlCur)
{
PERSISTEDFRAME pf = {0};
pf.cbSize = SIZEOF(pf);
pf.bid = GetBrowserIndex();
pf.cbPidl = ILGetSize(_bbd._pidlCur);
pf.type = PFTYPE_USEPIDL;
pf.lock = _bbd._eSecureLockIcon;
DEBUG_CODE(TCHAR szPath[INTERNET_MAX_URL_LENGTH + 1];)
DEBUG_CODE(IEGetDisplayName(_bbd._pidlCur, szPath, SHGDN_FORPARSING);)
ASSERT(SUCCEEDED(IStream_Write(pstm, (void *) c_szFrameMagic, CbFromCch(c_cchFrameMagic))));
//
// in order to use IPersistHistory we need to get the CLSID of the Ole Object
// then we need to get IPersistHistory off that object
// then we can save the PERSISTEDFRAME and the pidl and then pass
// the stream down into the objects IPersistHistory
//
// Right now we circumvent the view object for history - zekel - 18-JUL-97
// right now we just grab the DocObj from the view object, and then query
// the Doc for IPersistHistory. really what we should be doing is QI the view
// for pph, and then use it. however this requires risky work with the
// navigation stack, and thus should be postponed to IE5. looking to the future
// the view could persist all kinds of great state info
//
// but now we just get the background object. but check to make sure that it
// will be using the DocObjHost code by QI for IDocViewSite
//
// _bbd._psv can be null in subframes that have not completed navigating
// before a refresh is called
//
if (!_pphHistory && _bbd._psv)
{
hr = SafeGetItemObject(_bbd._psv, SVGIO_BACKGROUND, IID_PPV_ARG(IPersistHistory, &_pphHistory));
}
if (_pphHistory)
{
IDocViewSite *pdvs;
if (SUCCEEDED(_bbd._psv->QueryInterface(IID_PPV_ARG(IDocViewSite, &pdvs))) &&
SUCCEEDED(hr = _pphHistory->GetClassID(&(pf.clsid))))
{
pf.type = PFTYPE_USECLSID;
TraceMsg(TF_TRAVELLOG, "CBB::SaveHistory is PFTYPE_USECLSID");
}
ATOMICRELEASE(pdvs);
}
if (SUCCEEDED(hr = IStream_Write(pstm,(void *)&pf, pf.cbSize)))
hr = IStream_Write(pstm,(void *)_bbd._pidlCur, pf.cbPidl);
if (SUCCEEDED(hr) && pf.type == PFTYPE_USECLSID)
hr = _pphHistory->SaveHistory(pstm);
ATOMICRELEASE(_pphHistory);
}
TraceMsg(TF_TRAVELLOG, "CBB::SaveHistory exiting, hr =%X", hr);
return hr;
}
HRESULT CBaseBrowser2::SetPositionCookie(DWORD dwPositionCookie)
{
HRESULT hr = E_FAIL;
//
// we force the browser to update its internal location and the address bar
// this depends on the fact that setposition cookie was always
// started by a ptl->Travel(). so that the current position in the ptl
// is actually the correct URL for us to have. zekel - 22-JUL-97
//
ITravelLog *ptl;
GetTravelLog(&ptl);
if(ptl)
{
ITravelEntry *pte;
if(EVAL(SUCCEEDED(ptl->GetTravelEntry(SAFECAST(this, IShellBrowser *), 0, &pte))))
{
LPITEMIDLIST pidl;
if(SUCCEEDED(pte->GetPidl(&pidl)))
{
BOOL fUnused;
ASSERT(pidl);
if (SUCCEEDED(hr = _FireBeforeNavigateEvent(pidl, &fUnused)))
{
IPersistHistory *pph;
if(_bbd._psv && SUCCEEDED(hr = SafeGetItemObject(_bbd._psv, SVGIO_BACKGROUND, IID_PPV_ARG(IPersistHistory, &pph))))
{
ASSERT(pph);
// now that we are certain that we are going to call into
// the document, we need to update the entry right before.
// NOTE: after an update, we cannot revert if there was an
// error in the Set...
ptl->UpdateEntry(SAFECAST(this, IShellBrowser *), TRUE);
hr = pph->SetPositionCookie(dwPositionCookie);
pph->Release();
// this updates the browser to the new pidl,
// and navigates there directly if necessary.
BOOL fDidBrowse;
NotifyRedirect(_bbd._psv, pidl, &fDidBrowse);
if (!fDidBrowse)
{
// fire the event!
FireEvent_NavigateComplete(_bbd._pautoEDS, _bbd._pautoWB2, _bbd._pidlCur, _bbd._hwnd);
FireEvent_DocumentComplete(_bbd._pautoEDS, _bbd._pautoWB2, _bbd._pidlCur);
}
}
}
ILFree(pidl);
}
pte->Release();
}
ptl->Release();
}
TraceMsg(TF_TRAVELLOG, "CBB::SetPositionCookie exiting, cookie = %X, hr =%X", dwPositionCookie, hr);
return hr;
}
HRESULT CBaseBrowser2::GetPositionCookie(DWORD *pdwPositionCookie)
{
HRESULT hr = E_FAIL;
IPersistHistory *pph;
ASSERT(pdwPositionCookie);
if(pdwPositionCookie && _bbd._psv && SUCCEEDED(hr = SafeGetItemObject(_bbd._psv, SVGIO_BACKGROUND, IID_PPV_ARG(IPersistHistory, &pph))))
{
ASSERT(pph);
hr = pph->GetPositionCookie(pdwPositionCookie);
pph->Release();
}
TraceMsg(TF_TRAVELLOG, "CBB::GetPositionCookie exiting, cookie = %X, hr =%X", *pdwPositionCookie, hr);
return hr;
}
DWORD CBaseBrowser2::GetBrowserIndex()
{
// the first time we request the index, we init it.
if (!_dwBrowserIndex)
{
//
// the topframe browser all have the same browser index so
// that they can trade TravelEntries if necessary. because we now
// trade around TravelEntries, then we need to make the bids relatively
// unique. and avoid ever having a random frame be BID_TOPFRAMEBROWSER
//
if (IsTopFrameBrowser(SAFECAST(this, IServiceProvider *), SAFECAST(this, IShellBrowser *)))
_dwBrowserIndex = BID_TOPFRAMEBROWSER;
else do
{
_dwBrowserIndex = SHRandom();
} while (!_dwBrowserIndex || _dwBrowserIndex == BID_TOPFRAMEBROWSER);
// psp->Release();
TraceMsg(TF_TRAVELLOG, "CBB::GetBrowserIndex() NewFrame BID = %X", _dwBrowserIndex);
}
return _dwBrowserIndex;
}
HRESULT CBaseBrowser2::GetHistoryObject(IOleObject **ppole, IStream **ppstm, IBindCtx **ppbc)
{
ASSERT(ppole);
ASSERT(ppstm);
ASSERT(ppbc);
*ppole = _poleHistory;
*ppstm = _pstmHistory;
*ppbc = _pbcHistory;
// we dont need to release, because we are just giving away our
// reference.
_poleHistory = NULL;
_pstmHistory = NULL;
_pbcHistory = NULL;
if(*ppole)
return NOERROR;
ASSERT(!*ppstm);
return E_FAIL;
}
HRESULT CBaseBrowser2::SetHistoryObject(IOleObject *pole, BOOL fIsLocalAnchor)
{
if (!_poleHistory && !_fGeneratedPage)
{
ASSERT(pole);
// Note: _fIsLocalAnchor is ignored if (_dwDocFlags & DOCFLAG_DOCCANNAVIGATE)
// is TRUE. In that case, the document (Trident) can navigate
// itself and will take care of updating the travel log.
//
_fIsLocalAnchor = fIsLocalAnchor;
if (pole)
{
_poleHistory = pole;
_poleHistory->AddRef();
return NOERROR;
}
}
return E_FAIL;
}
HRESULT CBaseBrowser2::CacheOLEServer(IOleObject *pole)
{
TraceMsg(DM_CACHEOLESERVER, "CBB::CacheOLEServer called");
HRESULT hres;
IPersist* pps;
// ISVs want to turn off this caching because it's "incovenient"
// to have the browser hold onto their object. We can do that
// with a quick registry check here, but first let's make sure
// we don't have a real bug to fix...
hres = pole->QueryInterface(IID_PPV_ARG(IPersist, &pps));
if (FAILED(hres))
{
return hres;
}
CLSID clsid = CLSID_NULL;
hres = pps->GetClassID(&clsid);
pps->Release();
if (SUCCEEDED(hres))
{
SA_BSTRGUID str;
InitFakeBSTR(&str, clsid);
VARIANT v;
hres = _bbd._pautoWB2->GetProperty(str.wsz, &v);
if (SUCCEEDED(hres) && v.vt != VT_EMPTY)
{
// We already have it. We are fine.
TraceMsg(DM_CACHEOLESERVER, "CBB::CacheOLEServer not first time");
VariantClear(&v);
}
else
{
// We don't have it yet. Add it.
v.vt = VT_UNKNOWN;
v.punkVal = ClassHolder_Create(&clsid);
if (v.punkVal)
{
hres = _bbd._pautoWB2->PutProperty(str.wsz, v);
TraceMsg(DM_CACHEOLESERVER, "CBB::CacheOLEServer first time %x", hres);
v.punkVal->Release();
}
}
}
return hres;
}
HRESULT CBaseBrowser2::GetSetCodePage(VARIANT* pvarIn, VARIANT* pvarOut)
{
// Process the out parameter first so that the client can set and
// get the previous value in a single call.
if (pvarOut)
{
pvarOut->vt = VT_I4;
pvarOut->lVal = _cp;
}
if (pvarIn && pvarIn->vt==VT_I4)
{
TraceMsg(DM_DOCCP, "CBB::GetSetCodePage changing _cp from %d to %d", _cp, pvarIn->lVal);
_cp = pvarIn->lVal;
}
return S_OK;
}
HRESULT CBaseBrowser2::GetPidl(LPITEMIDLIST *ppidl)
{
ASSERT(ppidl);
*ppidl = ILClone(_bbd._pidlCur);
return *ppidl ? S_OK : E_FAIL;
}
HRESULT CBaseBrowser2::SetReferrer(LPITEMIDLIST pidl)
{
return E_NOTIMPL;
}
HRESULT CBaseBrowser2::GetBrowserByIndex(DWORD dwID, IUnknown **ppunk)
{
HRESULT hr = E_FAIL;
ASSERT(ppunk);
*ppunk = NULL;
//gotta get the target frame ...
ITargetFramePriv * ptf;
if(SUCCEEDED(_ptfrm->QueryInterface(IID_PPV_ARG(ITargetFramePriv, &ptf))))
{
ASSERT(ptf);
hr = ptf->FindBrowserByIndex(dwID, ppunk);
ptf->Release();
}
return hr;
}
HRESULT CBaseBrowser2::GetTravelLog(ITravelLog **pptl)
{
*pptl = NULL;
if (!_bbd._ptl)
{
IBrowserService *pbs;
if (SUCCEEDED(_pspOuter->QueryService(SID_STopFrameBrowser, IID_PPV_ARG(IBrowserService, &pbs))))
{
if (IsSameObject(SAFECAST(this, IBrowserService *), pbs))
{
// we are it, so we need to make us a TravelLog
CreateTravelLog(&_bbd._ptl);
}
else
{
pbs->GetTravelLog(&_bbd._ptl);
}
pbs->Release();
}
}
return _bbd._ptl ? _bbd._ptl->QueryInterface(IID_PPV_ARG(ITravelLog, pptl)) : E_FAIL;
}
HRESULT CBaseBrowser2::InitializeTravelLog(ITravelLog* ptl, DWORD dwBrowserIndex)
{
ptl->QueryInterface(IID_PPV_ARG(ITravelLog, &_bbd._ptl)); // hold a copy
_dwBrowserIndex = dwBrowserIndex;
return S_OK;
}
// Let the top level browser know that it might need to update it's zones information
void CBaseBrowser2::_Exec_psbMixedZone()
{
IShellBrowser *psbTop;
if (SUCCEEDED(_pspOuter->QueryService(SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psbTop))))
{
IUnknown_Exec(psbTop, &CGID_Explorer, SBCMDID_MIXEDZONE, 0, NULL, NULL);
psbTop->Release();
}
}
STDMETHODIMP CBaseBrowser2::QueryUseLocalVersionVector(BOOL *pfUseLocal)
{
*pfUseLocal = FALSE;
return S_OK;
}
STDMETHODIMP CBaseBrowser2::QueryVersionVector(IVersionVector *pVersion)
{
HRESULT hr;
ULONG cchVer = 0;
// Was IE's version set by a registry entry?
// This simplified call checks for presence of the IE string
//
hr = pVersion->GetVersion(L"IE", NULL, &cchVer);
ASSERT(hr == S_OK);
if (cchVer == 0)
{
// Not already set. Set it to default value.
// Note that the four digits of precision is required due to a peculiarity in the parser.
//
hr = pVersion->SetVersion(L"IE", L"6.0000");
}
return hr;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::FindWindowByIndex
//
// Interface : ITravelLogClient
//
// Synopsis : Returns the window with the given index.
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::FindWindowByIndex(DWORD dwID, IUnknown ** ppunk)
{
return GetBrowserByIndex(dwID, ppunk);
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::GetWindowData
//
// Interface : ITravelLogClient
//
// Synopsis : Returns a WINDOWDATA structure containing pertinent
// window information needed for the travel log..
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::GetWindowData(LPWINDOWDATA pWinData)
{
HRESULT hr = S_OK;
ASSERT(pWinData);
ASSERT(_bbd._pidlCur);
DEBUG_CODE(TCHAR szPath[INTERNET_MAX_URL_LENGTH + 1];)
DEBUG_CODE(IEGetDisplayName(_bbd._pidlCur, szPath, SHGDN_FORPARSING);)
DEBUG_CODE(TraceMsg(DM_TRACE, "CBB::ActivatePendingView (TRAVELLOG): _UpdateTravelLog called from shdocvw for %ws", szPath);)
ZeroMemory(pWinData, sizeof(WINDOWDATA));
// Window ID and codepage
//
pWinData->dwWindowID = GetBrowserIndex();
pWinData->uiCP = _cp;
// Current Pidl
//
pWinData->pidl = ILClone(_bbd._pidlCur);
// Title - when we are in a shell view,
// _bbd._pszTitleCur is NULL, which is correct.
// However, we still have to create memory for
// pWinData->lpszTitle and you can't pass NULL
// to SHStrDupW.
//
if (_bbd._pszTitleCur)
hr = SHStrDupW(_bbd._pszTitleCur, &pWinData->lpszTitle);
return hr;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::LoadHistoryPosition
//
// Interface : ITravelLogClient
//
// Synopsis : Loads the Url location and position cookie. This is used
// during a history navigation in a frame that involves a
// local anchor.
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::LoadHistoryPosition(LPOLESTR pszUrlLocation, DWORD dwCookie)
{
IEPlaySound(TEXT("Navigating"), FALSE);
return SetPositionCookie(dwCookie);
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::GetDummyWindowData
//
// Interface : ITravelLogClient2
//
// Synopsis : Fills in a windowdata and a stream very similar to
// that created by SaveHistory.
// Be sure to keep this and SaveHistory in sync.
//
//--------------------------------------------------------------------------
STDMETHODIMP CBaseBrowser2::GetDummyWindowData(
LPWSTR pszUrl,
LPWSTR pszTitle,
LPWINDOWDATA pWinData)
{
HRESULT hres = S_OK;
PERSISTEDFRAME pf = {0};
LPITEMIDLIST pidl;
// grab current window ID
pWinData->dwWindowID = GetBrowserIndex();
// everything else is dummy
pWinData->uiCP = 0;
hres = SHStrDup(pszUrl, &pWinData->lpszUrl);
hres = SHStrDup(pszTitle, &pWinData->lpszTitle);
if (!pWinData->pStream)
{
hres = CreateStreamOnHGlobal(NULL, FALSE, &pWinData->pStream);
if (hres)
goto done;
}
pidl = PidlFromUrl(pszUrl);
pf.cbSize = SIZEOF(pf);
pf.bid = GetBrowserIndex();
pf.cbPidl = ILGetSize(pidl);
pf.type = PFTYPE_USEPIDL;
pf.lock = 0; // _bbd._eSecureLockIcon;
ASSERT(SUCCEEDED(IStream_Write(pWinData->pStream, (void *) c_szFrameMagic, CbFromCch(c_cchFrameMagic))));
if (SUCCEEDED(hres = IStream_Write(pWinData->pStream,(void *)&pf, pf.cbSize)))
hres = IStream_Write(pWinData->pStream,(void *)pidl, pf.cbPidl);
ILFree(pidl);
done:
return SUCCEEDED(hres) ? S_OK : E_FAIL;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::UpdateDesktopComponent
//
// Interface : ITridentService
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::UpdateDesktopComponent(IHTMLWindow2 * pHTMLWindow)
{
BSTR bstrTitle = NULL;
BSTR bstrUrlUnencoded = NULL;
HRESULT hr = S_OK;
IHTMLDocument2 * pHTMLDocument2;
IHTMLDocument4 * pHTMLDocument4;
DWORD dwOptions = 0;
hr = pHTMLWindow->get_document(&pHTMLDocument2);
if (SUCCEEDED(hr))
{
hr = pHTMLDocument2->QueryInterface(IID_PPV_ARG(IHTMLDocument4, &pHTMLDocument4));
if (SUCCEEDED(hr))
{
pHTMLDocument2->get_title(&bstrTitle);
pHTMLDocument4->get_URLUnencoded(&bstrUrlUnencoded);
TraceMsg(DM_TRACE, "CBaseBrowser2::UpdateDesktopComponent: URLUnencoded - %ws; Title - %ws",
bstrUrlUnencoded, bstrTitle);
// Update the desktop component's friendly name.
//
UpdateDesktopComponentName(bstrUrlUnencoded, bstrTitle);
SysFreeString(bstrTitle);
SysFreeString(bstrUrlUnencoded);
pHTMLDocument4->Release();
}
pHTMLDocument2->Release();
}
return hr;
}
HRESULT
CBaseBrowser2::_InitDocHost(IWebBrowser2 * pWebBrowser)
{
HRESULT hr = E_FAIL;
IDispatch* pDocDispatch = NULL;
ASSERT(pWebBrowser);
// get the IHTMLWindow2 for the WebOC windowop
hr = pWebBrowser->get_Document(&pDocDispatch);
if (S_OK == hr && pDocDispatch)
{
IHTMLDocument2* pDoc;
hr = pDocDispatch->QueryInterface(IID_PPV_ARG(IHTMLDocument2, &pDoc));
pDocDispatch->Release();
if (S_OK == hr)
{
IHTMLWindow2* pHtmlWindow;
hr = pDoc->get_parentWindow(&pHtmlWindow);
pDoc->Release();
if (S_OK == hr)
{
BOOL fIsFrameWindow = IsFrameWindow(pHtmlWindow);
pHtmlWindow->Release();
if (!fIsFrameWindow && _bbd._pctView)
{
hr = _bbd._pctView->Exec(&CGID_ShellDocView,
SHDVID_NAVSTART,
0,
NULL,
NULL);
}
}
}
}
return hr;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::FireBeforeNavigate2
//
// Interface : ITridentService
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::FireBeforeNavigate2(IDispatch * pDispatch,
LPCTSTR lpszUrl,
DWORD dwFlags,
LPCTSTR lpszFrameName,
LPBYTE pPostData,
DWORD cbPostData,
LPCTSTR lpszHeaders,
BOOL fPlayNavSound,
BOOL * pfCancel)
{
HRESULT hr = S_OK;
BSTR bstrUrl = NULL;
LPITEMIDLIST pidl = NULL;
IWebBrowser2 * pWebBrowser = NULL;
ASSERT(pfCancel);
*pfCancel = FALSE;
// Stress fix
//
if (NULL == lpszUrl)
{
bstrUrl = SysAllocString(_T(""));
}
else
{
bstrUrl = SysAllocString(lpszUrl);
}
pidl = PidlFromUrl(bstrUrl);
if (pidl)
{
hr = _GetWebBrowserForEvt(pDispatch, &pWebBrowser);
if (S_OK == hr)
{
ASSERT(pWebBrowser);
_pidlBeforeNavigateEvent = pidl; // no need to copy.
// This is a work around for view linked weboc. Setting the frame name has side effects.
LPCTSTR pEffectiveName;
if (_fIsViewLinkedWebOC && (lpszFrameName == NULL || !lpszFrameName[0]))
{
pEffectiveName = _szViewLinkedWebOCFrameName;
}
else
{
pEffectiveName = lpszFrameName;
}
FireEvent_BeforeNavigate(pWebBrowser, _bbd._hwnd, pWebBrowser,
pidl, NULL, dwFlags, pEffectiveName,
pPostData, cbPostData, lpszHeaders, pfCancel);
// Make sure that we remove earlier URLs that are cached during redirections.
_InitDocHost(pWebBrowser);
ATOMICRELEASE(pWebBrowser);
ASSERT(_pidlBeforeNavigateEvent == pidl);
_pidlBeforeNavigateEvent = NULL;
ILFree(pidl);
if (!*pfCancel && fPlayNavSound)
{
IEPlaySound(TEXT("Navigating"), FALSE);
}
}
}
if (_phtmlWS && !pDispatch)
{
if (!_bbd._psvPending)
{
_phtmlWS->ViewReleased();
}
else
{
_phtmlWS->ViewReleaseIntelliForms();
}
}
else if (_phtmlWS && !_bbd._psvPending)
{
_DismissFindDialog();
}
SysFreeString(bstrUrl);
return hr;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::FireNavigateError
//
// Interface : ITridentService2
//
// Called when there is a binding error
//
// Input : pHTMLWindow - used to determine if we are a frame or
// the top-level.
// bstrURL - the URL which caused the error.
// bstrTargetFrameName - the frame being targeted.
// dwStatusCode - the binding error
// pfCancel - set by the host if it wants to
// cancel autosearch or friendly error
// pages
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::FireNavigateError(IHTMLWindow2 * pHTMLWindow2,
BSTR bstrURL,
BSTR bstrTargetFrameName,
DWORD dwStatusCode,
BOOL * pfCancel)
{
ASSERT(dwStatusCode != 0);
HRESULT hr = S_OK;
IWebBrowser2 * pWebBrowser = NULL;
*pfCancel = FALSE;
//
// Use top-level if window is not specified or the window is the top-level
//
if ((pHTMLWindow2 != NULL) && IsFrameWindow(pHTMLWindow2))
{
hr = _GetWebBrowserForEvt(pHTMLWindow2, &pWebBrowser);
}
else
{
hr = _GetWebBrowserForEvt(NULL, &pWebBrowser);
}
if (S_OK == hr)
{
ASSERT(pWebBrowser);
TCHAR szUrl[INTERNET_MAX_URL_LENGTH];
LPITEMIDLIST pidl = NULL;
if (bstrURL)
{
StrCpyN(szUrl, bstrURL, ARRAYSIZE(szUrl));
}
else
{
StrCpyN(szUrl, _T(""), ARRAYSIZE(szUrl));
}
pidl = PidlFromUrl(szUrl);
if (pidl)
{
FireEvent_NavigateError(pWebBrowser,
pWebBrowser,
pidl,
bstrTargetFrameName,
dwStatusCode,
pfCancel);
ILFree(pidl);
}
}
ATOMICRELEASE(pWebBrowser);
return hr;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::FirePrintTemplateEvent
//
// Interface : ITridentService
//
// Called when a template is instantiate or torndown
//
// pHTMLWindow is used to determine if we are a frame or the top-level
// dispidPrintEvent either DISPID_PRINTTEMPLATEINSTANTIATION
// or DISPID_PRINTTEMPLATETEARDOWN
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::FirePrintTemplateEvent(IHTMLWindow2 * pHTMLWindow2, DISPID dispidPrintEvent)
{
HRESULT hr = S_OK;
IWebBrowser2 * pWebBrowser = NULL;
//
// Use top-level if window is not specified or the window is the top-level
//
if ((pHTMLWindow2 != NULL) && IsFrameWindow(pHTMLWindow2))
{
hr = _GetWebBrowserForEvt(pHTMLWindow2, &pWebBrowser);
}
else
{
hr = _GetWebBrowserForEvt(NULL, &pWebBrowser);
}
if (S_OK == hr)
{
ASSERT(pWebBrowser);
FireEvent_PrintTemplateEvent(pWebBrowser,
pWebBrowser,
dispidPrintEvent);
}
ATOMICRELEASE(pWebBrowser);
return hr;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::FireUpdatePageStatus
//
// Interface : ITridentService
//
// Called when a template is instantiate or torndown
//
// pHTMLWindow is used to determine if we are a frame or the top-level
// nPage defined to be the number of pages spooled
// fDone a flag to indicate that the last page has been sppoled
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::FireUpdatePageStatus(IHTMLWindow2 * pHTMLWindow2, DWORD nPage, BOOL fDone)
{
HRESULT hr = S_OK;
IWebBrowser2 * pWebBrowser = NULL;
//
// Use top-level if window is not specified or the window is the top-level
//
if ((pHTMLWindow2 != NULL) && IsFrameWindow(pHTMLWindow2))
{
hr = _GetWebBrowserForEvt(pHTMLWindow2, &pWebBrowser);
}
else
{
hr = _GetWebBrowserForEvt(NULL, &pWebBrowser);
}
if (S_OK == hr)
{
ASSERT(pWebBrowser);
FireEvent_UpdatePageStatus(pWebBrowser,
pWebBrowser,
nPage,
fDone);
}
ATOMICRELEASE(pWebBrowser);
return hr;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::FirePrivacyImpactedStateChange
//
// Interface : ITridentService2
//
// Called whenever the global privacy impacted state changes
//
// Input : The new privacy impacted state
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::FirePrivacyImpactedStateChange(BOOL bPrivacyImpacted)
{
HRESULT hr = S_OK;
IWebBrowser2 * pWebBrowser = NULL;
//
// Update browser frame / save state
//
_UpdatePrivacyIcon(TRUE, bPrivacyImpacted);
//
// We always fire this at the top level
//
hr = _GetWebBrowserForEvt(NULL, &pWebBrowser);
if (S_OK == hr)
{
ASSERT(pWebBrowser);
FireEvent_PrivacyImpactedStateChange(pWebBrowser,
bPrivacyImpacted);
}
ATOMICRELEASE(pWebBrowser);
return hr;
}
HRESULT
CBaseBrowser2::_DismissFindDialog()
{
BSTR bstrName = SysAllocString(STR_FIND_DIALOG_NAME);
if (bstrName)
{
VARIANT varProp = {0};
_bbd._pautoWB2->GetProperty(bstrName, &varProp);
if ( (varProp.vt == VT_DISPATCH) && (varProp.pdispVal != NULL) )
{
IUnknown* pWindow = varProp.pdispVal;
//now that we've pulled the pdispVal out of the propbag, clear the property on the automation object
VARIANT varTmp = {0};
_bbd._pautoWB2->PutProperty(bstrName, varTmp);
//(davemi) see IE5 bug 57060 for why the below line doesn't work and IDispatch must be used instead
//pWindow->close();
IDispatch * pdisp;
if (SUCCEEDED(pWindow->QueryInterface(IID_PPV_ARG(IDispatch, &pdisp))))
{
DISPID dispid;
DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
BSTR bstrClose = SysAllocString(L"close");
if (bstrClose)
{
HRESULT hr;
hr = pdisp->GetIDsOfNames(IID_NULL, &bstrClose, 1, LOCALE_SYSTEM_DEFAULT, &dispid);
if (hr == S_OK)
pdisp->Invoke(dispid, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &dispparamsNoArgs, &varTmp, NULL, NULL);
SysFreeString(bstrClose);
}
pdisp->Release();
}
}
VariantClear(&varProp);
SysFreeString(bstrName);
}
return S_OK;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::_GetWebBrowserForEvt
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::_GetWebBrowserForEvt(IDispatch * pDispatch,
IWebBrowser2 ** ppWebBrowser)
{
if (_fIsViewLinkedWebOC && _pDispViewLinkedWebOCFrame && _fHadFirstBeforeNavigate)
{
*ppWebBrowser = _pDispViewLinkedWebOCFrame;
_pDispViewLinkedWebOCFrame->AddRef();
return S_OK;
}
else if (pDispatch) // Top-level
{
return IUnknown_QueryService(pDispatch,
SID_SWebBrowserApp,
IID_PPV_ARG(IWebBrowser2, ppWebBrowser));
}
else
{
*ppWebBrowser = _bbd._pautoWB2;
if (*ppWebBrowser)
{
(*ppWebBrowser)->AddRef();
return S_OK;
}
return E_FAIL;
}
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::GetUrlSearchComponent
//
// Interface : ITridentService
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::GetUrlSearchComponent(BSTR * pbstrSearch)
{
TCHAR sz[MAX_URL_STRING];
ASSERT(pbstrSearch);
ASSERT(!*pbstrSearch);
if (ILGetHiddenString(_bbd._pidlPending ? _bbd._pidlPending : _bbd._pidlCur,
IDLHID_URLQUERY,
sz,
SIZECHARS(sz)))
{
*pbstrSearch = SysAllocString(sz);
}
return (*pbstrSearch) ? S_OK : E_FAIL;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::IsErrorUrl
//
// Interface : ITridentService
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::IsErrorUrl(LPCTSTR lpszUrl, BOOL *pfIsError)
{
HRESULT hr = S_OK;
if (!lpszUrl || !pfIsError)
{
hr = E_INVALIDARG;
goto Cleanup;
}
*pfIsError = ::IsErrorUrl(lpszUrl);
Cleanup:
return hr;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::FireNavigateComplete2
//
// Interface : ITridentService
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::FireNavigateComplete2(IHTMLWindow2 * pHTMLWindow2,
DWORD dwFlags)
{
if (!pHTMLWindow2)
return E_POINTER;
HRESULT hr = E_FAIL;
BOOL fIsErrorUrl = FALSE;
BSTR bstrUrl = GetHTMLWindowUrl(pHTMLWindow2);
if (bstrUrl)
{
// If the URL is a res: URL, _GetPidlForDisplay will return
// the URL after the # in the res: URL.
//
LPITEMIDLIST pidl = _GetPidlForDisplay(bstrUrl, &fIsErrorUrl);
if (pidl)
{
BOOL fViewActivated = FALSE;
IWebBrowser2 * pWebBrowser = NULL;
// If this is not a frame, we update the browser
// state and pass the browser's IWebBrowser2 to
// FireEvent_NavigateComplete. If this is a frame,
// we pass the IWebBrowser2 of the window.
//
if (!(dwFlags & NAVDATA_FRAMEWINDOW))
{
fViewActivated = _ActivateView(bstrUrl, pidl, dwFlags, fIsErrorUrl);
if (_dwDocFlags & DOCFLAG_DOCCANNAVIGATE)
{
ATOMICRELEASE(_pHTMLDocument);
pHTMLWindow2->get_document(&_pHTMLDocument);
}
if (IEHard_ShowOnNavigateComplete())
{
EnableModelessSB(FALSE);
IEHard_NavWarning(_bbd._hwnd, IEHard_HostedInIE((IShellBrowser*)this));
EnableModelessSB(TRUE);
}
}
else
{
if (IsWindowVisible(_bbd._hwnd)
&& !(_dwSBSPQueued & SBSP_WRITENOHISTORY)
&& !(dwFlags & NAVDATA_FRAMECREATION)
&& !(dwFlags & NAVDATA_RESTARTLOAD))
{
IEPlaySound(TEXT("ActivatingDocument"), FALSE);
}
if (_pbsOuter)
{
_pbsOuter->UpdateBackForwardState();
}
}
hr = _GetWebBrowserForEvt((dwFlags & NAVDATA_FRAMEWINDOW) ? pHTMLWindow2 : NULL,
&pWebBrowser);
if (S_OK == hr && !fViewActivated)
{
ASSERT(pWebBrowser);
// fire the event!
FireEvent_NavigateComplete(pWebBrowser, pWebBrowser, pidl, _bbd._hwnd);
}
ATOMICRELEASE(pWebBrowser);
ILFree(pidl);
}
SysFreeString(bstrUrl);
}
return hr;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::_ActivateView
//
// Synopsis : If there is a pending view, it will be activated. If there
// if not a pending view, which is the case when we are
// navigating due to a hyperlink, OM navigation, or frame
// navigation, we update the browser state. (The view is already
// active in this case.) In either case, we update the
// state of the dochost.
//
//--------------------------------------------------------------------------
BOOL
CBaseBrowser2::_ActivateView(BSTR bstrUrl,
LPITEMIDLIST pidl,
DWORD dwFlags,
BOOL fIsErrorUrl)
{
BOOL fViewActivated = FALSE;
// Activate the pending view if there is one.
//
if (_bbd._psvPending)
{
ILFree(_bbd._pidlPending);
_bbd._pidlPending = ILClone(pidl);
_fDontUpdateTravelLog = !!(dwFlags & NAVDATA_DONTUPDATETRAVELLOG);
ASSERT(_pbsOuter);
_pbsOuter->ActivatePendingView();
_fDontUpdateTravelLog = FALSE;
fViewActivated = TRUE;
}
else
{
UINT uProt = GetUrlSchemeW(bstrUrl);
if ( uProt != URL_SCHEME_JAVASCRIPT
&& uProt != URL_SCHEME_VBSCRIPT)
{
_UpdateBrowserState(pidl);
}
// In the case where there is a pending view
// ActivatePendingView() will call ViewActivated().
// Also, the call to ViewActivated() must happen
// after the current pidl has changed. The current
// pidl changes in _UpdateBrowserState().
//
if (_phtmlWS)
{
_phtmlWS->ViewActivated();
}
// Don't play sound for the first navigation (to avoid multiple
// sounds to be played for a frame-set creation).
//
ASSERT(_bbd._psv);
if (IsWindowVisible(_bbd._hwnd) && !(_dwSBSPQueued & SBSP_WRITENOHISTORY))
{
IEPlaySound(TEXT("ActivatingDocument"), FALSE);
}
}
// In the case of an error URL, we must send the original URL
// to the dochost. It needs the res: URL in the case of an
// error page so it knows not to update the history.
//
if (!fIsErrorUrl)
{
_UpdateDocHostState(pidl, fIsErrorUrl);
}
else
{
LPITEMIDLIST pidlOriginal = PidlFromUrl(bstrUrl);
if (pidlOriginal)
{
_UpdateDocHostState(pidlOriginal, fIsErrorUrl);
ILFree(pidlOriginal);
}
}
if (_pbsOuter)
{
_pbsOuter->_OnFocusChange(ITB_VIEW);
}
return fViewActivated;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::_UpdateBrowserState
//
//--------------------------------------------------------------------------
void
CBaseBrowser2::_UpdateBrowserState(LPCITEMIDLIST pidl)
{
ASSERT(pidl);
ILFree(_bbd._pidlCur);
_bbd._pidlCur = ILClone(pidl);
// With the _bbd._pidlCur now updated, we can now call UpdateWindowList to
// update the window list with the new pidl.
//
_pbsOuter->UpdateWindowList();
_fGeneratedPage = FALSE;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::_UpdateDocHostState
//
//--------------------------------------------------------------------------
void
CBaseBrowser2::_UpdateDocHostState(LPITEMIDLIST pidl, BOOL fIsErrorUrl) const
{
DOCHOSTUPDATEDATA dhud;
VARIANT varVal;
ASSERT( (_bbd._psvPending && _bbd._pidlPending)
|| (!_bbd._psvPending && !_bbd._pidlPending));
ASSERT(!_bbd._pidlPending || ILIsEqual(_bbd._pidlPending, pidl) || fIsErrorUrl);
IShellView * psv = _bbd._psvPending ? _bbd._psvPending : _bbd._psv;
ASSERT(psv);
dhud._pidl = pidl;
dhud._fIsErrorUrl = fIsErrorUrl;
varVal.vt = VT_PTR;
varVal.byref = &dhud;
IUnknown_Exec(psv, &CGID_ShellDocView,
SHDVID_UPDATEDOCHOSTSTATE, 0, &varVal, NULL);
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::FireDocumentComplete
//
// Interface : ITridentService
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::FireDocumentComplete(IHTMLWindow2 * pHTMLWindow2,
DWORD dwFlags)
{
if (!pHTMLWindow2)
return E_POINTER;
HRESULT hr = E_FAIL;
BSTR bstrUrl = GetHTMLWindowUrl(pHTMLWindow2);
if (bstrUrl)
{
LPITEMIDLIST pidl = _GetPidlForDisplay(bstrUrl);
if (pidl)
{
IWebBrowser2 * pWebBrowser;
hr = _GetWebBrowserForEvt((dwFlags & NAVDATA_FRAMEWINDOW) ? pHTMLWindow2 : NULL,
&pWebBrowser);
ASSERT(pWebBrowser);
if (S_OK == hr)
{
FireEvent_DocumentComplete(pWebBrowser, pWebBrowser, pidl);
pWebBrowser->Release();
}
ILFree(pidl);
}
SysFreeString(bstrUrl);
}
if (!(dwFlags & NAVDATA_FRAMEWINDOW) && !IEHard_ShowOnNavigateComplete())
{
if (_CanShowModalDialogNow())
{
// Even if we think we can, don't do it from inside DocumentComplete or any other Trident event.
// Pumping messages during any of these events is bad for Trident, so we'll do it slightly later.
PostMessage(_bbd._hwnd, WMC_IEHARD_NAVWARNING, 0, 0);
}
}
return hr;
}
// Determines if we are in a reasonable state to show a random dialog box out of nowhere.
// The browser can get into states where the window is basically useless and no view
// is hosted. One such case is when a new window navigates to unhostable content
// (like an exe file). If we put up a dialog at a bad time, we'll disrupt some sensitive
// Trident timing and the content won't be sent to the download dialog.
BOOL CBaseBrowser2::_CanShowModalDialogNow()
{
return (_bbd._hwnd && IsWindow(_bbd._hwnd) && IsWindowVisible(_bbd._hwnd) &&
_bbd._hwndView && IsWindow(_bbd._hwndView) && IsWindowVisible(_bbd._hwndView));
}
void CBaseBrowser2::_ShowIEHardNavWarning()
{
if (_CanShowModalDialogNow())
{
// If GetLastActivePopup returns something other than the supplied window, it means
// there is a modal or modeless dialog that is active, and we don't bother showing any message.
HWND hwndPopup = GetLastActivePopup(_bbd._hwnd);
if (hwndPopup == _bbd._hwnd)
{
// Most of the time, this means that there are no dialogs attached to us.
// But there could still be a modeless dialog showing; it just isn't active right now.
// We'll show our messagebox anyway, because the same thing would happen if you opened
// the About dialog yourself -- it would be parented to the top window, and the modeless
// dialog would still be dangling around and enabled.
EnableModelessSB(FALSE);
IEHard_NavWarning(_bbd._hwnd, IEHard_HostedInIE((IShellBrowser*)this));
EnableModelessSB(TRUE);
}
}
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::FireDownloadBegin
//
// Interface : ITridentService
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::FireDownloadBegin()
{
return _updateNavigationUI();
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::FireDownloadComplete
//
// Interface : ITridentService
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::FireDownloadComplete()
{
return _updateNavigationUI();
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::GetPendingUrl
//
// Interface : ITridentService
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::GetPendingUrl(BSTR * pbstrPendingUrl)
{
if (!pbstrPendingUrl)
return E_POINTER;
*pbstrPendingUrl = NULL;
LPITEMIDLIST pidl = _bbd._pidlPending ? _bbd._pidlPending : _bbd._pidlCur;
TCHAR szPath[INTERNET_MAX_URL_LENGTH + 1];
HRESULT hr = IEGetDisplayName(pidl, szPath, SHGDN_FORPARSING);
if (S_OK == hr)
{
TraceMsg(DM_TRACE, "CBaseBrowser2::GetPendingUrl - URL: %ws", szPath);
*pbstrPendingUrl = SysAllocString(szPath);
}
return (*pbstrPendingUrl) ? S_OK : E_FAIL;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::ActiveElementChanged
//
// Interface : ITridentService
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::ActiveElementChanged(IHTMLElement * pHTMLElement)
{
// Forward the call to the OmWindow
//
if (_phtmlWS)
{
return _phtmlWS->ActiveElementChanged(pHTMLElement);
}
return E_FAIL;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::InitAutoImageResize()
//
// Interface : ITridentService2
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::InitAutoImageResize()
{
HRESULT hr = S_OK;
if (_pAIResize)
UnInitAutoImageResize();
CAutoImageResize *pAIResize = new CAutoImageResize();
if (pAIResize)
{
// need to get a pDoc2 for the init call...
IDispatch *pDocDispatch;
hr = _bbd._pautoWB2->get_Document(&pDocDispatch);
if (SUCCEEDED(hr))
{
IHTMLDocument2 *pDoc2;
hr = pDocDispatch->QueryInterface(IID_PPV_ARG(IHTMLDocument2,&pDoc2));
if (SUCCEEDED(hr))
{
// init the object
pAIResize->Init(pDoc2);
// cache the pointer for destruction later
_pAIResize=pAIResize;
pDoc2->Release();
}
pDocDispatch->Release();
}
if (FAILED(hr))
{
delete pAIResize;
}
}
else
hr = E_OUTOFMEMORY;
return hr;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::UnInitAutoImageResize()
//
// Interface : ITridentService2
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::UnInitAutoImageResize()
{
if (_pAIResize)
{
_pAIResize->UnInit();
ATOMICRELEASE(_pAIResize);
}
return S_OK;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::IsGalleryMeta
//
// Interface : ITridentService2
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::IsGalleryMeta(BOOL bFlag, void *pMyPics)
{
// Forward the call to the CMyPics object
//
if (pMyPics)
{
((CMyPics*)pMyPics)->IsGalleryMeta(bFlag);
return S_OK;
}
return E_FAIL;
}
//+-------------------------------------------------------------------------
//
// Functions that live in mypics.cpp that are called below
//
//--------------------------------------------------------------------------
HRESULT SendDocToMailRecipient(LPCITEMIDLIST pidl, UINT uiCodePage, DWORD grfKeyState, IUnknown *pUnkSite);
BOOL MP_IsEnabledInIEAK();
BOOL MP_IsEnabledInRegistry();
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::EmailPicture
//
// Interface : ITridentService2
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::EmailPicture(BSTR bstrURL)
{
IDispatch *pDocDispatch;
HRESULT hr = _bbd._pautoWB2->get_Document(&pDocDispatch);
if (SUCCEEDED(hr))
{
IHTMLDocument2 *pDoc2;
hr = pDocDispatch->QueryInterface(IID_PPV_ARG(IHTMLDocument2, &pDoc2));
if (SUCCEEDED(hr))
{
// Get that cmd target thingie...
IOleCommandTarget *pcmdt;
hr = IUnknown_QueryService(pDoc2, SID_SWebBrowserApp, IID_PPV_ARG(IOleCommandTarget, &pcmdt));
if (SUCCEEDED(hr))
{
// ... and thus the pidl...
LPITEMIDLIST pidlForImg;
hr = IEParseDisplayName(CP_ACP, bstrURL, &pidlForImg);
if (SUCCEEDED(hr))
{
// ... and pray this works...
SendDocToMailRecipient(pidlForImg, 0, FORCE_COPY, pcmdt);
ILFree(pidlForImg);
}
pcmdt->Release();
}
pDoc2->Release();
}
pDocDispatch->Release();
}
return hr;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::AttachMyPics
//
// Interface : ITridentService2
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::AttachMyPics(void *pDoc2, void **ppMyPics)
{
IHTMLDocument2 *pdoc2 = (IHTMLDocument2 *)pDoc2;
DWORD dwOptions = 0;
ASSERT(ppMyPics && *ppMyPics==NULL);
if (!MP_IsEnabledInIEAK() ||
!MP_IsEnabledInRegistry() ||
!IsInternetExplorerApp())
{
return S_OK;
}
if (!pdoc2 || !ppMyPics || (*ppMyPics != NULL))
{
return S_OK;
}
//Is this a desktop component?
if (SUCCEEDED(GetTopFrameOptions(_pspOuter, &dwOptions)))
{
if (dwOptions & FRAMEOPTIONS_DESKTOP)
{
return S_OK;
}
}
CMyPics *pPics = new CMyPics();
if (pPics)
{
if (SUCCEEDED(pPics->Init(pdoc2)))
{
*ppMyPics = pPics;
}
else
{
pPics->Release();
}
}
return S_OK;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::ReleaseMyPics
//
// Interface : ITridentService2
//
//--------------------------------------------------------------------------
BOOL
CBaseBrowser2::ReleaseMyPics(void *pMyPics)
{
CMyPics *pPics = (CMyPics *) pMyPics;
BOOL bRet = pPics->IsOff();
if (pPics)
{
pPics->UnInit();
pPics->Release();
}
return (bRet);
}
STDMETHODIMP CBaseBrowser2::SetFrameName(BSTR bstrFrameName)
{
return StringCchCopy(_szViewLinkedWebOCFrameName, ARRAYSIZE(_szViewLinkedWebOCFrameName), bstrFrameName);
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::AppStarting
//
// Interface : INotifyAppStart
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::AppStarting(void)
{
_dwStartingAppTick = GetTickCount();
SetCursor(LoadCursor(NULL, IDC_APPSTARTING));
SetTimer( _bbd._hwnd, IDT_STARTING_APP_TIMER, STARTING_APP_DURATION, NULL);
return S_OK;
}
//+-------------------------------------------------------------------------
//
// Method : CBaseBrowser2::AppStarted
//
// Interface : INotifyAppStart
//
//--------------------------------------------------------------------------
HRESULT
CBaseBrowser2::AppStarted(void)
{
_dwStartingAppTick = 0;
KillTimer( _bbd._hwnd, IDT_STARTING_APP_TIMER );
SetCursor(LoadCursor(NULL, IDC_ARROW));
return S_OK;
}