Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

13569 lines
415 KiB

//
// NOTES:
//
// This is the code which enables the explorer hosting (being a container)
// a DocObject (a super set of OLE in-place object). In a nut shell, this
// code creates an object (class CDocObjectHost) which can be plugged into
// the explorer's right pane (by supporting IShellView) is also a DocObject
// container (by supporting IOleClientSite, IOleInPlaceSite, ...).
//
// This CDocObjectHost directly supports following interfaces:
//
// Group 1 (to be plugged in):
// IShellView, IDropTarget
// Group 2 (to be a Doc site):
// IOleClientSite, IOleDocumentSite
// Group 3 (to be a View Site)
// IOleInPlaceSite
// It also supports following interfaces indirectly via contained object,
// CDocObjectFrame.
//
// IOleInPlaceFrame, IOleCommandTarget
//
// The reason we export them separately is because we may need to return
// a different hwnd for GetWindow method. The CDocObjectHost object always
// returns hwnd of the view window, but the CDocObjectFrame returns hwnd
// of the explorer in case the explorer support IOleInPlaceUIWindow.
//
// It also supports following interface indirectly via contained object,
// CProxyActiveObject.
//
// IOleInPlaceActiveObject
//
//
// --------------------------------------------------------
// Explorer (browser)
// --------------------------------------------------------
// ^ | |
// | | |
// ISB (+IOIUI) ISV IOIAO
// | | |
// | V |
// ----------------------------V---------------------------
// CDocObjectHost CProxyActiveObject CDocObjectFrame
// ----------------------------------------------^---------
// ^ | |
// | | |
// IOCS/IOIPS/IMDS IO/IOIPO/IMV/IMCT IOIUI/IOIF/IMCT
// | | |
// | V |
// ---------------------------------------------------------
// DocObject (Doc + View)
// ---------------------------------------------------------
//
#include "priv.h"
#include "iehelpid.h"
#include "bindcb.h"
#include "winlist.h"
#include "droptgt.h"
#include <mshtml.h> // CLSID_HTMLDocument
#include <mshtmcid.h>
#include "resource.h"
#include <htmlhelp.h>
#include <prsht.h>
#include <inetcpl.h>
#include <optary.h>
#include "impexp.h"
#include "impexpwz.h"
#include "thicket.h"
#include "uemapp.h"
#include "iextag.h" // web folders
#include "browsext.h"
#include "interned.h" // IHTMLPrivateWindow
#include "dochost.h"
#include <mluisupp.h>
#include <varutil.h>
#include "msiehost.h"
#include "shdocfl.h"
#include "mediautil.h"
#include "mediahlpr.h"
#include "privacyui.hpp"
#include "brdispp.h"
#include <strsafe.h>
// temp, going away once itbar edit stuff moves here
#define CITIDM_EDITPAGE 10
// Command group for private communication with CITBar
// 67077B95-4F9D-11D0-B884-00AA00B60104
const GUID CGID_PrivCITCommands = { 0x67077B95L, 0x4F9D, 0x11D0, 0xB8, 0x84,
0x00, 0xAA, 0x00, 0xB6, 0x01, 0x04 };
// end temp itbar stuff
// Command group for accessing Bind Context Param set by Trident (Media Bar hook)
// (copied from mshtml\src\other\include\othrguid.h)
static const GUID CGID_DownloadObjectBindContext = { 0x3050f3df, 0x98b5, 0x11cf, 0xbb, 0x82,
0x00, 0xaa, 0x00, 0xbd, 0xce, 0x0b };
static const GUID CLSID_JITWMP8 = { 0x6BF52A52, 0x394A, 0x11d3, 0xb1, 0x53, 0x00, 0xc0, 0x4f, 0x79, 0xfa, 0xa6 };
#define DBG_ACCELENTRIES 2
#define OPT_ACCELENTRIES 1
#ifdef UNIX
#include <unixstuff.h>
#define EXPLORER_EXE "explorer"
#define IEXPLORE_EXE "iexplorer"
#define DBG_ACCELENTRIES_WITH_FILEMENU 8
#define OPT_ACCELENTRIES_WITH_FILEMENU 7
#else
#define EXPLORER_EXE TEXT("explorer.exe")
#define IEXPLORE_EXE TEXT("iexplore.exe")
#define DBG_ACCELENTRIES_WITH_FILEMENU 6
#define OPT_ACCELENTRIES_WITH_FILEMENU 5
#endif /* UNIX */
EXTERN_C const GUID IID_IDocHostObject = {0x67431840L, 0xC511, 0x11CF, 0x89, 0xA9, 0x00, 0xA0, 0xC9, 0x05, 0x41, 0x29};
EXTERN_C const GUID IID_IMimeInfo = {0xF77459A0L, 0xBF9A, 0x11cf, 0xBA, 0x4E, 0x00, 0xC0, 0x4F, 0xD7, 0x08, 0x16};
EXTERN_C const GUID IID_IsPicsBrowser = {0xF114C2C0L, 0x90BE, 0x11D0, 0x83, 0xB1, 0x00, 0xC0, 0x4F, 0xD7, 0x05, 0xB2};
#include <shlwapi.h>
#include <ratingsp.h>
#include <ratings.h>
#define DM_ZONECROSSING 0
#define DM_SAVEASHACK 0
#define DM_MIMEMAPPING 0
#define DM_SELFASC TF_SHDBINDING
#define DM_ACCEPTHEADER 0
#define DM_DEBUGTFRAME 0
#define DM_DOCHOSTUIHANDLER 0
#define DM_PREMERGEDMENU 0
#define DM_FOCUS 0
#define DM_DOCCP 0
#define DM_PICS 0
#define DM_SSL 0
#define KEY_BINDCONTEXTPARAM _T("BIND_CONTEXT_PARAM")
// media bar entries
#define WZ_RADIO_PROTOCOL L"vnd.ms.radio:"
enum PlayMimeOptions {PLAYMIME_YES, PLAYMIME_NO, PLAYMIME_YESSAVE, PLAYMIME_NOSAVE};
// WARNING: Never define it in shipping product.
#ifdef DEBUG
// #define TEST_DELAYED_SHOWMSOVIEW
#endif
void CShdAdviseSink_Advise(IBrowserService * pwb, IOleObject* pole);
UINT MayOpenSafeOpenDialog(HWND hwndOwner,
LPCTSTR pszFileClass,
LPCTSTR pszURL,
LPCTSTR pszCacheName,
LPCTSTR pszDisplay,
UINT uiCP,
IUnknown * punk,
IOleCommandTarget * pCmdTarget = NULL,
BOOL fDisableOK = FALSE);
LONG _GetSearchFormatString(DWORD dwIndex, LPTSTR psz, DWORD cbpsz);
DWORD _GetErrorThreshold(DWORD dwError);
BOOL IsRegisteredClient(LPCTSTR pszClient);
HRESULT _GetSearchString(IServiceProvider * psp, VARIANT * pvarSearch);
int EUIndexFromError(DWORD dwError);
BOOL IsErrorHandled(DWORD dwError);
HRESULT _GetSearchInfo(IServiceProvider * psp,
LPDWORD pdwIndex,
LPBOOL pfAllowSearch,
LPBOOL pfContinueSearch,
LPBOOL pfSentToEngine,
VARIANT * pvarUrl);
STDAPI _URLMONMonikerFromPidl(LPCITEMIDLIST pidl, IMoniker ** ppmk, BOOL * pfFileProtocol);
LRESULT CALLBACK PolicyDlgProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
// macros
#define DO_SEARCH_ON_STATUSCODE(x) ((x == 0) || (x == HTTP_STATUS_BAD_GATEWAY) || (x == HTTP_STATUS_GATEWAY_TIMEOUT))
// Suite Apps Registry keys
#define NEW_MAIL_DEF_KEY TEXT("Mail")
#define NEW_NEWS_DEF_KEY TEXT("News")
#define NEW_CONTACTS_DEF_KEY TEXT("Contacts")
#define NEW_CALL_DEF_KEY TEXT("Internet Call")
#define NEW_APPOINTMENT_DEF_KEY TEXT("Appointment")
#define NEW_MEETING_DEF_KEY TEXT("Meeting")
#define NEW_TASK_DEF_KEY TEXT("Task")
#define NEW_TASKREQUEST_DEF_KEY TEXT("Task Request")
#define NEW_JOURNAL_DEF_KEY TEXT("Journal")
#define NEW_NOTE_DEF_KEY TEXT("Note")
#ifdef DEBUG
DWORD g_dwPerf = 0;
#endif
// #include "..\shell32\fstreex.h" // for IDFOLDER
// HACK:
struct IDFOLDERA
{
WORD cb;
BYTE bFlags;
};
typedef IDFOLDERA* LPIDFOLDERA;
const ITEMIDLIST s_idNull = { {0} };
//
// Icons are globally shared among multiple threads.
//
HICON g_hiconSSL = NULL;
HICON g_hiconFortezza = NULL;
HICON g_hiconOffline = NULL;
HICON g_hiconPrinter = NULL;
HICON g_hiconScriptErr = NULL;
HICON g_hiconPrivacyImpact = NULL;
HICON g_ahiconState[IDI_STATE_LAST-IDI_STATE_FIRST+1] = { NULL };
#define MAX_MIXED_STR_LEN 32
// OpenUIURL is just a wrapper for OpenUI, calling CreateURLMoniker() if the
// caller only has an URL.
extern BOOL __cdecl _FormatMessage(LPCSTR szTemplate, LPSTR szBuf, UINT cchBuf, ...);
#include "asyncrat.h"
#define MAX_STATUS_SIZE 128
//
// Set this flag if we are going to use IHlinkBrowseContext in HLINK.DLL
// #define HLINK_EXTRA
//
// Array of http error codes and file names.
//
extern ErrorUrls c_aErrorUrls[];
#define DM_RECYCLE DM_TRACE
#define DM_BINDAPPHACK TF_SHDAPPHACK
#define DM_ADVISE TF_SHDLIFE
#define DM_APPHACK DM_WARNING
#define NAVMSG3(psz, x, y) TraceMsg(0, "shdv NAV::%s %x %x", psz, x, y)
#define PAINTMSG(psz,x) TraceMsg(0, "shd TR-PAINT::%s %x", psz, x)
#define JMPMSG(psz, psz2) TraceMsg(0, "shd TR-CDOV::%s %s", psz, psz2)
#define JMPMSG2(psz, x) TraceMsg(0, "shd TR-CDOV::%s %x", psz, x)
#define DOFMSG(psz) TraceMsg(0, "shd TR-DOF::%s", psz)
#define DOFMSG2(psz, x) TraceMsg(0, "shd TR-DOF::%s %x", psz, x)
#define URLMSG(psz) TraceMsg(TF_SHDBINDING, "shd TR-DOF::%s", psz)
#define URLMSG2(psz, x) TraceMsg(TF_SHDBINDING, "shd TR-DOF::%s %x", psz, x)
#define URLMSG3(psz, x, y) TraceMsg(TF_SHDBINDING, "shd TR-DOF::%s %x %x", psz, x, y)
#define OIPSMSG(psz) TraceMsg(0, "shd TR-OIPS::%s", psz)
#define OIPSMSG3(psz, sz, p) TraceMsg(0, "shd TR-OIPS::%s %s,%x", psz, sz,p)
#define VIEWMSG(psz) TraceMsg(0, "sdv TR CDOV::%s", psz)
#define VIEWMSG2(psz,xx) TraceMsg(0, "sdv TR CDOV::%s %x", psz,xx)
#define OPENMSG(psz) TraceMsg(TF_SHDBINDING, "shd OPENING %s", psz)
#define OPENMSG2(psz, x) TraceMsg(TF_SHDBINDING, "shd OPENING %s %x", psz, x)
#define HFRMMSG(psz) TraceMsg(0, "shd HFRM::%s", psz)
#define HFRMMSG2(psz, x, y) TraceMsg(0, "shd HFRM::%s %x %x", psz, x, y)
#define MNKMSG(psz, psz2) TraceMsg(0, "shd MNK::%s (%s)", psz, psz2)
#define CHAINMSG(psz, x) TraceMsg(0, "shd CHAIN::%s %x", psz, x)
#define SHVMSG(psz, x, y) TraceMsg(0, "shd SHV::%s %x %x", psz, x, y)
#define HOMEMSG(psz, psz2, x) TraceMsg(TF_SHDNAVIGATE, "shd HOME::%s %s %x", psz, psz2, x)
#define SAVEMSG(psz, x) TraceMsg(0, "shd SAVE::%s %x", psz, x)
#define PERFMSG(psz, x) TraceMsg(TF_SHDPERF, "PERF::%s %d msec", psz, x)
static const TCHAR szRegKey_SMIEM[] = TEXT("Software\\Microsoft\\Internet Explorer\\Main");
static const TCHAR szRegVal_ErrDlgPerErr[] = TEXT("Error Dlg Displayed On Every Error");
static const TCHAR szRegVal_ErrDlgDetailsOpen[] = TEXT("Error Dlg Details Pane Open");
////////////////////////////////////////////////////////////
// ShabbirS (980917) - BugFix# 34259
// Repair menuitem in the Help Menu.
typedef HRESULT (* FIXIEPROC) (BOOL, DWORD);
void RepairIE()
{
HINSTANCE hIESetup;
FIXIEPROC fpFixIE;
hIESetup = LoadLibrary(L"IESetup.dll");
if (hIESetup)
{
fpFixIE = (FIXIEPROC) GetProcAddress(hIESetup,"FixIE");
if (fpFixIE)
{
fpFixIE(TRUE,0);
}
FreeLibrary(hIESetup);
}
}
BOOL _IsDesktopItem(CDocObjectHost * pdoh)
{
BOOL fIsDesktopItem = FALSE;
IServiceProvider * psb;
ASSERT(pdoh);
//Check if we are a desktop component.
if (SUCCEEDED(pdoh->QueryService(SID_STopLevelBrowser, IID_IServiceProvider, (void **)&psb)))
{
LPTARGETFRAME2 ptgf;
if (SUCCEEDED(psb->QueryService(IID_ITargetFrame2, IID_ITargetFrame2, (void **)&ptgf)))
{
DWORD dwOptions;
if (SUCCEEDED(ptgf->GetFrameOptions(&dwOptions)))
{
//Is this a desktop component?
if (IsFlagSet(dwOptions, FRAMEOPTIONS_DESKTOP))
fIsDesktopItem = TRUE;
}
ptgf->Release();
}
psb->Release();
}
return fIsDesktopItem;
}
BOOL _IsImmediateParentDesktop(CDocObjectHost *pdoh, IServiceProvider *psp)
{
BOOL fImmediateParentIsDesktop = FALSE;
LPTARGETFRAME2 ptgf;
//First check if this is hosted on desktop.
if (!_IsDesktopItem(pdoh))
return FALSE; //This is not a desktop item. So, the immediate parent can't be desktop!
//We know that this is a desktop item. Check if the immediate parent is desktop
// or it is hosted too deep on desktop!
if (psp && SUCCEEDED(psp->QueryService(IID_ITargetFrame2, IID_ITargetFrame2, (void **)&ptgf)))
{
IUnknown *pUnkParent;
//Get it's immediate parent.
if(SUCCEEDED(ptgf->GetParentFrame(&pUnkParent)))
{
if(pUnkParent)
{
//Has a parent. So, the immediate parent can't be desktop!
pUnkParent->Release();
fImmediateParentIsDesktop = FALSE;
}
else
fImmediateParentIsDesktop = TRUE; //No parent. Must be a desktop comp.
}
ptgf->Release();
}
return(fImmediateParentIsDesktop);
}
// Gets the current display name in wide char
//
// If fURL is TRUE, it returns file-URL with file: prefix.
//
HRESULT CDocObjectHost::_GetCurrentPageW(LPOLESTR * ppszDisplayName, BOOL fURL)
{
HRESULT hres = E_FAIL;
ASSERT(_pmkCur);
*ppszDisplayName = NULL;
if (_pmkCur) {
IBindCtx* pbc;
hres = CreateBindCtx(0, &pbc);
if (SUCCEEDED(hres))
{
hres = _pmkCur->GetDisplayName(pbc, NULL, ppszDisplayName);
//
// special handling just for file: urls.
//
if (SUCCEEDED(hres) && _fFileProtocol)
{
ASSERT(*ppszDisplayName);
WCHAR szText[MAX_URL_STRING];
DWORD cchText = SIZECHARS(szText);
if (!fURL)
{
hres = PathCreateFromUrlW(*ppszDisplayName, szText, &cchText, 0);
}
else
{
// we need this to be in the normalized form of the URL
// for internal usage. urlmon keeps them in the funny PATHURL style
hres = UrlCanonicalizeW(*ppszDisplayName, szText, &cchText, 0);
}
if (SUCCEEDED(hres))
{
UINT cchDisplayName = lstrlenW(*ppszDisplayName);
if (cchText > cchDisplayName)
{
// need to resize
CoTaskMemFree(*ppszDisplayName);
*ppszDisplayName = (WCHAR *)CoTaskMemAlloc((cchText + 1) * SIZEOF(WCHAR));
if (*ppszDisplayName)
{
// go ahead and copy it in
StrCpyNW(*ppszDisplayName, szText, cchText + 1);
}
else
hres = E_OUTOFMEMORY;
}
else
{
StrCpyNW(*ppszDisplayName, szText, cchDisplayName + 1);
}
}
else
OleFree(*ppszDisplayName);
}
pbc->Release();
}
}
return hres;
}
//
// We fire NavigateError in one location in DocObjectHost
// However it is fired from (n) locations in DocHostBsc
// so I have extracted the common code
//
void
CDocObjectHost::_FireNavigateErrorHelper(IHTMLWindow2 * pHTMLWindow2,
DWORD dwStatusCode,
BOOL * pfCancelAutoSearch,
BSTR bstrURL /* = NULL */)
{
ASSERT(!pHTMLWindow2 || !IsBadReadPtr(pHTMLWindow2, sizeof(IHTMLWindow2*)));
ASSERT(dwStatusCode != 0);
ASSERT(!IsBadWritePtr(pfCancelAutoSearch, sizeof(BOOL)));
ASSERT(!bstrURL || !IsBadReadPtr(bstrURL, sizeof(BSTR)));
if ( ! _pwb )
{
TraceMsg( TF_ERROR, "CDocObjectHost::_FireNavigateErrorHelper() - _pwb is NULL!" );
return;
}
ITridentService2 * pTridentSvc2;
HRESULT hres = _pwb->QueryInterface(IID_ITridentService2, (void**)&pTridentSvc2);
if (SUCCEEDED(hres))
{
LPOLESTR pwzURL = NULL;
BSTR bstrMkURL = NULL;
BSTR bstrPidlURL = NULL;
BSTR bstrFrameName = NULL;
if (!bstrURL)
{
ASSERT(!_pmkCur || !IsBadReadPtr(_pmkCur, sizeof(_pmkCur)));
ASSERT(!_pbcCur || !IsBadReadPtr(_pbcCur, sizeof(_pbcCur)));
if (_pmkCur && _pbcCur)
{
EVAL(S_OK == _pmkCur->GetDisplayName(_pbcCur, NULL, &pwzURL));
bstrMkURL = SysAllocString(pwzURL);
}
else if (_pidl)
{
TCHAR szUrl[MAX_URL_STRING];
HRESULT hresGDN = IEGetDisplayName(_pidl, szUrl, SHGDN_FORPARSING);
if (SUCCEEDED(hresGDN))
{
bstrPidlURL = SysAllocString(szUrl);
}
else
{
bstrPidlURL = SysAllocString(_T(""));
}
}
}
if (pHTMLWindow2)
pHTMLWindow2->get_name(&bstrFrameName);
pTridentSvc2->FireNavigateError(pHTMLWindow2,
bstrURL ? bstrURL : bstrMkURL ? bstrMkURL : bstrPidlURL,
bstrFrameName,
dwStatusCode,
pfCancelAutoSearch);
pTridentSvc2->Release();
OleFree(pwzURL);
SysFreeString(bstrPidlURL);
SysFreeString(bstrMkURL);
SysFreeString(bstrFrameName);
}
}
HRESULT CDocObjectHost::_GetCurrentPage(LPTSTR szBuf, UINT cchMax, BOOL fURL)
{
szBuf[0] = 0; // zero out buffer
WCHAR * pszDisplayName;
HRESULT hres = _GetCurrentPageW(&pszDisplayName, fURL);
if (SUCCEEDED(hres))
{
StrCpyN(szBuf, pszDisplayName, cchMax);
OleFree(pszDisplayName);
}
return hres;
}
void CDocObjectHost_GetCurrentPage(LPARAM that, LPTSTR szBuf, UINT cchMax)
{
CDocObjectHost* pdoh = (CDocObjectHost*)that;
pdoh->_GetCurrentPage(szBuf, cchMax);
}
//========================================================================
// CDocObjectHost members
//========================================================================
CDocObjectHost::CDocObjectHost(BOOL fWindowOpen /* = FALSE */) : _cRef(1), _uState(SVUIA_DEACTIVATE)
{
DllAddRef();
TraceMsg(TF_SHDLIFE, "ctor CDocObjectHost %x", this);
TraceMsg(DM_DEBUGTFRAME, "ctor CDocObjectHost %x, %x", this, &_bsc);
// Initialize proxy objects (which are contained)
_dof.Initialize(this);
_xao.Initialize(this);
#ifdef HLINK_EXTRA
HRESULT hres = HlinkCreateBrowseContext(NULL, IID_IHlinkBrowseContext, (void **)&_pihlbc);
TraceMsg(0, "sdv TR CDOV::constructor HlinkCreateBrowseContext returned %x", hres);
#endif // HLINK_EXTRA
::_RefPicsQueries(); /* we'll free PICS async query list when last dochost is destroyed */
_dwPicsKeyBase = 1;
_pScriptErrList = NULL;
_fScriptErrDlgOpen = FALSE;
_strPriorityStatusText = NULL;
_iString = -1;
_uiCP = CP_ACP;
_PicsProcBase._pdoh = this;
VariantInit(&_varUserEnteredUrl);
_fWindowOpen = fWindowOpen;
ASSERT(!_fDocCanNavigate);
ASSERT(!_fPrevDocHost);
}
CDocObjectHost::~CDocObjectHost()
{
ASSERT(_pole==NULL); // to catch extra release.
ASSERT(_psp==NULL); // to cache extra release.
ASSERT(_hwnd==NULL);
ASSERT(_pmsoc==NULL);
ASSERT(_pmsot==NULL);
ASSERT(_pmsov==NULL);
ASSERT(_pcmdMergedMenu==NULL);
ASSERT(NULL == _pHTMLWindow);
ASSERT(VT_EMPTY == V_VT(&_varUserEnteredUrl));
if (_pScriptErrList != NULL)
{
_pScriptErrList->Release();
}
if (_strPriorityStatusText != NULL)
{
SysFreeString(_strPriorityStatusText);
}
#ifdef HLINK_EXTRA
ASSERT(_phls == NULL);
ATOMICRELEASE(_pihlbc);
#endif // HLINK_EXTRA
::_ReleasePicsQueries();
if ( _PicsProcBase._pRootDownload
|| _PicsProcBase._pPicsProcNext) {
ASSERT(0); /* need to destroy this earlier to prevent Trident problems */
_RemoveAllPicsProcessors();
}
if (_padvise) {
_padvise->OnClose();
ATOMICRELEASE(_padvise);
}
if (_pwszRefreshUrl)
OleFree(_pwszRefreshUrl);
if (_hmenuBrowser) {
AssertMsg(0, TEXT("_hmenuBrowser should be NULL!"));
DestroyMenu(_hmenuBrowser);
}
if (_hmenuFrame) {
DestroyMenu(_hmenuFrame);
}
if (_hacc)
{
DestroyAcceleratorTable(_hacc);
_hacc = NULL;
}
if (_hinstInetCpl)
FreeLibrary(_hinstInetCpl);
if (_ptbStd)
delete [] _ptbStd;
if (_pBrowsExt)
{
_pBrowsExt->Release();
}
ILFree(_pidl);
_pidl = NULL;
// Make it sure that View Window is released (and _psb)
DestroyHostWindow(); // which will call _CloseMsoView and _UnBind
_ResetOwners();
TraceMsg(TF_SHDLIFE, "dtor CDocObjectHost %x", this);
DllRelease();
}
#ifdef DEBUG
/*----------------------------------------------------------
Purpose: Dump the menu handles for this docobj. Optionally
breaks after dumping handles.
Returns:
Cond: --
*/
void
CDocObjectHost::_DumpMenus(
IN LPCTSTR pszMsg,
IN BOOL bBreak)
{
if (IsFlagSet(g_dwDumpFlags, DF_DEBUGMENU))
{
ASSERT(pszMsg);
TraceMsg(TF_ALWAYS, "DocHost: Dumping menus for %#08x %s", (LPVOID)this, pszMsg);
TraceMsg(TF_ALWAYS, " _hmenuBrowser = %x, _hmenuSet = %x, _hmenuFrame = %x",
_hmenuBrowser, _hmenuSet, _hmenuFrame);
TraceMsg(TF_ALWAYS, " _hmenuCur = %x, _hmenuMergedHelp = %x, _hmenuObjHelp = %x",
_hmenuCur, _hmenuMergedHelp, _hmenuObjHelp);
_menulist.Dump(pszMsg);
if (bBreak && IsFlagSet(g_dwBreakFlags, BF_ONDUMPMENU))
DebugBreak();
}
}
#endif
HRESULT CDocObjectHost::QueryInterface(REFIID riid, void ** ppvObj)
{
static const QITAB qit[] = {
QITABENTMULTI(CDocObjectHost, IOleInPlaceSite, IOleInPlaceSiteEx),
QITABENTMULTI(CDocObjectHost, IOleWindow, IOleInPlaceSiteEx),
QITABENT(CDocObjectHost, IOleClientSite),
QITABENT(CDocObjectHost, IOleDocumentSite),
QITABENT(CDocObjectHost, IOleCommandTarget),
QITABENT(CDocObjectHost, IServiceProvider),
QITABENT(CDocObjectHost, IViewObject),
QITABENT(CDocObjectHost, IAdviseSink),
QITABENT(CDocObjectHost, IDocHostObject),
QITABENT(CDocObjectHost, IDocHostUIHandler),
QITABENT(CDocObjectHost, IDocHostUIHandler2),
QITABENT(CDocObjectHost, IDocHostShowUI),
QITABENT(CDocObjectHost, IDispatch),
QITABENT(CDocObjectHost, IPropertyNotifySink),
QITABENT(CDocObjectHost, IOleControlSite),
{ 0 },
};
static const QITAB qit1[] = {
QITABENT(CDocObjectHost, IOleInPlaceSiteEx),
{ 0 },
};
HRESULT hr = QISearch(this, qit, riid, ppvObj);
if (hr == S_OK)
return S_OK;
if (_pWebOCInPlaceSiteEx)
return QISearch(this, qit1, riid, ppvObj);
return hr;
}
void CDocObjectHost::_ResetOwners()
{
_pszLocation = NULL;
_uiCP = CP_ACP;
_ReleasePendingObject();
ATOMICRELEASE(_psv);
ATOMICRELEASE(_pmsoctView);
ATOMICRELEASE(_pdvs);
ATOMICRELEASE(_psb);
ATOMICRELEASE(_pwb);
ATOMICRELEASE(_phf);
ATOMICRELEASE(_pocthf);
ATOMICRELEASE(_punkSFHistory);
ATOMICRELEASE(_pmsoctBrowser);
ATOMICRELEASE(_psp);
ATOMICRELEASE(_peds);
ATOMICRELEASE(_pedsHelper);
ATOMICRELEASE(_pWebOCUIHandler);
ATOMICRELEASE(_pWebOCUIHandler2);
ATOMICRELEASE(_pWebOCShowUI);
ATOMICRELEASE(_pWebOCInPlaceSiteEx);
// Release cached OleInPlaceUIWindow of the browser
ATOMICRELEASE(_pipu);
// Tell embedded CDocHostUIHandler object to release its references on us.
_dhUIHandler.SetSite(NULL);
}
ULONG CDocObjectHost::AddRef()
{
_cRef++;
TraceMsg(TF_SHDREF, "CDocObjectHost(%x)::AddRef called, new _cRef=%d", this, _cRef);
return _cRef;
}
ULONG CDocObjectHost::Release()
{
_cRef--;
TraceMsg(TF_SHDREF, "CDocObjectHost(%x)::Release called, new _cRef=%d", this, _cRef);
if (_cRef > 0)
return _cRef;
delete this;
return 0;
}
// cut & paste from browseui\itbar.cpp
int RemoveHiddenButtons(TBBUTTON* ptbn, int iCount)
{
int i;
int iTotal = 0;
TBBUTTON* ptbn1 = ptbn;
for (i = 0; i < iCount; i++, ptbn1++) {
if (!(ptbn1->fsState & TBSTATE_HIDDEN)) {
if (ptbn1 != ptbn) {
*ptbn = *ptbn1;
}
ptbn++;
iTotal++;
}
}
return iTotal;
}
// We use two different image lists in the TBBUTTON array. The bitmaps for browser-specific buttons
// cut/copy/paste have been moved to shdocvw, and are therefore obtained from a second image list.
// MAKELONG(0,1) accesses the first image from this second list. Without a call to MAKELONG there is
// a 0 in the upper integer, thereby referencing the first list by default.
static const TBBUTTON c_tbStd[] = {
{10, DVIDM_SHOWTOOLS, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, 10},
{13, DVIDM_MAILNEWS, TBSTATE_ENABLED, BTNS_WHOLEDROPDOWN, {0,0}, 0, 13 },
{ 8, DVIDM_FONTS, TBSTATE_ENABLED, BTNS_WHOLEDROPDOWN, {0,0}, 0, 8 },
{ 7, DVIDM_PRINT, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, 7 },
{ 9, DVIDM_EDITPAGE, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, 9 },
{15, DVIDM_DISCUSSIONS, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, 15 },
{MAKELONG(0,1), DVIDM_CUT, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, 0 },
{MAKELONG(1,1), DVIDM_COPY, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, 0 },
{MAKELONG(2,1), DVIDM_PASTE, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, 0 },
{MAKELONG(3,1), DVIDM_ENCODING, TBSTATE_ENABLED, BTNS_WHOLEDROPDOWN, {0,0}, 0, 0 },
{MAKELONG(5,1), DVIDM_PRINTPREVIEW, TBSTATE_ENABLED, BTNS_BUTTON, {0,0}, 0, 0 },
};
// c_tbStd and c_rest need to match exactly
static const BROWSER_RESTRICTIONS c_rest[] = {
REST_BTN_TOOLS,
REST_BTN_MAIL,
REST_BTN_FONTS,
REST_BTN_PRINT,
REST_BTN_EDIT,
REST_BTN_DISCUSSIONS,
REST_BTN_CUT,
REST_BTN_COPY,
REST_BTN_PASTE,
REST_BTN_ENCODING,
REST_BTN_PRINTPREVIEW,
};
#ifdef DEBUG
void _AssertRestrictionOrderIsCorrect()
{
COMPILETIME_ASSERT(ARRAYSIZE(c_tbStd) == ARRAYSIZE(c_rest));
for (UINT i = 0; i < ARRAYSIZE(c_tbStd); i++)
{
// If any of these rip, it means that c_rest and c_tbStd have
// gotten out of sync. Need to fix up c_rest to match c_tbStd.
switch (c_tbStd[i].idCommand)
{
case DVIDM_SHOWTOOLS: ASSERT(c_rest[i] == REST_BTN_TOOLS); break;
case DVIDM_MAILNEWS: ASSERT(c_rest[i] == REST_BTN_MAIL); break;
case DVIDM_FONTS: ASSERT(c_rest[i] == REST_BTN_FONTS); break;
case DVIDM_PRINT: ASSERT(c_rest[i] == REST_BTN_PRINT); break;
case DVIDM_EDITPAGE: ASSERT(c_rest[i] == REST_BTN_EDIT); break;
case DVIDM_DISCUSSIONS: ASSERT(c_rest[i] == REST_BTN_DISCUSSIONS); break;
case DVIDM_CUT: ASSERT(c_rest[i] == REST_BTN_CUT); break;
case DVIDM_COPY: ASSERT(c_rest[i] == REST_BTN_COPY); break;
case DVIDM_PASTE: ASSERT(c_rest[i] == REST_BTN_PASTE); break;
case DVIDM_ENCODING: ASSERT(c_rest[i] == REST_BTN_ENCODING); break;
case DVIDM_PRINTPREVIEW: ASSERT(c_rest[i] == REST_BTN_PRINTPREVIEW); break;
default: ASSERT(0); break;
}
}
}
#endif
BYTE _BtnStateFromRestIfAvailable(BOOL fAvailable, DWORD dwRest)
{
if (fAvailable)
return SHBtnStateFromRestriction(dwRest, TBSTATE_ENABLED);
return TBSTATE_HIDDEN;
}
BOOL CDocObjectHost::_ToolsButtonAvailable()
{
OLECMD rgcmd = { OLECMDID_HIDETOOLBARS, 0 };
if (_pmsoctBrowser)
_pmsoctBrowser->QueryStatus(NULL, 1, &rgcmd, NULL);
return (rgcmd.cmdf & OLECMDF_SUPPORTED);
}
__inline BYTE CDocObjectHost::_DefToolsButtonState(DWORD dwRest)
{
BOOL fAvailable = _ToolsButtonAvailable();
return _BtnStateFromRestIfAvailable(fAvailable, dwRest);
}
static const TCHAR c_szRegKeyCoolbar[] = TEXT("Software\\Microsoft\\Internet Explorer\\Toolbar");
BYTE CDocObjectHost::_DefFontsButtonState(DWORD dwRest)
{
BYTE fsState = TBSTATE_ENABLED;
// default to whatever the IE4 reg key specifies,
// or FALSE if reg key not present (clean install)
if (!SHRegGetBoolUSValue(c_szRegKeyCoolbar, TEXT("ShowFonts"), FALSE, FALSE))
fsState |= TBSTATE_HIDDEN;
return SHBtnStateFromRestriction(dwRest, fsState);
}
DWORD CDocObjectHost::_DiscussionsButtonCmdf()
{
if (SHRegGetBoolUSValue(c_szRegKeyCoolbar,
TEXT("ShowDiscussionButton"), FALSE, TRUE) &&
_pmsoctBrowser) {
OLECMD rgcmds[] = {
{ SBCMDID_DISCUSSIONBAND, 0 },
};
static const int buttonsInternal[] = {
DVIDM_DISCUSSIONS,
};
_pmsoctBrowser->QueryStatus(&CGID_Explorer, ARRAYSIZE(rgcmds), rgcmds, NULL);
return rgcmds[0].cmdf;
}
return 0;
}
__inline BOOL CDocObjectHost::_DiscussionsButtonAvailable()
{
return (_DiscussionsButtonCmdf() & OLECMDF_SUPPORTED);
}
__inline BYTE CDocObjectHost::_DefDiscussionsButtonState(DWORD dwRest)
{
BOOL fAvailable = _DiscussionsButtonAvailable();
return _BtnStateFromRestIfAvailable(fAvailable, dwRest);
}
BOOL CDocObjectHost::_MailButtonAvailable()
{
OLECMD rgcmdMailFavs[] = { { SBCMDID_DOMAILMENU, 0} };
if (_pmsoctBrowser)
_pmsoctBrowser->QueryStatus(&CGID_Explorer, ARRAYSIZE(rgcmdMailFavs), rgcmdMailFavs, NULL);
if (rgcmdMailFavs[0].cmdf & OLECMDF_ENABLED)
return TRUE;
return FALSE;
}
__inline BYTE CDocObjectHost::_DefMailButtonState(DWORD dwRest)
{
BOOL fAvailable = _MailButtonAvailable();
return _BtnStateFromRestIfAvailable(fAvailable, dwRest);
}
// We default the edit button to visible if there is an html editer registered
BOOL CDocObjectHost::_EditButtonAvailable()
{
DWORD cchVerb;
return (SUCCEEDED(AssocQueryString(0, ASSOCSTR_COMMAND, TEXT(".htm"), TEXT("edit"), NULL, &cchVerb)) ||
SUCCEEDED(AssocQueryString(0, ASSOCSTR_COMMAND, TEXT("htmlfile"), TEXT("edit"), NULL, &cchVerb)));
}
__inline BYTE CDocObjectHost::_DefEditButtonState(DWORD dwRest)
{
BYTE fsState;
if (_EditButtonAvailable())
fsState = TBSTATE_ENABLED;
else
fsState = TBSTATE_HIDDEN;
return SHBtnStateFromRestriction(dwRest, fsState);
}
void CDocObjectHost::_MarkDefaultButtons(PTBBUTTON tbStd)
{
// We're assuming tbStd is the same size as c_tbStd
#ifdef DEBUG
_AssertRestrictionOrderIsCorrect();
#endif
DWORD dwRest[ARRAYSIZE(c_tbStd)];
BOOL fCheckRestriction = SHRestricted2(REST_SPECIFYDEFAULTBUTTONS, NULL, 0);
for (UINT i = 0; i < ARRAYSIZE(c_rest); i++) {
if (fCheckRestriction)
dwRest[i] = SHRestricted2(c_rest[i], NULL, 0);
else
dwRest[i] = RESTOPT_BTN_STATE_DEFAULT;
}
// We want the Cut, Copy, Paste buttons to default off of the toolbar
// (but available in the view-toolbars-customize dialog)
// We set the state of the buttons to TBSTATE_HIDDEN here, but leave them alone
// in ETCMDID_GETBUTTONS so that they appear in the customize dialog.
ASSERT(tbStd[6].idCommand == DVIDM_CUT);
ASSERT(tbStd[7].idCommand == DVIDM_COPY);
ASSERT(tbStd[8].idCommand == DVIDM_PASTE);
ASSERT(tbStd[9].idCommand == DVIDM_ENCODING);
ASSERT(tbStd[10].idCommand == DVIDM_PRINTPREVIEW);
for (i = 6; i <= 10; i++)
tbStd[i].fsState = SHBtnStateFromRestriction(dwRest[i], tbStd[i].fsState | TBSTATE_HIDDEN);
ASSERT(tbStd[0].idCommand == DVIDM_SHOWTOOLS);
tbStd[0].fsState = _DefToolsButtonState(dwRest[0]);
ASSERT(tbStd[1].idCommand == DVIDM_MAILNEWS);
tbStd[1].fsState = _DefMailButtonState(dwRest[1]);
ASSERT(tbStd[2].idCommand == DVIDM_FONTS);
tbStd[2].fsState = _DefFontsButtonState(dwRest[2]);
ASSERT(tbStd[3].idCommand == DVIDM_PRINT);
tbStd[3].fsState = SHBtnStateFromRestriction(dwRest[3], TBSTATE_ENABLED);
ASSERT(tbStd[4].idCommand == DVIDM_EDITPAGE);
tbStd[4].fsState = _DefEditButtonState(dwRest[4]);
ASSERT(tbStd[5].idCommand == DVIDM_DISCUSSIONS);
tbStd[5].fsState = _DefDiscussionsButtonState(dwRest[5]);
}
const GUID* CDocObjectHost::_GetButtonCommandGroup()
{
if (_ToolsButtonAvailable())
return &CLSID_MSOButtons;
else
return &CLSID_InternetButtons;
}
void CDocObjectHost::_AddButtons(BOOL fForceReload)
{
if (!_pBrowsExt)
return;
IExplorerToolbar* pxtb;
if (_psp && SUCCEEDED(_psp->QueryService(SID_SExplorerToolbar, IID_IExplorerToolbar, (void **)&pxtb)))
{
const GUID* pguid = _GetButtonCommandGroup();
HRESULT hr = pxtb->SetCommandTarget((IOleCommandTarget*)this, pguid, 0);
if (!fForceReload && hr == S_FALSE) {
// Another dochost already merged the buttons into the toolbar under the
// same command group, so don't bother re-merging. We just need to initialize
// _iString, since we're skipping the call to _pBrowsExt->InitButtons below.
VARIANT var = { VT_I4 };
IUnknown_Exec(_pBrowsExt, &CLSID_PrivBrowsExtCommands, PBEC_GETSTRINGINDEX, 0, &var, NULL); // should always succeed
_iString = var.lVal;
} else {
UINT nNumExtButtons = 0;
_pBrowsExt->GetNumButtons(&nNumExtButtons);
int nNumButtons = nNumExtButtons + ARRAYSIZE(c_tbStd);
// GetTBArray insures that tbStd != NULL, so we don't need that check here
TBBUTTON *tbStd = new TBBUTTON[nNumButtons];
if (tbStd != NULL)
{
memcpy(tbStd, c_tbStd, SIZEOF(TBBUTTON) * ARRAYSIZE(c_tbStd));
UINT iStringIndex = (UINT)-1; // result of adding the string buffer to the toolbar string list
HRESULT hr = _pBrowsExt->InitButtons(pxtb, &iStringIndex, pguid);
ASSERT(tbStd[6].idCommand == DVIDM_CUT);
ASSERT(tbStd[7].idCommand == DVIDM_COPY);
ASSERT(tbStd[8].idCommand == DVIDM_PASTE);
ASSERT(tbStd[9].idCommand == DVIDM_ENCODING);
ASSERT(tbStd[10].idCommand == DVIDM_PRINTPREVIEW);
if (SUCCEEDED(hr) && iStringIndex != -1)
{
tbStd[6].iString = iStringIndex;
tbStd[7].iString = iStringIndex + 1;
tbStd[8].iString = iStringIndex + 2;
tbStd[9].iString = iStringIndex + 3;
tbStd[10].iString = iStringIndex+ 4;
_iString = (int)iStringIndex;
}
else
{
tbStd[6].iString = tbStd[7].iString = tbStd[8].iString = tbStd[9].iString = tbStd[10].iString = -1;
_iString = -1;
}
// Add custom buttons to the toolbar array. We pass in the nNumButtons
// as a *sanity check*...
_pBrowsExt->GetButtons(&tbStd[ARRAYSIZE(c_tbStd)], nNumExtButtons, TRUE);
_MarkDefaultButtons(tbStd);
nNumButtons = RemoveHiddenButtons(tbStd, nNumButtons);
pxtb->AddButtons(pguid, nNumButtons, tbStd);
delete [] tbStd;
}
}
pxtb->Release();
}
}
HRESULT CDocObjectHost::UIActivate(UINT uState, BOOL fPrevViewIsDocView)
{
TraceMsg(TF_SHDUIACTIVATE, "DOH::UIActivate called %d->%d (this=%x)",
_uState, uState, this);
HRESULT hres = S_OK;
UINT uStatePrev = _uState;
// We are supposed to update the menu
if (uState != _uState)
{
// There was a state transition.
//
_uState = uState;
// If the new state is SVUIA_DEACTIVATE
//
if (_uState == SVUIA_DEACTIVATE)
{
//
// When we are deactivating (we are navigating away)
// we UIDeactivate the current MsoView.
//
_UIDeactivateMsoView();
_IPDeactivateMsoView(_pmsov);
_DestroyBrowserMenu();
}
else if (_uState == SVUIA_INPLACEACTIVATE && uStatePrev == SVUIA_ACTIVATE_FOCUS)
{
// Transition from SVUIA_ACTIVATE_FOCUS->SVUIA_INPLACEACTIVATE
//
// If we set this DONT_UIDEACTIVATE, then we stop calling
// UIActivate(FALSE) when a DocObject in a frameset looses a focus.
// It will solve some problems with Office apps (Excel, PPT), which
// InPlaceDeactivate when we call UIActivate(FALSE). We want to treat
// it as a bug, but unfortunately DocObject spec says that's OK.
//
// Putting this work around, however, slightly confuses MSHTML
// (both classic and Trident). Once it's UIActivated, it keep
// thinking that it's UIActivated and never calls onUIActivate.
// Until we figure out what's the right implementation,
// we can't turn this on. (SatoNa -- 11/04/96).
//
_GetAppHack(); // get if we don't have it yet.
if (_dwAppHack & BROWSERFLAG_DONTUIDEACTIVATE) {
//
// HACK: We are supposed to just call UIActivate(FALSE) when
// another DocObject (in the case of a frame set) became
// UIActivated. Excel/PPT, however, InplaceDeactivate instead.
// To work around, SriniK suggested us to call
// OnDocWindowActivate(FALSE). (SatoNa)
//
IOleInPlaceActiveObject* piact = _xao.GetObject(); // no AddRef
TraceMsg(TF_SHDAPPHACK, "DOH::UIActivate APPHACK calling %x->OnDocWindowActivate (this=%x)",
piact, this);
if (piact)
{
piact->OnDocWindowActivate(FALSE);
}
}
else if (!(_dwAppHack & BROWSERFLAG_DONTDEACTIVATEMSOVIEW))
{
// HACK: In Excel, if we deactiveate the view, it never gets focus again
// fix for the bug: #20906
// Also, we don't want to deactivate the view
// if the document is currently navigating.
//
_UIDeactivateMsoView();
}
else
{
// We're transitioning from SVUIA_ACTIVATE_FOCUS->SVUIA_INPLACEACTIVATE
// and BROWSERFLAG_DONTDEACTIVATEMSOVIEW is set.
// call the object's IOleInPlactActiveObject::OnFrameWindowActivate(FALSE);
IOleInPlaceActiveObject* piact = _xao.GetObject(); // no AddRef
if (piact)
{
piact->OnFrameWindowActivate(FALSE);
}
}
}
else if (uStatePrev == SVUIA_DEACTIVATE)
{
//
// If UIActivate is called either
// (1) when the binding is pending; _bsc._pbc!=NULL
// (2) when the async binding is done; _bsc._pole!=NULL
//
SHVMSG("UIActivate about to call _Bind", _bsc._pbc, NULL);
if (_pole == NULL && _bsc._pbc)
{
ASSERT(_pmkCur);
IBindCtx* pbc = _bsc._pbc;
pbc->AddRef();
HRESULT hresT = _BindSync(_pmkCur, _bsc._pbc, _bsc._psvPrev);
pbc->Release();
ASSERT(_bsc._pbc==NULL);
ASSERT(_bsc._psvPrev==NULL);
ASSERT(_bsc._pbscChained==NULL);
}
hres = _EnsureActivateMsoView();
// We are being asked to UI activate and we are currently deactivated. Show
// the view now.
if (SUCCEEDED(hres) && DocCanHandleNavigation())
{
_ShowMsoView();
}
_AddButtons(FALSE);
}
else
{
// opening a new document for 1st time (to UIActive or IPActive)
goto GoSetFocus;
}
}
else
{
TraceMsg(TF_SHDUIACTIVATE, "DOH:::UIActivate -- same uState (%x)", _uState);
GoSetFocus:
if ((_uState == SVUIA_ACTIVATE_FOCUS)) {
// see if object is already UIActive.
if (_ActiveHwnd()) {
// if it is, we have an hwnd and all we need to do
// is SetFocus (for compatibility w/ weird guys...)
if ( IsChildOrSelf( _ActiveHwnd(), GetFocus() ) != S_OK )
{
TraceMsg(TF_SHDUIACTIVATE, "DOH:::UIActivate -- calling SetFocus(%x)", _ActiveHwnd());
SetFocus(_ActiveHwnd());
}
}
else {
// we're in the OC, and it's IPActive not UIActive.
// (either that or it's the very 1st time for the main view).
// NOTE: Due to CBaseBrowser code that defers SVUIA_ACTIVATE_FOCUS until
// application is active, we can have a top level docobject go
// SVUIA_INPLACEACTIVE and then on activation of the window,
// we transition to SVUIA_ACTIVATE_FOCUS, thus never UIActivating
// the docobject (cf: BUG 62138)
hres = _DoVerbHelper(FALSE);
}
}
}
if ((_uState == SVUIA_INPLACEACTIVATE) || (_uState == SVUIA_ACTIVATE_FOCUS))
_PlaceProgressBar();
return hres;
}
//*** _DoVerbHelper -- DoVerb w/ various hacks
// NOTES
// do comments in _OnSetFocus apply here?
HRESULT CDocObjectHost::_DoVerbHelper(BOOL fOC)
{
HRESULT hres = E_FAIL;
LONG iVerb = OLEIVERB_SHOW;
MSG msg;
LPMSG pmsg = NULL;
if (_pole)
{
if (_uState == SVUIA_INPLACEACTIVATE)
{
iVerb = OLEIVERB_INPLACEACTIVATE;
}
else if ((_uState == SVUIA_ACTIVATE_FOCUS))
{
iVerb = OLEIVERB_UIACTIVATE;
}
else
{
TraceMsg(TF_ERROR, "DOC::_DoVerbHelper untested (and probably the wrong iVerb mapping)");
}
if (_pedsHelper)
{
if (SUCCEEDED(_pedsHelper->GetDoVerbMSG(&msg)))
{
pmsg = &msg;
}
}
hres = _pole->DoVerb(iVerb, pmsg, this, (UINT)-1, _hwnd, &_rcView);
if (hres == OLEOBJ_E_INVALIDVERB && iVerb == OLEIVERB_INPLACEACTIVATE)
{
hres = _pole->DoVerb(OLEIVERB_SHOW, pmsg, this, (UINT)-1, _hwnd, &_rcView);
}
if (FAILED(hres))
{
TraceMsg(DM_ERROR, "DOC::_DoVerbHelper _pole->DoVerb ##FAILED## %x", hres);
}
}
return hres;
}
void CDocObjectHost::_IPDeactivateMsoView(IOleDocumentView* pmsov)
{
IInitViewLinkedWebOC* pInitViewLinkedWebOC;
BOOL fIsViewLinked = FALSE;
TraceMsg(TF_SHDUIACTIVATE, "DOH::_IPDeactivateMsoView called (this==%x)", this);
if (pmsov)
{
// Because of NativeFrames, in the viewlinked WebOC case, the ReleaseShellView does not
// Deactivate the DocumentView. The sequence of these events is critical to Application
// Compatibility. So we force the show before the InPlaceDeactivate if we have a viewlinked WebOC.
ASSERT(_pwb);
HRESULT hres = E_FAIL;
if ( _pwb )
{
hres = _pwb->QueryInterface(IID_IInitViewLinkedWebOC, (void**)&pInitViewLinkedWebOC);
}
if (SUCCEEDED(hres))
{
hres = pInitViewLinkedWebOC->IsViewLinkedWebOC(&fIsViewLinked);
if (SUCCEEDED(hres) && fIsViewLinked)
{
pmsov->Show(FALSE);
}
pInitViewLinkedWebOC->Release();
}
IOleInPlaceObject* pipo = NULL;
HRESULT hresT = _pole->QueryInterface(IID_IOleInPlaceObject, (void **)&pipo);
if (SUCCEEDED(hresT))
{
pipo->InPlaceDeactivate();
pipo->Release();
}
if (!fIsViewLinked)
pmsov->Show(FALSE);
}
}
void CDocObjectHost::_UIDeactivateMsoView(void)
{
TraceMsg(TF_SHDUIACTIVATE, "DOH::_UIDeactivateMsoView called (this==%x)", this);
if (_pmsov)
{
_pmsov->UIActivate(FALSE);
}
}
//
// Hide the office toolbar
//
void CDocObjectHost::_HideOfficeToolbars(void)
{
if (_pmsot) {
OLECMD rgcmd = { OLECMDID_HIDETOOLBARS, 0 };
_pmsot->QueryStatus(NULL, 1, &rgcmd, NULL);
// LATCHED means hidden
rgcmd.cmdf &= (OLECMDF_SUPPORTED | OLECMDF_LATCHED);
// If it's supported and visible (not LATCHED), toggle it.
if (rgcmd.cmdf == OLECMDF_SUPPORTED) {
_pmsot->Exec(NULL, OLECMDID_HIDETOOLBARS, OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL);
}
}
}
void CDocObjectHost::_ShowMsoView(void)
{
HRESULT hres;
//
// HACK: Word97 UIDeactivate when we call SetInPlaceSite even with the
// same in-place site.
//
IOleInPlaceSite* psite;
hres = _pmsov->GetInPlaceSite(&psite);
if (SUCCEEDED(hres) && psite) {
if (psite!=this) {
_pmsov->SetInPlaceSite(this);
} else {
TraceMsg(TF_SHDAPPHACK, "DOH::_ShowMsoView not calling SetInPlaceSite because it's already set");
}
psite->Release();
} else {
_pmsov->SetInPlaceSite(this);
}
GetClientRect(_hwnd, &_rcView);
if ((_uState != SVUIA_INPLACEACTIVATE)
|| !(_dwAppHack & BROWSERFLAG_MSHTML) // or if it's not Trident (office apps expect this call)
)
{
// Trident is sending progress changed messages here -- and causing Compuserve a problem.
// Flag the fact that we're UIActivating them, and suppress forwarding ProgressChanged
// messages to our container when this flag is true. (IE v4.1 bug 54787)
//
_fUIActivatingView = TRUE;
_pmsov->UIActivate(TRUE);
_fUIActivatingView = FALSE;
}
//
// HACK:
//
// We call _HideOfficeToolbars when our OnUIActivate is called.
// SriniK suggested us to do it in order to avoid flashing.
// It works well with Excel (3404), but does not work with Word.
// Word does not hide its toolbars correctly. To work around that
// bug, we call _HideofficeToolbars here again.
//
_HideOfficeToolbars();
hres = _pmsov->SetRect(&_rcView);
if (FAILED(hres)) {
TraceMsg(DM_ERROR, "DOC::_ShowMsoView _pmsov->SetRect ##FAILED## %x", hres);
}
if (FAILED(hres) && _uState == SVUIA_INPLACEACTIVATE) {
TraceMsg(TF_SHDAPPHACK, "APPHACK# DOH::_ShowMsoView calling UIActivate");
// HACKHACK: for word. they refuse to show if they aren't UIActivated.
// if the setrect fails, and we didn't do a UIActivate, do it now.
_fDontInplaceActivate = TRUE;
TraceMsg(TF_SHDAPPHACK, "HACK: CDOH::_ShowMsoView calling UIActive(TRUE) to work around Word bug");
_pmsov->UIActivate(TRUE);
_pmsov->SetRect(&_rcView);
}
// This is the other case where Trident sends Progress changed messages.
//
_fUIActivatingView = TRUE;
hres = _pmsov->Show(TRUE);
_fUIActivatingView = FALSE;
if (FAILED(hres)) {
TraceMsg(DM_ERROR, "DOH::_ShowMsoView _pmsov->Show ##FAILED## %x", hres);
}
_fDrawBackground = FALSE; /* now that we've shown the object, no need to paint our own background */
}
HRESULT CDocObjectHost::_ActivateMsoView()
{
_EnableModeless(FALSE);
#ifdef DEBUG
PERFMSG(TEXT("_ActivateMsoView"), GetCurrentTime() - g_dwPerf);
g_dwPerf = GetCurrentTime();
#endif
HRESULT hres = NOERROR;
if (!DocCanHandleNavigation() || _fWindowOpen)
{
if (!_phls)
{
_pole->QueryInterface(IID_IHlinkSource, (void **)&_phls);
}
if (_phls && !_fIsHistoricalObject)
{
//
// Special test case for IHlinkFrame marshaling.
//
hres = _phls->Navigate(0, _pszLocation);
//
// If this is one of our internal error pages, we can ignore the
// failure on the bogus location. In this case pwszLocation will
// be the original url that failed preceeded with a '#'.
//
LPOLESTR pwszUrl;
if (FAILED(hres) && SUCCEEDED(_GetCurrentPageW(&pwszUrl, TRUE)))
{
// if it begins with res: it may be our erro page
if (pwszUrl[0] == L'r' && pwszUrl[1] == L'e' && IsErrorUrl(pwszUrl))
{
// It's our internal error page, so ignore the error
hres = S_OK;
}
OleFree(pwszUrl);
}
#ifdef DEBUG
if (!_fWindowOpen && FAILED(hres))
{
TraceMsg(DM_ERROR, "DOC::_ActivateMsoView _phls->Navigate(%s) ##FAILED## %x",
_pszLocation ? _pszLocation : TEXT(""), hres);
}
#endif
}
else
{
// todo: use _DoVerbHelper? (but careful! ACT_FOCUS different)
LONG iVerb = OLEIVERB_SHOW;
MSG msg;
LPMSG pmsg = NULL;
if (_uState == SVUIA_INPLACEACTIVATE) {
iVerb = OLEIVERB_INPLACEACTIVATE;
}
if (_pedsHelper)
{
if (SUCCEEDED(_pedsHelper->GetDoVerbMSG(&msg)))
{
pmsg = &msg;
}
}
hres = _pole->DoVerb(iVerb, pmsg, this, (UINT)-1, _hwnd, &_rcView);
if (hres == OLEOBJ_E_INVALIDVERB && iVerb == OLEIVERB_INPLACEACTIVATE)
hres = _pole->DoVerb(OLEIVERB_SHOW, pmsg, this, (UINT)-1, _hwnd, &_rcView);
if (FAILED(hres)) {
TraceMsg(DM_ERROR, "DOC::_ActivateMsoView _pole->DoVerb ##FAILED## %x", hres);
}
}
}
_fPrevDocHost = TRUE;
// the doc is activated
if (SUCCEEDED(hres))
{
_ReleasePendingObject();
if (_fHaveParentSite)
{
_HideOfficeToolbars();
}
}
_EnableModeless(TRUE);
return hres;
}
void CDocObjectHost::OnInitialUpdate()
{
_fWindowOpen = FALSE;
ResetRefreshUrl();
}
void CDocObjectHost::ResetRefreshUrl()
{
if (_pwszRefreshUrl)
{
OleFree(_pwszRefreshUrl);
_pwszRefreshUrl = NULL;
}
}
//+---------------------------------------------------------------------------
//
// Member : CDocObjectHost::_NavigateDocument
//
// Synopsis : Navigates the document to the given URL
//
//+---------------------------------------------------------------------------
HRESULT
CDocObjectHost::_NavigateDocument(BSTR bstrUrl, BSTR bstrLocation)
{
HRESULT hres = E_FAIL;
DWORD dwFlags = 0;
IHTMLPrivateWindow * pPrivWindow = NULL;
ASSERT(_fDocCanNavigate && (_fPrevDocHost || _uState == SVUIA_DEACTIVATE));
ASSERT(_pHTMLWindow);
// Waiting for the real navigate call so we don't get duplicate events
if (_pwb)
{
_pwb->SetNavigateState(BNS_NORMAL);
}
if (_pHTMLWindow) // Stress fix
{
hres = _pHTMLWindow->QueryInterface(IID_IHTMLPrivateWindow, (void**)&pPrivWindow);
if (S_OK == hres)
{
CComBSTR cbstrShortCut;
IBindStatusCallback * pBindStatusCallback;
LPTSTR pszHeaders = NULL;
LPBYTE pPostData = NULL;
DWORD cbPostData = 0;
STGMEDIUM stgPostData;
BOOL fHavePostStg = FALSE;
BSTR bstrHeaders = NULL;
VARIANT vaHeaders = {0};
SAFEARRAY * psaPostData = NULL;
VARIANT vaPostData = {0};
if (SUCCEEDED(IUnknown_QueryService(_phf, SID_SHlinkFrame, IID_PPV_ARG(IBindStatusCallback, &pBindStatusCallback))))
{
GetHeadersAndPostData(pBindStatusCallback, &pszHeaders, &stgPostData, &cbPostData, NULL);
pBindStatusCallback->Release();
fHavePostStg = TRUE;
if (stgPostData.tymed == TYMED_HGLOBAL)
{
pPostData = (LPBYTE) stgPostData.hGlobal;
if (pPostData && cbPostData)
{
// make a SAFEARRAY for post data
psaPostData = MakeSafeArrayFromData(pPostData, cbPostData);
// put the post data SAFEARRAY into a variant so we can pass through automation
if (psaPostData)
{
V_VT(&vaPostData) = VT_ARRAY | VT_UI1;
V_ARRAY(&vaPostData) = psaPostData;
}
}
}
if (pszHeaders && pszHeaders[0])
{
bstrHeaders = SysAllocStringT(pszHeaders);
V_VT(&vaHeaders) = VT_BSTR;
V_BSTR(&vaHeaders) = bstrHeaders;
}
}
_GetShortCutPath(&cbstrShortCut);
_GetDocNavFlags(&dwFlags);
// Do the navigate
//
hres = pPrivWindow->SuperNavigate(bstrUrl,
bstrLocation,
cbstrShortCut,
NULL,
&vaPostData,
&vaHeaders,
dwFlags);
pPrivWindow->Release();
if (fHavePostStg)
{
ReleaseStgMedium(&stgPostData);
}
if (bstrHeaders)
{
SysFreeString(bstrHeaders);
}
if (V_ARRAY(&vaPostData))
{
ASSERT(V_VT(&vaPostData) == (VT_ARRAY | VT_UI1));
VariantClearLazy(&vaPostData);
}
_fRefresh = FALSE; // clear the refresh flag.
}
}
return hres;
}
//+---------------------------------------------------------------------------
//
// Member : CDocObjectHost::_GetShortCutPath
//
// Synopsis : Returns the shortcut path.
//
//+---------------------------------------------------------------------------
void
CDocObjectHost::_GetShortCutPath(BSTR * pbstrShortCutPath)
{
HRESULT hres;
VARIANT varShortCutPath = {0};
IOleCommandTarget * pcmdt;
ASSERT(pbstrShortCutPath);
*pbstrShortCutPath = NULL;
hres = QueryService(SID_SHlinkFrame, IID_IOleCommandTarget, (void **)&pcmdt);
if (S_OK == hres)
{
hres = pcmdt->Exec(&CGID_Explorer, SBCMDID_GETSHORTCUTPATH, 0, NULL, &varShortCutPath);
if (S_OK == hres && VT_BSTR == V_VT(&varShortCutPath) && V_BSTR(&varShortCutPath))
{
*pbstrShortCutPath = SysAllocString(V_BSTR(&varShortCutPath));
}
else if (_pocthf)
{
VariantClear(&varShortCutPath);
// if we couldn't find it on that service, try the cached HLink interface
// this is necessary for ND & Mars which provide a different implementation for the HLink interface
hres = _pocthf->Exec(&CGID_Explorer, SBCMDID_GETSHORTCUTPATH, 0, NULL, &varShortCutPath);
if (S_OK == hres && VT_BSTR == V_VT(&varShortCutPath) && V_BSTR(&varShortCutPath))
{
*pbstrShortCutPath = SysAllocString(V_BSTR(&varShortCutPath));
}
}
pcmdt->Release();
}
VariantClear(&varShortCutPath);
}
//+---------------------------------------------------------------------------
//
// Member : CDocObjectHost::_GetDocNavFlags
//
// Synopsis : Returns the flags for navigation.
//
//+---------------------------------------------------------------------------
void
CDocObjectHost::_GetDocNavFlags(DWORD * pdwDocNavFlags)
{
// Find out if we are in the middle of an autosearch.
// If we are, set the necessary document flags.
//
HRESULT hr;
DWORD dwSuffixIdx = -1;
BOOL fAllowSearch = FALSE;
BOOL fContinueSearch = FALSE;
DWORD dwNavFlags = 0;
ASSERT(pdwDocNavFlags);
hr = _GetSearchInfo(_psp, &dwSuffixIdx, &fAllowSearch,
&fContinueSearch, NULL, NULL);
if (SUCCEEDED(hr) && dwSuffixIdx > 1 && fContinueSearch)
{
// We aren't really in an autosearch, but are in
// a redirection from an autosearch (i.e., there is
// a long standing bug where the MSN autosearch page
// is placed in the travel log. When you press back,
// the search is restarted. However, if the search
// info is not reset here, the suffix index is incorrect.
//
if (!fAllowSearch)
{
_bsc._SetSearchInfo(this, 0, fAllowSearch, fContinueSearch, FALSE);
}
}
// Check to see if the navigation should be suppressed from the history
//
if ( _pwb && (S_OK == _pwb->GetFlags(&dwNavFlags))
&& (dwNavFlags & BSF_NAVNOHISTORY))
{
*pdwDocNavFlags |= DOCNAVFLAG_DONTUPDATETLOG;
}
if ( _fRefresh )
{
*pdwDocNavFlags |= DOCNAVFLAG_REFRESH;
}
}
HRESULT CDocObjectHost::_EnsureActivateMsoView()
{
HRESULT hres = E_FAIL;
// if we've got an ole object and
// either we don't have a view, or we don't have an active view..
// do the activation
if (_pole)
{
if (!_pmsov || !_ActiveObject()) {
hres = _ActivateMsoView();
// Note that we should not UIActivate it here. We should wait
// until the DocObject calls our ActivateMe
// _ShowMsoView();
}
}
return hres;
}
//
// This member closes the MsoView window and releases interface
// pointers. This is essentially the reverse of _CreateMsoView.
//
void CDocObjectHost::_CloseMsoView(void)
{
ATOMICRELEASE(_pmsot);
if (_pmsov)
{
VIEWMSG(TEXT("_CloseMsoView calling pmsov->UIActivate(FALSE)"));
IOleDocumentView* pmsov = _pmsov;
_pmsov = NULL;
_fDontInplaceActivate = FALSE;
#ifdef DONT_UIDEACTIVATE
if (_uState != SVUIA_DEACTIVATE)
pmsov->UIActivate(FALSE);
#else // DONT_UIDEACTIVATE
if (_uState == SVUIA_ACTIVATE_FOCUS)
pmsov->UIActivate(FALSE);
#endif // DONT_UIDEACTIVATE
_IPDeactivateMsoView(pmsov);
pmsov->CloseView(0);
pmsov->SetInPlaceSite(NULL);
pmsov->Release();
VIEWMSG(TEXT("_CloseMsoView called pmsov->Release()"));
}
ATOMICRELEASE(_pmsoc);
}
void CDocObjectHost::_OnPaint(HDC hdc)
{
if (_pmsov && !_ActiveObject())
{
HRESULT hres;
RECT rcClient;
GetClientRect(_hwnd, &rcClient);
hres = OleDraw(_pmsov, DVASPECT_CONTENT, hdc, &rcClient);
TraceMsg(0, "shd TR ::_OnPaint OleDraw returns %x", hres);
}
}
HRESULT _GetDefaultLocation(LPWSTR pszPath, DWORD cchPathSizeIn, UINT id)
{
WCHAR szPath[MAX_URL_STRING];
DWORD cbSize = SIZEOF(szPath);
DWORD cchPathSize = cchPathSizeIn;
HRESULT hres = E_FAIL;
HKEY hkey;
// Share this code!!!
// This is Internet Explorer Specific
HKEY hkeyroot = id == IDP_CHANNELGUIDE ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE;
if (RegOpenKeyW(hkeyroot,
L"Software\\Microsoft\\Internet Explorer\\Main",
&hkey)==ERROR_SUCCESS)
{
DWORD dwType;
LPCWSTR pszName;
switch(id) {
default:
ASSERT(0);
case DVIDM_GOHOME:
pszName = L"Default_Page_URL";
break;
case DVIDM_GOSEARCH:
pszName = L"Default_Search_URL";
break;
case IDP_UPDATE:
pszName = L"Default_Update_URL";
break;
case IDP_CHANNELGUIDE:
pszName = L"ChannelsURL";
break;
}
if (RegQueryValueExW(hkey, pszName,
0, &dwType, (LPBYTE)szPath, &cbSize)==ERROR_SUCCESS)
{
// When reading a URL from registry, treat it like it was typed
// in on the address bar.
hres = S_OK;
if(!ParseURLFromOutsideSourceW(szPath, pszPath, &cchPathSize, NULL))
StrCpyNW(pszPath, szPath, cchPathSizeIn);
if(IsFileUrlW(pszPath))
{
cchPathSize = cchPathSizeIn;
hres = PathCreateFromUrlW(pszPath, pszPath, &cchPathSize, 0);
}
}
RegCloseKey(hkey);
}
HOMEMSG("_GetStdLocation returning",
(SUCCEEDED(hres) ? pszPath : TEXT("Error")), hres);
return hres;
}
HRESULT _GetStdLocation(LPTSTR pszPath, DWORD cchPathSize, UINT id)
{
TCHAR szPathTemp[MAX_URL_STRING];
DWORD cchTempSize = ARRAYSIZE(szPathTemp);
HRESULT hres = E_FAIL;
LPCWSTR pszName = NULL;
ASSERT(cchPathSize >= cchTempSize); // If we hit this, we will truncate the URL in some cases.
ASSERT(pszPath && (cchPathSize > 0)); // Not Optional
pszPath[0] = TEXT('\0');
// Share this code!!!
// This is Internet Explorer Specific
switch(id) {
default:
ASSERT(0);
case DVIDM_GOHOME:
pszName = L"Start Page";
break;
case DVIDM_GOFIRSTHOME:
case DVIDM_GOFIRSTHOMERO:
pszName = L"First Home Page";
break;
case DVIDM_GOSEARCH:
pszName = L"Search Page";
break;
case DVIDM_SEARCHBAR:
pszName = L"Search Bar";
break;
case DVIDM_GOLOCALPAGE:
pszName = L"Local Page";
break;
}
hres = URLSubRegQuery(szRegKey_SMIEM, pszName, TRUE,
szPathTemp, cchTempSize, URLSUB_ALL);
if (FAILED(hres) &&
((DVIDM_GOFIRSTHOME == id) || (DVIDM_GOFIRSTHOMERO == id)))
{
// The First Home key doesn't exist so use the home key.
pszName = TEXT("Start Page");
hres = URLSubRegQuery(szRegKey_SMIEM, pszName, TRUE,
szPathTemp, cchTempSize, URLSUB_ALL);
id = DVIDM_GOHOME;
}
if (SUCCEEDED(hres))
{
// When reading a URL from registry, treat it like it was typed
// in on the address bar.
// Recursion block. If requesting the home page, and the home page is "about:home", substitute
// "about:blank" (otherwise we'll recurse to death)
if (DVIDM_GOHOME == id && !StrCmpI(szPathTemp, TEXT("about:home")) )
{
StrCpyN(szPathTemp, TEXT("about:blank"), MAX_URL_STRING);
}
if(ParseURLFromOutsideSourceW(szPathTemp, pszPath, &cchPathSize, NULL))
{
if(IsFileUrlW(pszPath))
hres = PathCreateFromUrlW(pszPath, pszPath, &cchPathSize, 0);
}
}
if (DVIDM_GOFIRSTHOME == id) // Delete that FIRSTHOME key
{
HUSKEY hUSKey;
if (ERROR_SUCCESS == SHRegOpenUSKey(szRegKey_SMIEM, KEY_WRITE, NULL, &hUSKey, FALSE))
{
SHRegDeleteUSValue(hUSKey, TEXT("First Home Page"), SHREGDEL_DEFAULT);
SHRegCloseUSKey(hUSKey);
}
hres = S_OK;
}
HOMEMSG("_GetStdLocation returning",
(SUCCEEDED(hres) ? pszPath : TEXT("Error")), hres);
return hres;
}
HRESULT WINAPI _GetDefaultLocation(UINT idp, LPWSTR pszPath, UINT cchMax)
{
switch (idp)
{
case IDP_UPDATE:
case IDP_CHANNELGUIDE:
URLSubLoadString(NULL, IDS_DEF_UPDATE+(idp-IDP_UPDATE), pszPath, cchMax, URLSUB_ALL);
break;
default:
_GetDefaultLocation(pszPath, cchMax, (idp == IDP_SEARCH) ? DVIDM_GOSEARCH : DVIDM_GOHOME);
break;
}
return S_OK;
}
HRESULT WINAPI SHDGetPageLocation(HWND hwndOwner, UINT idp, LPWSTR pszPath, UINT cchMax, LPITEMIDLIST *ppidlOut)
{
TCHAR szBuf[MAX_URL_STRING];
if (pszPath==NULL) {
pszPath = szBuf;
cchMax = ARRAYSIZE(szBuf);
}
*pszPath = L'\0';
*ppidlOut = NULL;
HRESULT hres = S_OK;
switch (idp) {
case IDP_UPDATE:
case IDP_CHANNELGUIDE:
ASSERT(IDP_CHANNELGUIDE-IDP_UPDATE == IDS_DEF_CHANNELGUIDE-IDS_DEF_UPDATE);
if (FAILED(hres = _GetDefaultLocation(pszPath, cchMax, idp)))
{
hres = _GetDefaultLocation(idp, pszPath, cchMax);
}
break;
default:
ASSERT(idp==IDP_START || idp==IDP_SEARCH);
hres = _GetStdLocation(pszPath, cchMax,
(idp == IDP_SEARCH) ? DVIDM_GOSEARCH : DVIDM_GOHOME);
if (FAILED(hres))
{
hres = _GetDefaultLocation(idp, pszPath, cchMax);
}
break;
}
if (SUCCEEDED(hres))
{
hres = IECreateFromPath(pszPath, ppidlOut);
if (FAILED(hres))
{
// IECreateFromPath() above could have failed if the default location
// was invalid. (Like file://server_no_exist/
_GetDefaultLocation(idp, pszPath, cchMax);
hres = IECreateFromPath(pszPath, ppidlOut);
}
HOMEMSG("SHDGetPageLocation SHILCreateFromPage returned", pszPath, hres);
}
return hres;
}
void CDocObjectHost::_ChainBSC()
{
if (!_bsc._pbscChained && _phf)
{
// Get "chaigned" bind status, if any
IServiceProvider * psp = NULL;
HRESULT hres = _phf->QueryInterface(IID_IServiceProvider, (void **)&psp);
CHAINMSG("_StartAsyncBinding hlf->QI returns", hres);
if (SUCCEEDED(hres))
{
ASSERT(NULL == _bsc._pbscChained);
hres = psp->QueryService(SID_SHlinkFrame, IID_IBindStatusCallback, (void **)&_bsc._pbscChained);
CHAINMSG("_StartAsyncBinding psp(hlf)->QS returns", hres);
psp->Release();
if (SUCCEEDED(hres))
{
ASSERT(NULL==_bsc._pnegotiateChained);
_bsc._pbscChained->QueryInterface(IID_IHttpNegotiate, (void **)&_bsc._pnegotiateChained);
}
}
}
}
//
// WARNING: Following two global variables are shared among multiple-threads
// in a thread. Therefore, all right-access must be serialized and all read
// access should be blocked when right is going on.
//
// Right now, we just initialize them once (based on the registry setting)
// and never update. It allows us to simplify the code quite a bit. If we
// need to update, then _RegisterMediaTypeClass should be changed significantlly
// so that we can safely handle multiple access to those hdsa's. (SatoNa)
//
HDSA g_hdsaCls = NULL;
HDSA g_hdsaStr = NULL;
BOOL CDocObjectHost::_BuildClassMapping(void)
{
if (g_hdsaCls)
{
return DSA_GetItemCount(g_hdsaCls) == DSA_GetItemCount(g_hdsaStr);
}
ENTERCRITICAL;
if (!g_hdsaCls) {
g_hdsaStr = DSA_Create(SIZEOF(LPCSTR), 32);
if (g_hdsaStr)
{
HDSA hdsaCls = DSA_Create(SIZEOF(CLSID), 32);
if (hdsaCls)
{
HKEY hkey;
if (RegOpenKey(HKEY_LOCAL_MACHINE,
TEXT("Software\\Microsoft\\Internet Explorer\\MediaTypeClass"),
&hkey) == ERROR_SUCCESS)
{
TCHAR szCLSID[64]; // enough for "{CLSID}"
for (int iKey=0;
RegEnumKey(hkey, iKey, szCLSID, SIZEOF(szCLSID)/sizeof(szCLSID[0]))==ERROR_SUCCESS;
iKey++)
{
CLSID clsid;
if (FAILED(CLSIDFromString(szCLSID, &clsid))) {
TraceMsg(DM_WARNING, "CDOH::_RMTC CLSIDFromString(%x) failed", szCLSID);
continue;
}
TraceMsg(DM_MIMEMAPPING, "CDOH::_RMTC RegEnumKey found %s", szCLSID);
HKEY hkeyCLSID;
if (RegOpenKey(hkey, szCLSID, &hkeyCLSID) == ERROR_SUCCESS)
{
for (int iValue=0; ; iValue++)
{
CHAR szFormatName[128];
DWORD dwType;
DWORD cchValueName = ARRAYSIZE(szFormatName);
//
// Keep the name ansi because it needs to get
// passed to urlmon's RegisterMediaTypeClass as
// ansi.
//
if (RegEnumValueA(hkeyCLSID, iValue, szFormatName, &cchValueName, NULL,
&dwType, NULL, NULL)==ERROR_SUCCESS)
{
TraceMsg(DM_MIMEMAPPING, "CDOH::_RMTC RegEnumValue found %s", szFormatName);
LPSTR psz = StrDupA(szFormatName);
if (psz) {
DSA_InsertItem(hdsaCls, 0xffff, &clsid);
if (DSA_InsertItem(g_hdsaStr, 0xffff, &psz)<0) {
LocalFree(psz);
psz = NULL;
break;
}
}
} else {
break;
}
}
RegCloseKey(hkeyCLSID);
} else {
TraceMsg(DM_WARNING, "CDOH::_RMTC RegOpenKey(%s) failed", szCLSID);
}
}
RegCloseKey(hkey);
} else {
TraceMsg(0, "CDOH::_RMTC RegOpenKey(MediaTypeClass) failed");
}
//
// Update g_hdsaCls at the end so that other thread won't
// access while we are adding items.
//
g_hdsaCls = hdsaCls;
ASSERT(DSA_GetItemCount(g_hdsaCls)==DSA_GetItemCount(g_hdsaStr));
}
}
}
LEAVECRITICAL;
return (g_hdsaCls && DSA_GetItemCount(g_hdsaCls)==DSA_GetItemCount(g_hdsaStr));
}
HRESULT CDocObjectHost::_RegisterMediaTypeClass(IBindCtx* pbc)
{
HRESULT hres = S_FALSE; // Assume no mapping
if (_BuildClassMapping() && DSA_GetItemCount(g_hdsaCls)) {
//
// WARNING: This code assumes that g_hdsaCls/g_hdsaStr never
// changes once they are initializes. Read notes above
// those global variables for detail.
//
hres = RegisterMediaTypeClass(pbc,
DSA_GetItemCount(g_hdsaCls),
(LPCSTR*)DSA_GetItemPtr(g_hdsaStr, 0),
(CLSID*)DSA_GetItemPtr(g_hdsaCls, 0), 0);
TraceMsg(DM_MIMEMAPPING, "CDOH::_StartAsyncBinding RegisterMTC returns %x", hres);
}
// Now see if the container has anything that needs to be registered
//
if (_psp)
{
IMimeInfo * pIMimeInfo;
hres = _psp->QueryService(SID_IMimeInfo, IID_IMimeInfo, (void **)&pIMimeInfo);
if (SUCCEEDED(hres))
{
UINT cTypes = 0;
LPCSTR *ppszTypes = NULL;
CLSID *pclsIDs= NULL;
ASSERT(pIMimeInfo);
hres = pIMimeInfo->GetMimeCLSIDMapping(&cTypes, &ppszTypes, &pclsIDs);
if (SUCCEEDED(hres)) {
if (cTypes && ppszTypes && pclsIDs) {
// Last one to register wins, so if the container wants to override what is
// already registered this should do it.
// URLMon will handle the duplicates corectly.
//
hres = RegisterMediaTypeClass(pbc, cTypes, ppszTypes, pclsIDs, 0);
TraceMsg(DM_MIMEMAPPING, "CDOH::_StartAsyncBinding RegisterMTC for Container returns %x", hres);
}
// RegisterMediaTypeClass should have made copies
// so free the containers allocations as it expects us to do
//
// CoTaskMemFree(NULL) is OK
//
CoTaskMemFree(ppszTypes);
CoTaskMemFree(pclsIDs);
}
pIMimeInfo->Release();
} else {
hres = S_FALSE;
}
}
return hres;
}
HRESULT _RegisterAcceptHeaders(IBindCtx* pbc, IShellBrowser* psb)
{
return RegisterDefaultAcceptHeaders(pbc, psb);
}
HRESULT GetAmbientBoolProp(IExpDispSupport* peds, DISPID dispid, BOOL *pb)
{
VARIANT var = {0};
// Assume failure
*pb = FALSE;
HRESULT hres = peds->OnInvoke(dispid, IID_NULL, NULL, DISPATCH_PROPERTYGET, (DISPPARAMS *)&g_dispparamsNoArgs, &var, NULL, NULL);
if (SUCCEEDED(hres))
{
// VB returns success with VT_EMPTY, so we can't assert here
if (var.vt == VT_BOOL)
{
*pb = (var.boolVal) ? TRUE : FALSE;
}
else
{
// Even though VB says VT_EMPTY, we don't know what other containers
// might shove in here. Make sure we clean up.
//
VariantClear(&var);
}
}
else
{
hres = E_FAIL;
}
return hres;
}
HRESULT CDocObjectHost::_GetOfflineSilent(BOOL *pbIsOffline, BOOL *pbIsSilent)
{
if (_peds)
{
if (pbIsOffline)
GetAmbientBoolProp(_peds, DISPID_AMBIENT_OFFLINEIFNOTCONNECTED, pbIsOffline);
if (pbIsSilent)
GetAmbientBoolProp(_peds, DISPID_AMBIENT_SILENT, pbIsSilent);
}
else
{
if (pbIsOffline)
*pbIsOffline = FALSE;
if (pbIsSilent)
*pbIsSilent = FALSE;
}
return S_OK;
}
/*
Callback function for RatingObtainQuery
*/
void RatingObtainQueryCallback(DWORD dwUserData, HRESULT hr, LPCSTR pszRating, LPVOID lpvInpageRating)
{
TraceMsg(DM_PICS, "RatingObtainQueryCallback given result %x", hr);
/* WARNING: This function is called by MSRATING.DLL on a separate thread,
* not the main message loop thread. Touch nothing in important data
* structures not protected by critical sections!
*
* Merely format up a windows message with the info we have; we'll handle
* this in the main thread, if we ever get there.
*
* Note that pszRating is ignored, we count on the ratings engine to have
* called RatingCheckUserAccess for us and provide the HRESULT.
*/
if (!::_PostPicsMessage(dwUserData, hr, lpvInpageRating))
{
if ( lpvInpageRating )
{
::RatingFreeDetails(lpvInpageRating);
lpvInpageRating = NULL;
}
}
}
HRESULT CDocObjectHost::_StartAsyncBinding(IMoniker * pmk, IBindCtx * pbc, IShellView * psvPrev)
{
URLMSG(TEXT("_StartAsyncBinding called"));
HRESULT hres;
ASSERT(_bsc._pbc == NULL && _pole == NULL);
_bsc._RegisterObjectParam(pbc);
//
// Associate the client site as an object parameter to this
// bind context so that Trident can pick it up while processing
// IPersistMoniker::Load().
//
pbc->RegisterObjectParam(WSZGUID_OPID_DocObjClientSite, SAFECAST(this, IOleClientSite*));
_ChainBSC();
IUnknown * punk = NULL;
_bsc._pbc = pbc;
pbc->AddRef();
// Decide right here whether or not this frame is offline
BOOL bFrameIsOffline = FALSE;
BOOL bFrameIsSilent = FALSE;
this->_GetOfflineSilent(&bFrameIsOffline, &bFrameIsSilent);
_bsc._bFrameIsOffline = bFrameIsOffline ? TRUE : FALSE;
_bsc._bFrameIsSilent = bFrameIsSilent ? TRUE : FALSE;
BOOL bSuppressUI = (_bsc._bFrameIsSilent || _IsDesktopItem(SAFECAST(this, CDocObjectHost*))) ? TRUE : FALSE;
#ifdef DEBUG
PERFMSG(TEXT("_StartAsyncBinding Calling pmk->BindToObject"), GetCurrentTime()-g_dwPerf);
g_dwPerf = GetCurrentTime();
#endif
#ifdef DEBUG
if (g_dwPrototype & 0x00000800) {
TraceMsg(DM_TRACE, "CDOH::_StartAsyncBinding skipping CLSID mapping");
}
else
#endif
{
// Register overriding mime->CLSID mapping
_RegisterMediaTypeClass(pbc);
}
// Register accept headers
_RegisterAcceptHeaders(pbc, _psb);
if (_pwb)
{
_pwb->SetNavigateState(BNS_BEGIN_NAVIGATE);
}
_StartBasePicsProcessor();
//
// Crazy sync/async behavior of URLMON. - zekel - 6-AUG-97
// any of the following may occur:
//
// 1. SUCCESS or FAILURE: we receive sync E_PENDING from BindToObject,
// and then get an Async HRESULT on OnStopBinding().
// this is the most common case and the basic design.
//
// 2. SUCCESS: we receive sync S_OK from BindToObject and
// need to complete the async behavior on our BSCB ourself
// since urlmon started but did not finish.
//
// 3. SUCCESS: while inside BindToObject(), we receive sync S_OK
// from OnStopBinding(), and then BindToObject returns with S_OK.
//
// 4. FAILURE: simplest case is an error being returned from BindToObject()
// but without an any OnStopBinding() so we need to complete
// the async behavior on our BSCB ourself since urlmon started but did not finish.
// this usually occurs when accessing local files.
//
// 5. FAILURE: while inside BindToObject(), we receive sync S_OK from OnStopBinding(),
// and then BindToObject returns with some other error that needs to be handled.
// this occurs with some malformed urls.
//
// 6. FAILURE: while inside BindToObject(), we receive a sync error from OnStopBinding(),
// and then BindToObject returns with some other error (usually E_FAIL).
// we need to trust the first one. this occurs when wininet
// returns syncronous errors, and its error is the one returned in OnStopBinding()
//
// 7. FAILURE: while inside BindToObject(), we receive a sync error from OnStopBinding(),
// and then BindToObject returns with E_PENDING. which we think means everything
// is going great, and urlmon thinks it is done. this happens with a file: to
// a resource that is not hostable. we need to show the download UI.
//
// in order to support all the errors in the most consistent and safe manner,
// we defer any errors in OnStopBinding() if they are delivered synchronously
// on BindToObject(). the OnStopBinding() error always overrides the BindToObject()
// error, but any error will always override any success.
//
ASSERT(S_OK == _hrOnStopBinding);
_fSyncBindToObject = TRUE;
URLMSG(TEXT("_StartAsyncBinding calling pmk->BindToObject"));
hres = pmk->BindToObject(pbc, NULL, IID_IUnknown, (void**)&punk);
URLMSG3(TEXT("_StartAsyncBinding pmk->BindToObject returned"), hres, punk);
_fSyncBindToObject = FALSE;
if (SUCCEEDED(_hrOnStopBinding) && (SUCCEEDED(hres) || hres==E_PENDING))
{
hres = S_OK;
if (_bsc._pbc) {
//
// In case OnStopBinding hasn't been called.
//
if (!_pole)
{
if (psvPrev)
{
_bsc._psvPrev = psvPrev;
psvPrev->AddRef();
}
}
else
{
URLMSG3(TEXT("_StartAsyncBinding we've already got _pole"), hres, _pole);
}
//
// If moniker happen to return the object synchronously, emulate
// OnDataAvailable callback and OnStopBinding.
//
if (punk)
{
_bsc.OnObjectAvailable(IID_IUnknown, punk);
_bsc.OnStopBinding(hres, NULL);
punk->Release();
ASSERT(_bsc._pbc==NULL);
}
}
else
{
//
// OnStopBinding has been already called.
//
if (punk)
{
AssertMsg(0, TEXT("CDOH::_StartAsyncBinding pmk->BindToObject returned punk after calling OnStopBinding")); // Probably URLMON bug.
punk->Release();
}
}
}
else
{
// Binding failed.
TraceMsg(DM_WARNING, "CDOH::_StartAsyncBinding failed (%x)", hres);
//
// Urlmon is inconsistent in it's error handling - zekel - 4-AUG-97
// urlmon can return errors in three different ways from BindToObject()
// 1. it can return back a simple syncronous error. without calling OnStopBinding()
//
// 2. it can return a sync error,
// but call OnStopBinding() with S_OK first on the same thread;
//
// 3. it can return a sync error,
// but also call OnStopBinding() with the real Error first on the same thread.
//
// 4. it can return E_PENDING,
// but already have called OnStopBinding() with the real error.
//
// SOLUTIONS:
// in all cases of error in OnStopBinding(), we will now postpone the OnStopBinding processing util after
// we have returned from the BindToObject(). we try to use the best error.
// we allow successful OnStopBinding() to pass through unmolested, and trap
// the error here if necessary.
//
if (FAILED(_hrOnStopBinding))
hres = _hrOnStopBinding;
if (_bsc._pbc)
_bsc.OnStopBinding(hres, NULL);
else if (!bSuppressUI)
{
//
// OnStopBinding was already called, but with a success
// so we need to handle the error here. this happens
// with some invalid URLs like http:/server
//
BOOL fCancelErrorPage = FALSE;
_FireNavigateErrorHelper(NULL, ERRORPAGE_SYNTAX, &fCancelErrorPage);
if (fCancelErrorPage)
{
_CancelPendingNavigation(TRUE, FALSE);
}
else
{
// Fix for W98 webtv app. If we're in a frame don't
// blow away the frame set to dispaly the error.
//
if (!_fHaveParentSite)
{
_bsc._NavigateToErrorPage(ERRORPAGE_SYNTAX, this, FALSE);
}
}
}
ASSERT(_bsc._pbc==NULL);
}
return hres;
}
void CDocObjectHost::_ReleasePendingObject(BOOL fIfInited)
{
HRESULT hres;
IOleObject *polePending;
#ifdef TRIDENT_NEEDS_LOCKRUNNING
IRunnableObject *pro;
#endif
if (fIfInited == FALSE && _fPendingWasInited == FALSE)
return;
if (_punkPending)
{
if (_fCreatingPending)
{
_fAbortCreatePending = 1;
return;
}
if (!_fPendingNeedsInit && !IsSameObject(_punkPending, _pole))
{
hres = _punkPending->QueryInterface(IID_IOleObject, (void **) &polePending);
if (SUCCEEDED(hres)) {
LPOLECLIENTSITE pcs;
if (SUCCEEDED(polePending->GetClientSite(&pcs)) && pcs)
{
if (pcs == SAFECAST(this, LPOLECLIENTSITE))
{
polePending->SetClientSite(NULL);
}
pcs->Release();
}
polePending->Release();
}
}
#ifdef TRIDENT_NEEDS_LOCKRUNNING
// TRIDENT NO LONGER SUPPORTS IRunnableObject
hres = _punkPending->QueryInterface(IID_IRunnableObject, (void **) &pro);
if (SUCCEEDED(hres))
{
hres = pro->LockRunning(FALSE, TRUE);
pro->Release();
}
#endif
SAFERELEASE(_punkPending);
_fPendingWasInited = FALSE;
}
}
void CDocObjectHost::_ReleaseOleObject(BOOL fIfInited)
{
TraceMsg(DM_DEBUGTFRAME, "CDocObjectHost::_ReleaseOleObject called %x (%x)", _pole, this);
// Minimize impact by cleaning up in affected cases only.
if (fIfInited == FALSE && _fPendingWasInited == FALSE)
return;
// release _pole object and all the associated QI'ed pointers
if (_phls) {
_phls->SetBrowseContext(NULL); // probably no need
ATOMICRELEASE(_phls);
}
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 == (IAdviseSink *)this) {
_pvo->SetAdvise(0, 0, NULL);
} else {
ASSERT(0); // do we really hit this case?
}
pSink->Release();
}
ATOMICRELEASE(_pvo);
}
if (_pole)
{
LPOLECLIENTSITE pcs;
if (SUCCEEDED(_pole->GetClientSite(&pcs)) && pcs)
{
if (IsSameObject(pcs, SAFECAST(this, LPOLECLIENTSITE)))
{
_pole->SetClientSite(NULL);
}
pcs->Release();
}
// Notes: Make it sure that we don't hold a bogus _pole even
// for a moment (while we call Release).
ATOMICRELEASE(_pole);
}
}
//
// This member releases all the interfaces to the DocObject, which is
// essentially the reverse of _Bind.
//
void CDocObjectHost::_UnBind(void)
{
ATOMICRELEASE(_pHTMLWindow);
ATOMICRELEASE(_pmsot);
VariantClear(&_varUserEnteredUrl);
ASSERT(!_pmsov); // paranoia
ATOMICRELEASE(_pmsov);
ASSERT(!_pmsoc); // paranoia
ATOMICRELEASE(_pmsoc);
_xao.SetActiveObject(NULL);
if (_pole)
{
// Just in case we're destroyed while we were waiting
// for the docobj to display itself.
//
_RemoveTransitionCapability();
//
// If this is NOT MSHTML, cache the OLE server so that we don't
// need to restart or load the OLE server again.
//
if (!(_dwAppHack & (BROWSERFLAG_MSHTML | BROWSERFLAG_DONTCACHESERVER)))
{
IBrowserService *pbs;
if (SUCCEEDED(QueryService(SID_STopLevelBrowser, IID_IBrowserService, (void **)&pbs)))
{
pbs->CacheOLEServer(_pole);
pbs->Release();
}
}
TraceMsg(DM_ADVISE, "CDocObjectHost::_UnBind about to call Close of %x", _pole);
_pole->Close(OLECLOSE_NOSAVE);
_ReleaseOleObject();
}
_ReleasePendingObject();
ATOMICRELEASE(_pstg);
ATOMICRELEASE(_pbcCur);
ATOMICRELEASE(_pmkCur);
}
//
// HACK: If we open Excel95 objects directly, Excel goes crazy and eventually
// hit GPF. Here is the background info, I've got Office guys (SatoNa).
//
// From: Rajeev Misra (Xenix)
//
// 1) Excel does not handle the foll. case very well. Taking a normal file
// loading it through IPersistFile:Load and then bringing it up as an
// embedded object. The code was always tested so that the embedded
// objects always got loaded through ScPrsLoad. I am seeing a bunch of
// asserts in Excel that say that this assumption is being destroyed.
// ASSERT(_pole);
//
// From: Srini Koppolu
//
// For you, there is only one case, i.e. you always deal with the files. Then your code should look like this
//
// CreateFileMoniker from the file
// pUIActiveObject->OnFrameWindowActivate(FALSE);
// pmk->BindToObject(IID_IDataObject, &pdobj)
// pUIActiveObject->OnFrameWindowActivate(TRUE);
// OleCreateFromData()
//
// OnFrameWindowActivate is done to take care of another excel problem.
// If you currently have and Excel object UIActive in you and you try to
// do IPersistFile::Load on Excel, then it will cause problems.
//
void CDocObjectHost::_AppHackForExcel95(void)
{
ASSERT(_pole);
HRESULT hres;
IDataObject* pdt = NULL;
hres = _pole->QueryInterface(IID_IDataObject, (void **)&pdt);
TraceMsg(DM_BINDAPPHACK, "_PostBindAppHack -- QI(IOleDataObject) returned %x", hres);
if (SUCCEEDED(hres))
{
ASSERT(_pstg==NULL);
hres = StgCreateDocfile(NULL,
STGM_DIRECT | STGM_CREATE | STGM_READWRITE
| STGM_SHARE_EXCLUSIVE | STGM_DELETEONRELEASE,
0, &_pstg);
TraceMsg(DM_BINDAPPHACK, "_PostBindAppHack StgCreateDocFile(NULL) returned %x", hres);
if (SUCCEEDED(hres))
{
IOleObject* poleCopy = NULL;
hres = OleCreateFromData(pdt, IID_IOleObject, OLERENDER_NONE,
NULL, this, _pstg, (void **)&poleCopy);
TraceMsg(DM_BINDAPPHACK, "_PostBindAppHack OleCreateFromData(IOleObject) returned %x", hres);
if (SUCCEEDED(hres)) {
_fCantSaveBack = TRUE;
ATOMICRELEASE(_pole);
_pole = poleCopy;
}
}
pdt->Release();
}
}
//
// This function get the UserClassID from the object and opens the regkey
// for that CLSID and returns. If pdwAppHack is non-NULL AND CLSID is
// CLSID_HTMLDocument, we skip all and returns the default apphack flag.
// This is a perf optimization, but prevents us from setting browser
// flags for Trident, which is fine. (SatoNa)
//
HKEY _GetUserCLSIDKey(IOleObject* pole, const CLSID* pclsid, DWORD* pdwAppHack)
{
HKEY hkey = NULL; // assume error
HRESULT hres;
CLSID clsid = CLSID_NULL;
if (pole)
{
hres = pole->GetUserClassID(&clsid);
// GetUserClassID is optional, can return E_FAIL, then is defined to be
// the same as that returned by IPersist::GetClassID. cf, msdev documentation
// for GetUserClassID
if (FAILED(hres))
{
hres = IUnknown_GetClassID(pole, &clsid);
}
}
else if (pclsid)
{
clsid = *pclsid;
hres = S_OK;
}
else
{
return NULL;
}
//
// Notice that we check for two CLSIDs to see if this is MSHTML.
//
if (pdwAppHack)
{
static const IID IID_IVBOleObj =
{0xb88c9640, 0x14e0, 0x11d0, { 0xb3, 0x49, 0x0, 0xa0, 0xc9, 0xa, 0xea, 0x82 } };
IUnknown * pVBOleObj;
if ( IsEqualGUID(clsid, CLSID_HTMLDocument)
|| IsEqualGUID(clsid, CLSID_MHTMLDocument)
|| IsEqualGUID(clsid, CLSID_HTMLPluginDocument) )
{
TraceMsg(TF_SHDAPPHACK, "_GetUserCLSID this is Trident. Skip opening reg key");
*pdwAppHack = BROWSERFLAG_NEVERERASEBKGND | BROWSERFLAG_SUPPORTTOP
| BROWSERFLAG_MSHTML;
return NULL;
}
else if (pole && SUCCEEDED(pole->QueryInterface(IID_IVBOleObj, (void**)&pVBOleObj) ))
{
// If the object answers to IID_IVBOleObj, it's a VB doc object and shouldn't be cached.
//
pVBOleObj->Release();
*pdwAppHack = BROWSERFLAG_DONTCACHESERVER;
}
}
//
// HACK: MSHTML.DLL does not implement GetUserClassID, but
// returns S_OK. That's why we need to check for CLSID_NULL.
//
if (SUCCEEDED(hres) && !IsEqualGUID(clsid, CLSID_NULL)) {
TCHAR szBuf[50]; // 50 is enough for GUID
SHStringFromGUID(clsid, szBuf, ARRAYSIZE(szBuf));
TraceMsg(DM_BINDAPPHACK, "_PostBindAppHack GetUserClassID = %s", szBuf);
TCHAR szKey[60]; // 60 is enough for CLSID\\{CLSID_XX}
wnsprintf(szKey, ARRAYSIZE(szKey), TEXT("CLSID\\%s"), szBuf);
if (RegOpenKey(HKEY_CLASSES_ROOT, szKey, &hkey)!=ERROR_SUCCESS)
{
TraceMsg(DM_WARNING, "_GetUserCLSIDKey RegOpenKey(%s) failed", szKey);
// I don't trust RegOpenKey.
hkey = NULL;
}
}
return hkey;
}
BOOL _GetAppHackKey(LPCTSTR pszProgID, DWORD* pdwData)
{
BOOL fSuccess = FALSE;
HKEY hkey;
if (RegOpenKey(HKEY_CLASSES_ROOT, pszProgID, &hkey)==ERROR_SUCCESS)
{
DWORD dwType;
DWORD cbSize = SIZEOF(*pdwData);
if (RegQueryValueEx(hkey, TEXT("BrowserFlags"), NULL,
&dwType, (LPBYTE)pdwData, &cbSize)==ERROR_SUCCESS
&& (dwType==REG_DWORD || (dwType==REG_BINARY && cbSize==SIZEOF(*pdwData))))
{
fSuccess = TRUE;
}
else
{
//
// Unlike IE3, we make it absolutely sure that the type of object
// has either "DocObject" key or "BrowseInPlace" key under the
// ProgID. We can't rely on QI(IID_IOleDocument) because MFC 4.2
// has a bug and returns S_OK to it. As far as I know, MS-Paint
// and OmniPage pro are affected by this. We could individually
// address each of them, but it's probably impossible to catch
// all. This change has a small risk of breaking existing DocObject
// server which does not have neither key. If we find such a
// server, we'll address those individually (which is much easier
// than covering all MFC apps). (SatoNa)
//
TCHAR ach[MAX_PATH];
LONG cb;
BOOL fBrowsable = FALSE;
if ((cb=SIZEOF(ach)) && RegQueryValue(hkey, TEXT("DocObject"), ach, &cb) == ERROR_SUCCESS)
fBrowsable = TRUE;
else if ((cb=SIZEOF(ach)) && RegQueryValue(hkey, TEXT("BrowseInPlace"), ach, &cb) == ERROR_SUCCESS)
fBrowsable = TRUE;
if (!fBrowsable) {
TraceMsg(DM_WARNING, "_GetAppHackKey this is neither DocObject or BrowseInPlace");
*pdwData = BROWSERFLAG_DONTINPLACE;
}
}
RegCloseKey(hkey);
}
return fSuccess;
}
void GetAppHackFlags(IOleObject* pole, const CLSID* pclsid, DWORD* pdwAppHack)
{
HKEY hkey = _GetUserCLSIDKey(pole, pclsid, pdwAppHack);
if (hkey)
{
TCHAR szValue[MAX_PATH];
LONG cb = SIZEOF(szValue);
if (RegQueryValue(hkey, TEXT("ProgID"), szValue, &cb) == ERROR_SUCCESS)
{
//
// First, check if we have an BrowserFlags flag in the registry.
// If there is, use it. Otherwise, try hard-coded progIDs as
// we did in IE 3.0
//
_GetAppHackKey(szValue, pdwAppHack);
if (!(*pdwAppHack & BROWSERFLAG_REPLACE)) {
typedef struct _APPHACK {
LPCTSTR pszProgID;
DWORD dwAppHack;
} APPHACK;
//
// We no longer need to disable in-place activation of
// MS-PAINT because we look for "BrowseInPlace" or
// "DocObject" key
//
// { "Paint.Picture", BROWSERFLAG_DONTINPLACE },
//
const static APPHACK s_aah[] = {
{ TEXT("Excel.Sheet.5"), BROWSERFLAG_OPENCOPY },
{ TEXT("Excel.Chart.5"), BROWSERFLAG_OPENCOPY },
{ TEXT("SoundRec"), BROWSERFLAG_OPENVERB },
{ TEXT("Word.Document.6"), BROWSERFLAG_SETHOSTNAME },
{ TEXT("Word.Document.8"), BROWSERFLAG_DONTUIDEACTIVATE | BROWSERFLAG_SETHOSTNAME },
{ TEXT("PowerPoint.Show.8"), BROWSERFLAG_DONTUIDEACTIVATE | BROWSERFLAG_PRINTPROMPTUI },
{ TEXT("Excel.Sheet.8"), BROWSERFLAG_DONTDEACTIVATEMSOVIEW | BROWSERFLAG_INITNEWTOKEEP },
{ TEXT("Excel.Chart.8"), BROWSERFLAG_DONTDEACTIVATEMSOVIEW | BROWSERFLAG_INITNEWTOKEEP },
{ TEXT("ABCFlowCharter6.Document"), BROWSERFLAG_DONTINPLACE },
{ TEXT("ABCFlowCharter7.Document"), BROWSERFLAG_DONTINPLACE },
{ TEXT("FlowCharter7.Document"), BROWSERFLAG_DONTINPLACE },
{ TEXT("ChannelFile"), BROWSERFLAG_DONTAUTOCLOSE },
{ TEXT("Visio.Drawing.5"), BROWSERFLAG_ENABLETOOLSBTN | BROWSERFLAG_SAVEASWHENCLOSING },
{ TEXT("Visio.Drawing.4"), BROWSERFLAG_ENABLETOOLSBTN | BROWSERFLAG_SAVEASWHENCLOSING }
};
const static TCHAR s_ActiveMoveCtx[] = TEXT("AMOVIE.ActiveMovieControl");
if (!StrCmpN(szValue, s_ActiveMoveCtx, ARRAYSIZE(s_ActiveMoveCtx)-1))
{
*pdwAppHack = BROWSERFLAG_DONTAUTOCLOSE;
}
else
{
for (int i=0; i<ARRAYSIZE(s_aah); i++) {
if (StrCmp(szValue, s_aah[i].pszProgID)==0)
{
*pdwAppHack |= s_aah[i].dwAppHack;
break;
}
}
}
}
TraceMsg(DM_BINDAPPHACK, "_GetAppHack ProgID=%s, *pdwAppHack=%x",
szValue, *pdwAppHack);
} else {
TraceMsg(DM_BINDAPPHACK, "_GetAppHack RegQueryValue(ProgID) failed");
}
RegCloseKey(hkey);
}
}
DWORD CDocObjectHost::_GetAppHack(void)
{
ASSERT(_pole);
if (!_fHaveAppHack && _pole)
{
_dwAppHack = 0; // Assume no hack
_fHaveAppHack = TRUE;
::GetAppHackFlags(_pole, NULL, &_dwAppHack);
}
return _pole ? _dwAppHack : 0;
}
void CDocObjectHost::_PostBindAppHack(void)
{
_GetAppHack();
if (_fAppHackForExcel()) {
_AppHackForExcel95();
}
}
//
// This member binds to the object specified by a moniker.
//
HRESULT CDocObjectHost::_BindSync(IMoniker* pmk, IBindCtx* pbc, IShellView* psvPrev)
{
ASSERT(pbc || !_pole);
HRESULT hres = S_OK;
ASSERT(_pole==NULL);
// Check if we are in the middle of asynchronous binding
if (_bsc._fBinding)
{
// Yes, wait until it's done or canceled/stopped
URLMSG(TEXT("_Bind called in the middle of async-binding. Wait in a message loop"));
while(_bsc._fBinding)
{
MSG msg;
if (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
if (!_pole)
{
hres = E_FAIL; // Get the error code from OnStopBinding
}
}
else
{
// No, bind synchronously
URLMSG(TEXT("_Bind. Performing syncronous binding"));
hres = pmk->BindToObject(pbc, NULL, IID_IOleObject, (void **)&_pole);
}
TraceMsg(0, "sdv TR : _Bind -- pmk->BindToObject(IOleObject) returned %x", hres);
_OnBound(hres);
return hres;
}
void CDocObjectHost::_OnBound(HRESULT hres)
{
if (SUCCEEDED(hres)) {
_PostBindAppHack();
_InitOleObject();
}
}
//
// This function returns TRUE if the specified file's open command is
// associated with "explorer.exe" or "iexplore.exe".
//
// NOTES: It does not check if the "open" command is actually the default
// or not, but that's sufficient in 99.99 cases.
//
BOOL IsAssociatedWithIE(LPCWSTR szPath)
{
LPCTSTR pszExtension = PathFindExtension(szPath);
BOOL bRet = FALSE;
TCHAR szBuf[MAX_PATH];
TCHAR szExt[_MAX_EXT];
DWORD cchBuf = ARRAYSIZE(szBuf);
if (SUCCEEDED(AssocQueryString(0, ASSOCSTR_COMMAND, szExt, TEXT("open"), szBuf, &cchBuf)))
{
TraceMsg(TF_SHDBINDING, "IsAssociatedWithIE(%s) found %s as open command", szPath, szBuf);
LPCTSTR pszFound;
if ( (pszFound=StrStrI(szBuf, IEXPLORE_EXE))
|| (pszFound=StrStrI(szBuf, EXPLORER_EXE)) )
{
if (pszFound==szBuf || *(pszFound - 1)==TEXT('\\'))
{
bRet = TRUE;
}
}
}
TraceMsg(DM_SELFASC, "IsAssociatedWithIE(%s) returning %d", szPath, bRet);
return bRet;
}
HRESULT CDocObjectHost::_MayHaveVirus(REFCLSID rclsid)
{
//
// We'll call this function twice if the file is associated
// with a bogus CLSID (such as ImageComposer).
//
if (_fConfirmed)
{
TraceMsg(TF_SHDAPPHACK, "CDOH::_MayHaveVirus called twice. Return S_OK");
return S_OK;
}
TraceMsg(TF_SHDPROGRESS, "DOH::_MayHaveVirus called");
LPWSTR pwzProgID = NULL;
HRESULT hresT = E_FAIL;
if (SUCCEEDED(ProgIDFromCLSID(rclsid, &pwzProgID)))
{
if ( StrCmpI(pwzProgID, TEXT("htmlfile")) != 0
&& StrCmpI(pwzProgID, TEXT("htmlfile_FullWindowEmbed")) != 0
&& StrCmpI(pwzProgID, TEXT("mhtmlfile")) != 0
&& StrCmpI(pwzProgID, TEXT("xmlfile")) != 0
&& StrCmpI(pwzProgID, TEXT("xslfile")) != 0)
{
TCHAR szURL[MAX_URL_STRING];
TCHAR * pszURL = szURL;
hresT = _GetCurrentPage(szURL, ARRAYSIZE(szURL), TRUE);
if (SUCCEEDED(hresT))
{
UINT uRet = IDOK;
if (_PicsProcBase._fbPicsWaitFlags || !_PicsProcBase._fPicsAccessAllowed)
{
_PicsProcBase._fbPicsWaitFlags &= ~(PICS_WAIT_FOR_INDOC | PICS_WAIT_FOR_END); /* indoc ratings only on htmlfile */
TraceMsg(DM_PICS, "CDOH::_MayHaveVirus found non-HTML, waitflags now %x", (DWORD)_PicsProcBase._fbPicsWaitFlags);
uRet = _PicsProcBase._PicsBlockingDialog();
}
if (uRet == IDOK)
{
TraceMsg(TF_SHDPROGRESS, "DOH::_MayHaveVirus calling MayOpenSafeDialogOpenDialog(%s)", pwzProgID);
if (_bsc._pszRedirectedURL && *_bsc._pszRedirectedURL)
pszURL = _bsc._pszRedirectedURL;
IUnknown * punk;
BOOL fDisableOpen = TRUE;
LPCTSTR pszExt = NULL;
if (_bsc._pszCacheFileName)
pszExt = PathFindExtension(_bsc._pszCacheFileName);
else if (pszURL)
pszExt = PathFindExtension(pszURL);
if (pszExt)
{
TCHAR szClassName[MAX_PATH];
DWORD cbSize = SIZEOF(szClassName);
// if the ProgIDs don't match then disable the open button
if (ERROR_SUCCESS == SHGetValue(HKEY_CLASSES_ROOT, pszExt, NULL, NULL, szClassName, &cbSize))
fDisableOpen = StrCmpI(pwzProgID, szClassName);
}
QueryInterface(IID_IUnknown, (void**)&punk);
uRet = MayOpenSafeOpenDialog(_hwnd, pwzProgID, pszURL, _bsc._pszCacheFileName, NULL, _uiCP, punk, _pmsoctBrowser, fDisableOpen);
ATOMICRELEASE(punk);
_fCalledMayOpenSafeDlg = TRUE;
}
switch(uRet)
{
case IDIGNORE:
//
// allow the safeopen dlg to pop up later if necessary
//
_fCalledMayOpenSafeDlg = FALSE;
_fConfirmed = FALSE;
break; // continue download
case IDOK:
//
// Set this flag to avoid poppping this dialog box twice.
//
_fConfirmed = TRUE;
break; // continue download
case IDD_SAVEAS:
IUnknown *punk;
hresT = QueryInterface(IID_IUnknown, (void**)&punk);
if(SUCCEEDED(hresT))
{
CDownLoad_OpenUI(_pmkCur, _bsc._pbc, FALSE, TRUE, NULL, NULL, NULL, NULL, NULL, _bsc._pszRedirectedURL, _uiCP, punk);
punk->Release();
}
// fall through to abort binding.
case IDCANCEL:
hresT = HRESULT_FROM_WIN32(ERROR_CANCELLED);
break;
}
}
else
{
TraceMsg(DM_ERROR, "DOH::_MayHaveVirus _GetCurrentPage failed %x", hresT);
}
}
else
{
TraceMsg(TF_SHDPROGRESS, "DOH::_MayHaveVirus this is htmlfile -- don't call MayOpenSafeDialogOpenDialog");
_fPicsBlockLate = TRUE;
}
OleFree(pwzProgID);
}
return hresT;
}
STDMETHODIMP CDocObjectHost::SaveObject(void)
{
TraceMsg(0, "sdv TR: CDOV::SaveObject called");
// Implemente it later.
return S_OK;
}
STDMETHODIMP CDocObjectHost::GetMoniker(DWORD dwAssign,
DWORD dwWhichMoniker,
IMoniker **ppmk)
{
HRESULT hres = E_INVALIDARG;
*ppmk = NULL;
TraceMsg(TF_SHDBINDING, "CDOH::GetMoniker called dwWhichMoniker=%x", dwWhichMoniker);
switch(dwWhichMoniker)
{
case OLEWHICHMK_OBJREL:
case OLEWHICHMK_OBJFULL:
if (_pmkCur)
{
*ppmk = _pmkCur;
_pmkCur->AddRef();
hres = S_OK;
}
else
{
hres = E_UNEXPECTED;
}
break;
}
return hres;
}
STDMETHODIMP CDocObjectHost::GetContainer(
IOleContainer **ppContainer)
{
// According to CKindel, we should implement this method
// as the way for a DocObject to access IDispatch interface of
// the container (i.e., frame). I'm currently thinking leaving
// all it's non-IUnknown memeber unimplemented. If there is no
// need to enumerates objects, we can simply QI from IShellBrowser
// to IOleContainer and return it. (SatoNa)
//
// NOTE: If trident calls this after DestroyHostWindow, we have nothing
// to give out. Hopefully this is not bad. (MikeSh)
TraceMsg(0, "sdv TR: CDOV::GetContainer called");
if (_psb)
return _psb->QueryInterface(IID_IOleContainer, (void **)ppContainer);
return E_FAIL;
}
STDMETHODIMP CDocObjectHost::ShowObject(void)
{
TraceMsg(0, "sdv TR: CDOV::ShowObject called");
return E_NOTIMPL; // As specified in Kraig's document
}
STDMETHODIMP CDocObjectHost::OnShowWindow(BOOL fShow)
{
TraceMsg(TF_SHDUIACTIVATE, "DOH::OnShowWindow(%d) called (this=%x)", fShow, this);
return E_NOTIMPL; // As specified in Kraig's document
}
STDMETHODIMP CDocObjectHost::RequestNewObjectLayout(void)
{
TraceMsg(0, "sdv TR: CDOV::RequestNewObjectLayout called");
return E_NOTIMPL; // As specified in Kraig's document
}
//
// This is the standard way for non-active embedding to access
// the IHlinkFrame interface. We happened to use our QI to implement
// this, but the semantics of QueryService is different from QI.
// It does not necessary return the same object.
//
HRESULT CDocObjectHost::QueryService(REFGUID guidService, REFIID riid, void **ppvObj)
{
if( IsEqualGUID(guidService, IID_IElementNamespaceTable) )
{
return IUnknown_QueryService(_pole, IID_IElementNamespaceTable, riid, ppvObj);
}
// In order for the context menu to work correctly inside IFrames, we
// need to fail a certain query ONLY for IFrames on desktop.
else if (!IsEqualGUID(guidService, CLSID_HTMLDocument) || !_IsImmediateParentDesktop(this, _psp))
{
//
// Delegate ISP to the _psb.
//
if (_psb && _psp)
return _psp->QueryService(guidService, riid, ppvObj);
}
*ppvObj = NULL;
return E_NOINTERFACE;
}
/*----------------------------------------------------------
Purpose: Remove the submenu(s) that are in _hmenuFrame
from _hmenuBrowser.
*/
void CDocObjectHost::_RemoveFrameSubMenus(void)
{
HMENU hmenu;
ASSERT(IS_VALID_HANDLE(_hmenuBrowser, MENU));
ASSERT(IS_VALID_HANDLE(_hmenuFrame, MENU));
// The file menu in _hmenuBrowser consists of the file menu from
// _hmenuFrame and IShellBrowser. The part added by _hmenuFrame
// includes a submenu (Send To), which must be removed before
// _hmenuBrowser is destroyed.
// We could just explicitly remove the Send To submenu. But to
// prevent the expensive bug hunt that it took to find this in the
// first place, we're going to iterate thru the menu and, for
// any submenus that belong to our template, we'll remove them.
int citemFile = 0;
UINT nID = 0;
// Get the count of menu items in our template's File menu and
// the ID of the first menu item.
hmenu = GetMenuFromID(_hmenuFrame, FCIDM_MENU_FILE);
if (hmenu)
{
citemFile = GetMenuItemCount(hmenu);
nID = GetMenuItemID(hmenu, 0);
}
// Now look at the browser menu's File menu and, starting at
// nID, remove any submenus.
hmenu = GetMenuFromID(_hmenuBrowser, FCIDM_MENU_FILE);
if (hmenu)
{
int citem = GetMenuItemCount(hmenu);
int iTop;
int i;
// Where does our template file menu start?
for (iTop = 0; iTop < citem; iTop++)
{
if (GetMenuItemID(hmenu, iTop) == nID)
{
// Start at where our template file menu ends and work up
for (i = iTop + citemFile - 1; 0 < citemFile ; i--, citemFile--)
{
HMENU hmenuSub = GetSubMenu(hmenu, i);
if (hmenuSub)
RemoveMenu(hmenu, i, MF_BYPOSITION);
}
break;
}
}
}
}
/*----------------------------------------------------------
Purpose: Destroy the browser menu.
*/
HRESULT CDocObjectHost::_DestroyBrowserMenu(void)
{
TraceMsg(TF_SHDUIACTIVATE, "DOH::_DestroyBrowserMenu called");
if (_hmenuBrowser) {
// First remove any submenus that are held by other menus,
// so we don't blow them away.
_RemoveFrameSubMenus();
if (EVAL(_psb)) {
_psb->RemoveMenusSB(_hmenuBrowser);
}
DestroyMenu(_hmenuBrowser);
_hmenuBrowser = NULL;
}
return S_OK;
}
HRESULT CDocObjectHost::_CreateBrowserMenu(LPOLEMENUGROUPWIDTHS pmw)
{
TraceMsg(TF_SHDUIACTIVATE, "DOH::_CreateBrowserMenu called");
if (_hmenuBrowser) {
return S_OK;
}
_hmenuBrowser = CreateMenu();
if (!_hmenuBrowser) {
return E_OUTOFMEMORY;
}
HRESULT hres = E_FAIL;
// Allow IShellBrowser a chance to add its menus
if (EVAL(_psb))
hres = _psb->InsertMenusSB(_hmenuBrowser, pmw);
// HACK: Win95 explorer returns E_NOTIMPL
if (hres==E_NOTIMPL) {
hres = S_OK;
}
if (SUCCEEDED(hres)) {
// Load our menu if not loaded yet
if (!_hmenuFrame)
{
_hmenuFrame = LoadMenu(MLGetHinst(), MAKEINTRESOURCE(MID_FOCUS));
}
// Get the "File" sub-menu from the shell browser.
MENUITEMINFO mii;
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_SUBMENU;
if (GetMenuItemInfo(_hmenuBrowser, FCIDM_MENU_FILE, FALSE, &mii))
{
HMENU hmenuFileBrowse = mii.hSubMenu;
// Merge our menuitems into this submenu.
if (_hmenuFrame)
{
MENUITEMINFO miiItem;
miiItem.cbSize = SIZEOF(MENUITEMINFO);
miiItem.fMask = MIIM_SUBMENU;
if (GetMenuItemInfo(_hmenuFrame, FCIDM_MENU_FILE, FALSE, &miiItem))
{
TCHAR szItem[128];
HMENU hmenuFileT = miiItem.hSubMenu;
UINT citem = GetMenuItemCount(hmenuFileT);
for (int i=citem-1; i>=0 ; i--)
{
// We need to reset for each item.
miiItem.fMask = MIIM_STATE | MIIM_ID | MIIM_SUBMENU | MIIM_CHECKMARKS | MIIM_TYPE | MIIM_DATA;
miiItem.fType = MFT_STRING;
miiItem.cch = ARRAYSIZE(szItem);
miiItem.dwTypeData = szItem;
miiItem.dwItemData = 0;
if (GetMenuItemInfo(hmenuFileT, i, TRUE, &miiItem)) {
InsertMenuItem(hmenuFileBrowse, 0, TRUE, &miiItem);
}
}
}
}
}
else
{
TraceMsg(TF_SHDUIACTIVATE, "DOH::_CreateBrowseMenu parent has no File menu (it's probably a browser OC)");
ASSERT(0); // DocObject in OC is not supposed to call InsertMenus.
}
}
DEBUG_CODE( _DumpMenus(TEXT("after _CreateBrowserMenu"), TRUE); )
return hres;
}
//
// IOleInPlaceFrame::InsertMenus equivalent
//
HRESULT CDocObjectHost::_InsertMenus(
/* [in] */ HMENU hmenuShared,
/* [out][in] */ LPOLEMENUGROUPWIDTHS lpMenuWidths)
{
HRESULT hres = S_OK;
int nMenuOffset = 0;
TraceMsg(TF_SHDUIACTIVATE, "DOH::InsertMenus called (this=%x)", this);
// Assume error (no menu merge)
lpMenuWidths->width[0] = 0;
lpMenuWidths->width[2] = 0;
lpMenuWidths->width[4] = 0;
lpMenuWidths->width[5] = 0;
// be extra safe and don't attempt menu merging if we're not top level
if (_fHaveParentSite)
return S_OK;
OLEMENUGROUPWIDTHS mw = { {0} };
hres = _CreateBrowserMenu(&mw);
if (FAILED(hres)) {
TraceMsg(DM_ERROR, "DOH::InsertMenus _CreateBrpwserMenu failed");
return hres;
}
// Get the "File" sub-menu from the shell browser.
MENUITEMINFO mii;
TCHAR szSubMenu[128];
mii.cbSize = SIZEOF(mii);
mii.fMask = MIIM_SUBMENU|MIIM_TYPE|MIIM_ID;
mii.cch = ARRAYSIZE(szSubMenu);
mii.dwTypeData = szSubMenu;
if (EVAL(GetMenuItemInfo(_hmenuBrowser, FCIDM_MENU_FILE, FALSE, &mii)))
{
ASSERT(szSubMenu == mii.dwTypeData);
InsertMenuItem(hmenuShared, nMenuOffset++, TRUE, &mii);
lpMenuWidths->width[0] = 1;
}
// Note that we need to re-initialize mii
mii.cch = ARRAYSIZE(szSubMenu);
if (EVAL(GetMenuItemInfo(_hmenuBrowser, FCIDM_MENU_EXPLORE, FALSE, &mii)))
{
// GetMenuItemInfo is recursive (why?). The item it retrieves
// for FCIDM_MENU_EXPLORE can either be the top level Go menu, or if that
// does not exist (NT5 case), it returns the Go To submenu of View.
//
// Code has been added in in the SetMenu implementations of Shell Browser
// and Dochost to detect the second case, because the menu dispatch list
// does not recognize this kind of menu merging (80734).
DeleteMenu(mii.hSubMenu, FCIDM_PREVIOUSFOLDER, MF_BYCOMMAND);
InsertMenuItem(hmenuShared, nMenuOffset++, TRUE, &mii);
lpMenuWidths->width[4]++;
}
mii.cch = ARRAYSIZE(szSubMenu);
if (EVAL(GetMenuItemInfo(_hmenuBrowser, FCIDM_MENU_FAVORITES, FALSE, &mii)))
{
InsertMenuItem(hmenuShared, nMenuOffset++, TRUE, &mii);
lpMenuWidths->width[4]++;
}
if (_hmenuFrame)
{
// Micro-merge the help menu.
mii.cch = ARRAYSIZE(szSubMenu);
if (EVAL(GetMenuItemInfo(_hmenuFrame, FCIDM_MENU_HELP, FALSE, &mii)))
{
InsertMenuItem(hmenuShared, nMenuOffset++, TRUE, &mii);
lpMenuWidths->width[5]++;
}
}
DEBUG_CODE( _DumpMenus(TEXT("after InsertMenus"), TRUE); )
return hres;
}
/*----------------------------------------------------------
Purpose: Different objects may add their own Help menu (like
Word and Excel). This function detects if the object
added its own help menu, or if it added items to our
help menu, or if it is just using our help menu.
If they added their own help menu, we remove ours.
*/
void CDocObjectHost::_CompleteHelpMenuMerge(HMENU hmenu)
{
HMENU hmenuHelp;
MENUITEMINFO mii;
TCHAR szSubMenu[80];
mii.cbSize = SIZEOF(mii);
mii.fMask = MIIM_SUBMENU;
// see if they added anything to our menu
if (GetMenuItemInfo(_hmenuFrame, FCIDM_MENU_HELP, FALSE, &mii))
{
hmenuHelp = mii.hSubMenu;
int iMenuCount = GetMenuItemCount(mii.hSubMenu);
// Did the number of items in the help menu change?
if (iMenuCount != HELP_ITEM_COUNT) {
// Yes; that means they added something. This has been micro-merged.
_hmenuMergedHelp = mii.hSubMenu;
_hmenuObjHelp = GetSubMenu(mii.hSubMenu, iMenuCount -1);
goto Bail;
}
// Our menu didn't change. Now find out if they added their own
// help menu or if we ARE the help. If they added their own, we need
// to remove our help menu.
_hmenuMergedHelp = NULL;
_hmenuObjHelp = NULL;
int iCount = GetMenuItemCount(hmenu) - 1;
int i;
for (i = iCount ; i >= 0 ; i--) {
mii.fMask = MIIM_SUBMENU|MIIM_TYPE;
mii.cch = ARRAYSIZE(szSubMenu);
mii.dwTypeData = szSubMenu;
if (GetMenuItemInfo(hmenu, i, TRUE, &mii)) {
if (mii.hSubMenu == hmenuHelp) {
BOOL bRemove = FALSE;
if (iCount != i) {
// if we're not the last one, then we're not it
bRemove = TRUE;
} else {
// if we are the last one see if the help menu was added
// right before us
TCHAR szMenuTitle[80];
mii.cch = ARRAYSIZE(szMenuTitle);
mii.dwTypeData = szMenuTitle;
if (GetMenuItemInfo(hmenu, i-1, TRUE, &mii)) {
if (!StrCmpI(szMenuTitle, szSubMenu)) {
// same menu string yank ours
bRemove = TRUE;
}
}
}
if (bRemove) {
RemoveMenu(hmenu, i, MF_BYPOSITION);
}
}
}
}
}
Bail:;
DEBUG_CODE( _DumpMenus(TEXT("after _CompleteHelpMenuMerge"), TRUE); )
}
//
// IOleInPlaceFrame::SetMenu equivalent
//
HRESULT CDocObjectHost::_SetMenu(
/* [in] */ HMENU hmenuShared, OPTIONAL
/* [in] */ HOLEMENU holemenu, OPTIONAL
/* [in] */ HWND hwndActiveObject)
{
TraceMsg(TF_SHDUIACTIVATE, "DOH::SetMenus(%x) called (this=%x)",
hmenuShared, this);
// be extra safe and don't attempt menu merging if we're not top level
if (_fHaveParentSite)
return S_OK;
// A NULL hmenuShared means to reinstate the container's original menu.
if (hmenuShared)
{
// Clean up duplicate help menus
_CompleteHelpMenuMerge(hmenuShared);
}
// Simply forwarding it to IShellBrowser
_hmenuSet = hmenuShared;
HRESULT hres = E_FAIL;
if (EVAL(_psb))
hres = _psb->SetMenuSB(hmenuShared, holemenu, hwndActiveObject);
if (SUCCEEDED(hres))
{
// need to tell the shell browser that we want doc obj style menu merging
if (_pmsoctBrowser)
_pmsoctBrowser->Exec(&CGID_Explorer, SBCMDID_ACTIVEOBJECTMENUS, 0, NULL, NULL);
// Compose our list of object/frame menus, so our menuband
// can dispatch the messages correctly. Essentially this is
// the same as the contents of holemenu, but since we don't
// have access to the internal struct, we must derive this
// info ourselves.
_menulist.Set(hmenuShared, _hmenuBrowser);
if (_hmenuMergedHelp)
_menulist.RemoveMenu(_hmenuMergedHelp);
if (_hmenuObjHelp)
_menulist.AddMenu(_hmenuObjHelp);
_hmenuCur = hmenuShared;
HWND hwndFrame;
_psb->GetWindow(&hwndFrame);
// 80734: Was the Go To menu taken from the View menu and grafted onto the
// main menu by DocHost? The menulist won't detect this graft, so we have
// to check ourselves and make sure it's not marked as belonging to the
// docobject.
//
// This test is duplicated in CShellBrowser2::SetMenuSB
MENUITEMINFO mii;
mii.cbSize = SIZEOF(mii);
mii.fMask = MIIM_SUBMENU;
if (hmenuShared && _hmenuBrowser &&
GetMenuItemInfo(hmenuShared, FCIDM_MENU_EXPLORE, FALSE, &mii))
{
HMENU hmenuGo = mii.hSubMenu;
if (GetMenuItemInfo(_hmenuBrowser, FCIDM_MENU_EXPLORE, FALSE, &mii) &&
mii.hSubMenu == hmenuGo && _menulist.IsObjectMenu(hmenuGo))
{
_menulist.RemoveMenu(hmenuGo);
}
}
// (scotth): why are we calling this, since this isn't compatible
// with menubands? That's the whole reason we have _menulist.
hres = OleSetMenuDescriptor(holemenu, hwndFrame, hwndActiveObject, &_dof, _ActiveObject());
}
DEBUG_CODE( _DumpMenus(TEXT("after SetMenu"), TRUE); )
return hres;
}
void CDocObjectHost::_SetStatusText(LPCSTR pszText)
{
if (_psb)
{
WPARAM wParam = STATUS_PANE_NAVIGATION;
if (g_bBiDiW95Loc && *pszText)
{
char szBuf[256];
szBuf[0] = szBuf[1] = TEXT('\t');
StrCpyNA(&szBuf[2], pszText, ARRAYSIZE(szBuf)-2);
pszText = szBuf;
wParam = SBT_RTLREADING;
}
_psb->SendControlMsg(FCW_STATUS, SB_SETTEXT, wParam | SBT_NOTABPARSING, (LPARAM)pszText, NULL);
_psb->SendControlMsg(FCW_STATUS, SB_SETTIPTEXT, wParam, (LPARAM)pszText, NULL);
}
}
/*----------------------------------------------------------
Purpose: Returns TRUE if the given menu belongs to the browser
(as opposed to the object)
*/
BOOL CDocObjectHost::_IsMenuShared(HMENU hmenu)
{
ASSERT(hmenu);
// (scotth): can we use _menulist here? (it would be faster)
if (_hmenuBrowser) {
for (int i = GetMenuItemCount(_hmenuBrowser) - 1 ; i >= 0; i--) {
if (GetSubMenu(_hmenuBrowser, i) == hmenu)
return TRUE;
}
}
// We have to special case the help menu. It's possible that the
// help menu in the shared menu actually came from _hmenuFrame
// (not _hmenuBrowser). We need to detect this case, otherwise
// the help menu gets destroyed but it is still referenced in
// _hmenuFrame.
MENUITEMINFO mii;
mii.cbSize = SIZEOF(mii);
mii.fMask = MIIM_SUBMENU;
ASSERT(IS_VALID_HANDLE(_hmenuFrame, MENU));
// Is this our help menu from _hmenuFrame?
if (GetMenuItemInfo(_hmenuFrame, FCIDM_MENU_HELP, FALSE, &mii) &&
mii.hSubMenu == hmenu)
{
// Yes
return TRUE;
}
return FALSE;
}
//
// IOleInPlaceFrame::RemoveMenus equivalent
//
HRESULT CDocObjectHost::_RemoveMenus(/* [in] */ HMENU hmenuShared)
{
TraceMsg(TF_SHDUIACTIVATE, "DOH::RemoveMenus called (this=%x)", this);
// be extra safe and don't attempt menu merging if we're not top level
if (_fHaveParentSite)
return S_OK;
ASSERT(GetMenuItemCount(hmenuShared) != (UINT)-1);
//
// It is ok to simply remove sub-menus here.
// because ours are shared with the _hmenuBrowser
// and destroying that below will take care of cleanup.
// However, we need to only remove menus that are ours.
//
for (int i = (int)GetMenuItemCount(hmenuShared) - 1 ; i >= 0; i--)
{
// TraceMsg(0, "sdv TR - ::RemoveMenus calling RemoveMenu(0)");
HMENU hmenu = GetSubMenu(hmenuShared, i);
if (hmenu && _IsMenuShared(hmenu)) {
RemoveMenu(hmenuShared, i, MF_BYPOSITION);
}
}
// TraceMsg(0, "sdv TR - ::RemoveMenus exiting");
return S_OK;
}
//
// IOleInPlaceFrame::SetStatusText equivalent
//
HRESULT CDocObjectHost::_SetStatusText(/* [in] */ LPCOLESTR pszStatusText)
{
LPCOLESTR pszForward;
if (_psb != NULL)
{
// if it's NULL or just "" then give precedence to
// _strPriorityStatusText, otherwise we display
// whatever we're given
if (pszStatusText != NULL && pszStatusText[0] != TEXT('\0') ||
_strPriorityStatusText == NULL)
{
pszForward = pszStatusText;
}
else
{
pszForward = _strPriorityStatusText;
}
_psb->SetStatusTextSB(pszForward);
}
// Always return S_OK or scripting will put up an error dialog.
return S_OK;
}
void CDocObjectHost::_SetPriorityStatusText(LPCOLESTR pszPriorityStatusText)
{
// if they gave us a new string, replace the old one,
// otherwise just NULL out the old one
if (_strPriorityStatusText != NULL)
{
SysFreeString(_strPriorityStatusText);
}
if (pszPriorityStatusText != NULL)
{
_strPriorityStatusText = SysAllocString(pszPriorityStatusText);
}
else
{
_strPriorityStatusText = NULL;
}
_SetStatusText(_strPriorityStatusText);
}
HRESULT CDocObjectHost::_EnableModeless(/* [in] */ BOOL fEnable)
{
TraceMsg(0, "sdv TR - ::EnableModeless called");
// Note that we used call _CancelPendingNavigation here.
// We do it in CBaseBrowser:::EnableModelesSB intead. (Satona)
// Simply forwarding it (which is not implemented)
if (EVAL(_psb))
return _psb->EnableModelessSB(fEnable);
return E_FAIL;
}
HRESULT CDocObjectHost::TranslateHostAccelerators(LPMSG lpmsg)
{
if (_hacc && ::TranslateAccelerator(_hwnd, _hacc, lpmsg)) {
return S_OK;
}
return S_FALSE;
}
// IOleInPlaceFrame equivalent ::TranslateAccelerator
// Forwarding it from DocObject -> Browser
HRESULT CDocObjectHost::_TranslateAccelerator(
/* [in] */ LPMSG lpmsg,
/* [in] */ WORD wID)
{
// TranslateAccelerator goes to the guy with the focus first
if (EVAL(_psb))
if (S_OK == _psb->TranslateAcceleratorSB(lpmsg, wID))
return S_OK;
#ifdef DEBUG
if (lpmsg->message == WM_KEYDOWN) {
TraceMsg(0, "CDocObjectHost::TrAcc(UP) called");
}
#endif
return TranslateHostAccelerators(lpmsg);
}
// IViewObject
HRESULT CDocObjectHost::Draw(DWORD dwDrawAspect, LONG lindex, void *pvAspect,
DVTARGETDEVICE *ptd, HDC hicTargetDev, HDC hdcDraw,
const RECTL *lprcBounds, const RECTL *lprcWBounds,
BOOL (*pfnContinue)(ULONG_PTR), ULONG_PTR dwContinue)
{
if (_pvo && lprcBounds)
{
if (_uState == SVUIA_DEACTIVATE && _hwnd)
{
HRESULT hresT = S_OK;
RECT rcClient;
GetClientRect(_hwnd, &rcClient);
//
// We should not call SetExtent with an empty rectangle.
// It happens when we print a page with a floating frame.
//
if (rcClient.right > 0 && rcClient.bottom > 0)
{
SIZEL sizel;
sizel.cx = MulDiv( rcClient.right, 2540, GetDeviceCaps( hdcDraw, LOGPIXELSX ) );
sizel.cy = MulDiv( rcClient.bottom, 2540, GetDeviceCaps( hdcDraw, LOGPIXELSY ) );
hresT = _pole->SetExtent(DVASPECT_CONTENT, &sizel);
}
#ifdef DEBUG
MoveToEx(hdcDraw, lprcBounds->left, lprcBounds->top, NULL);
LineTo(hdcDraw, lprcBounds->right, lprcBounds->bottom);
LineTo(hdcDraw, lprcBounds->left, lprcBounds->bottom);
LineTo(hdcDraw, lprcBounds->right, lprcBounds->top);
#endif
if (hresT!=S_OK) {
TraceMsg(DM_ERROR, "CDOH::Draw SetExtent returns non S_OK %x", hresT);
}
}
return _pvo->Draw(dwDrawAspect, lindex, pvAspect, ptd, hicTargetDev,
hdcDraw, lprcBounds, lprcWBounds, pfnContinue, dwContinue);
}
return OLE_E_BLANK;
}
HRESULT CDocObjectHost::GetColorSet(DWORD dwAspect, LONG lindex,
void *pvAspect, DVTARGETDEVICE *ptd, HDC hicTargetDev,
LOGPALETTE **ppColorSet)
{
if (_pvo)
{
return _pvo->GetColorSet(dwAspect, lindex, pvAspect, ptd, hicTargetDev,
ppColorSet);
}
if (ppColorSet)
*ppColorSet = NULL;
return S_FALSE;
}
HRESULT CDocObjectHost::Freeze(DWORD, LONG, void *, DWORD *pdwFreeze)
{
if (pdwFreeze)
*pdwFreeze = 0;
return E_NOTIMPL;
}
HRESULT CDocObjectHost::Unfreeze(DWORD)
{
return E_NOTIMPL;
}
HRESULT CDocObjectHost::SetAdvise(DWORD dwAspect, DWORD advf,
IAdviseSink *pSink)
{
if (dwAspect != DVASPECT_CONTENT)
return DV_E_DVASPECT;
if (advf & ~(ADVF_PRIMEFIRST | ADVF_ONLYONCE))
return E_INVALIDARG;
if (pSink != _padvise)
{
ATOMICRELEASE(_padvise);
_padvise = pSink;
if (_padvise)
_padvise->AddRef();
}
if (_padvise)
{
_advise_aspect = dwAspect;
_advise_advf = advf;
if (advf & ADVF_PRIMEFIRST)
OnViewChange(_advise_aspect, -1);
}
else
_advise_aspect = _advise_advf = 0;
return S_OK;
}
HRESULT CDocObjectHost::GetAdvise(DWORD *pdwAspect, DWORD *padvf,
IAdviseSink **ppSink)
{
if (pdwAspect)
*pdwAspect = _advise_aspect;
if (padvf)
*padvf = _advise_advf;
if (ppSink)
{
if (_padvise)
_padvise->AddRef();
*ppSink = _padvise;
}
return S_OK;
}
// IAdviseSink
void CDocObjectHost::OnDataChange(FORMATETC *, STGMEDIUM *)
{
}
void CDocObjectHost::OnViewChange(DWORD dwAspect, LONG lindex)
{
dwAspect &= _advise_aspect;
if (dwAspect && _padvise)
{
IAdviseSink *pSink = _padvise;
IUnknown *punkRelease;
if (_advise_advf & ADVF_ONLYONCE)
{
punkRelease = pSink;
_padvise = NULL;
_advise_aspect = _advise_advf = 0;
}
else
punkRelease = NULL;
pSink->OnViewChange(dwAspect, lindex);
if (punkRelease)
punkRelease->Release();
}
}
void CDocObjectHost::OnRename(IMoniker *)
{
}
void CDocObjectHost::OnSave()
{
}
void CDocObjectHost::OnClose()
{
//
// the doc object below went away so tell our advisee something changed
//
if (_padvise)
OnViewChange(_advise_aspect, -1);
}
// IOleWindow
HRESULT CDocObjectHost::GetWindow(HWND * lphwnd)
{
*lphwnd = _hwnd;
return S_OK;
}
HRESULT CDocObjectHost::ContextSensitiveHelp(BOOL fEnterMode)
{
// NOTES: This is optional
return E_NOTIMPL; // As specified in Kraig's document (optional)
}
// IOleInPlaceSite
HRESULT CDocObjectHost::CanInPlaceActivate(void)
{
OIPSMSG(TEXT("CanInPlaceActivate called"));
return S_OK;
}
HRESULT CDocObjectHost::OnInPlaceActivate(void)
{
OIPSMSG(TEXT("OnInPlaceActivate called"));
return S_OK;
}
HRESULT CDocObjectHost::OnUIActivate( void)
{
TraceMsg(TF_SHDUIACTIVATE, "-----------------------------------");
TraceMsg(TF_SHDUIACTIVATE, "OH::OnUIActivate called (this=%x)", this);
//
// Hide Office toolbars early enough so that it won't flash.
//
_HideOfficeToolbars();
// REVIEW:
// Should we remove 'our' menu here instead?
//
// [Copied from OLE 2.01 Spec]
// The container should remove any UI associated with its own
// activation. This is significant if the container is itself
// an embedded object.
//
OIPSMSG(TEXT("OnUIActivate called"));
if (EVAL(_psb))
{
// If we had the DocObject in SVUIA_INPLACEACTIVATE send it to SVUIA_ACTIVATE_FOCUS
//
// NOTES: Unlike IE3.0, we don't call _psv->UIActivate which has a side
// effect. We just update the _uState.
//
// _psv->UIActivate(SVUIA_ACTIVATE_FOCUS);
//
_uState = SVUIA_ACTIVATE_FOCUS;
return _psb->OnViewWindowActive(_psv);
}
return E_FAIL;
}
void CDocObjectHost::_GetClipRect(RECT* prc)
{
GetClientRect(_hwnd, prc);
prc->right -= _bwTools.right;
prc->bottom -= _bwTools.bottom;
}
IOleInPlaceSite* CDocObjectHost::_GetParentSite()
{
IOleInPlaceSite* pparentsite = NULL; // the parent's inplace site
if (_pwb)
{
_pwb->GetParentSite(&pparentsite);
}
return pparentsite;
}
HRESULT CDocObjectHost::GetWindowContext(
/* [out] */ IOleInPlaceFrame **ppFrame,
/* [out] */ IOleInPlaceUIWindow **ppDoc,
/* [out] */ LPRECT lprcPosRect,
/* [out] */ LPRECT lprcClipRect,
/* [out][in] */ LPOLEINPLACEFRAMEINFO lpFrameInfo)
{
OIPSMSG(TEXT("GetWindowContext called"));
// Todo: verify that lpFrameInfo->cb is the correct size!
// TraceMsg(0, "sdv TR - ::GetWindowContext called with lpFI->cb=%d (%d)",
// lpFrameInfo->cb, sizeof(*lpFrameInfo));
*ppFrame = &_dof; AddRef();
*ppDoc = NULL; // indicating that doc window == frame window
_GetClipRect(lprcClipRect);
lpFrameInfo->fMDIApp = FALSE;
//
// If the frame has IOleInPlaceUIWindow (both IE and Shell have),
// return that hwnd as hwndFrame so that OLE's menu dispatching
// code works correctly.
//
_dof.GetWindow(&lpFrameInfo->hwndFrame);
//
// We need to find out (from SriniK or KraigB), what is the
// implecation of this accelerator. Dealing with Word, it seems that
// Word does not call our TranslateAccelerator at all, unless the key
// stroke is the accelerator. If that's the spec. (of DocObject),
// there is no way to process the accelerator of the browser.
//
lpFrameInfo->haccel = _hacc;
if (!SHRestricted(REST_NOFILEMENU))
{
#ifdef DEBUG
lpFrameInfo->cAccelEntries = DBG_ACCELENTRIES_WITH_FILEMENU; // WARNING: see shdocvw.rc, ACCELL_DOCVIEW
#else
lpFrameInfo->cAccelEntries = OPT_ACCELENTRIES_WITH_FILEMENU; // WARNING: see shdocvw.rc, ACCELL_DOCVIEW
#endif
}
else
{
#ifdef DEBUG
lpFrameInfo->cAccelEntries = DBG_ACCELENTRIES; // WARNING: see shdocvw.rc, ACCELL_DOCVIEW
#else
lpFrameInfo->cAccelEntries = OPT_ACCELENTRIES; // WARNING: see shdocvw.rc, ACCELL_DOCVIEW
#endif
}
*lprcPosRect = _rcView;
return S_OK;
}
HRESULT CDocObjectHost::Scroll(
/* [in] */ SIZE scrollExtant)
{
TraceMsg(0, "sdv TR - ::Scroll called");
return E_NOTIMPL; // As specified in Kraig's document
}
HRESULT CDocObjectHost::OnUIDeactivate(
/* [in] */ BOOL fUndoable)
{
TraceMsg(TF_SHDUIACTIVATE, "DOH::OnUIDeactivate called (this=%x)", this);
DEBUG_CODE( _DumpMenus(TEXT("on OnUIDeactivate"), TRUE); )
if (_hmenuSet) {
OIPSMSG(TEXT("OnUIDeactivate We need to SetMenu(NULL, NULL, NULL)"));
_SetMenu(NULL, NULL, NULL);
}
return S_OK;
}
HRESULT CDocObjectHost::OnInPlaceDeactivate( void)
{
OIPSMSG(TEXT("OnInPlaceDeactivate called"));
return S_OK;
}
HRESULT CDocObjectHost::DiscardUndoState( void)
{
TraceMsg(0, "sdv TR - ::DiscardUndoState called");
return S_OK;
}
HRESULT CDocObjectHost::DeactivateAndUndo( void)
{
TraceMsg(0, "sdv TR - ::DeactivateAndUndo called");
return S_OK;
}
HRESULT CDocObjectHost::OnPosRectChange(
/* [in] */ LPCRECT lprcPosRect)
{
return E_NOTIMPL; // As specified in Kraig's document
}
HRESULT CDocObjectHost::OnInPlaceActivateEx(
/* [out] */ BOOL __RPC_FAR *pfNoRedraw,
/* [in] */ DWORD dwFlags)
{
OIPSMSG(TEXT("OnInPlaceActivateEx called"));
return S_OK;
}
HRESULT CDocObjectHost::OnInPlaceDeactivateEx(
/* [in] */ BOOL fNoRedraw)
{
OIPSMSG(TEXT("OnInPlaceDeactivateEx called"));
return S_OK;
}
HRESULT CDocObjectHost::RequestUIActivate( void)
{
if (_pWebOCInPlaceSiteEx)
return _pWebOCInPlaceSiteEx->RequestUIActivate();
else
return TRUE;
}
void CDocObjectHost::_OnNotify(LPNMHDR lpnm)
{
switch(lpnm->code) {
case TBN_BEGINDRAG:
#define ptbn ((LPTBNOTIFY)lpnm)
_OnMenuSelect(ptbn->iItem, 0, NULL);
break;
}
}
void MapAtToNull(LPTSTR psz)
{
while (*psz)
{
if (*psz == TEXT('@'))
{
LPTSTR pszNext = CharNext(psz);
*psz = 0;
psz = pszNext;
}
else
{
psz = CharNext(psz);
}
}
}
void BrowsePushed(HWND hDlg)
{
TCHAR szText[MAX_PATH];
DWORD cchText = ARRAYSIZE(szText);
TCHAR szFilter[MAX_PATH];
TCHAR szTitle[MAX_PATH];
LPITEMIDLIST pidl;
LPCITEMIDLIST pidlChild;
IShellFolder * pSF;
// load the filter and then replace all the @ characters with NULL. The end of the string will be doubly
// null-terminated
MLLoadShellLangString(IDS_BROWSEFILTER, szFilter, ARRAYSIZE(szFilter));
MapAtToNull(szFilter);
GetDlgItemText(hDlg, IDD_COMMAND, szText, ARRAYSIZE(szText));
PathUnquoteSpaces(szText);
// eliminate the "file://" stuff if necessary
if (IsFileUrlW(szText))
PathCreateFromUrl(szText, szText, &cchText, 0);
MLLoadShellLangString(IDS_TITLE, szTitle, ARRAYSIZE(szTitle));
if (GetFileNameFromBrowse(hDlg, szText, ARRAYSIZE(szText), NULL,
TEXT(".htm"), szFilter, szTitle))
{
if (SUCCEEDED(IECreateFromPath(szText, &pidl)))
{
if (SUCCEEDED(IEBindToParentFolder(pidl, &pSF, &pidlChild)))
{
HWND hWndCombo = GetDlgItem(hDlg, IDD_COMMAND);
COMBOBOXEXITEM cbexItem = {0};
cbexItem.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE;
cbexItem.pszText = szText;
cbexItem.cchTextMax = ARRAYSIZE(szText);
cbexItem.iItem = -1;
cbexItem.iImage = IEMapPIDLToSystemImageListIndex(pSF, pidlChild, &cbexItem.iSelectedImage);
INT_PTR iPosition = SendMessage(hWndCombo, CBEM_INSERTITEM, (WPARAM)0, (LPARAM)(LPVOID)&cbexItem);
SendMessage(hWndCombo, CB_SETCURSEL, (WPARAM)iPosition, (LPARAM)0);
pSF->Release();
}
ILFree(pidl);
}
else
{
PathUnquoteSpaces(szText);
SetDlgItemText(hDlg, IDD_COMMAND, szText);
}
EnableOKButtonFromID(hDlg, IDD_COMMAND);
// place the focus on OK
SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)GetDlgItem(hDlg, IDOK), TRUE);
}
}
struct SOpenDlg {
TCHAR szURL[MAX_URL_STRING];
IAddressEditBox *paebox; // Object that controls ComboBoxEx
IBandSite *pbs; // Used in AEBox Init call (used as a Connection Site)
IWinEventHandler *pweh; // Used to funnel IDD_COMMAND messages to the AEBox
};
const DWORD c_mapCtrlToContextIds[] = { 0, 0 };
const DWORD c_aRunHelpIds[] = {
IDD_ICON, NO_HELP,
IDD_PROMPT, NO_HELP,
IDD_RUNDLGOPENPROMPT, IDH_IE_RUN_COMMAND,
IDD_COMMAND, IDH_IE_RUN_COMMAND,
IDD_BROWSE, IDH_RUNBROWSE,
IDC_ASWEBFOLDER, IDH_WEB_FOLDERS_CKBOX,
0, 0
};
#ifndef UNIX
HRESULT OpenDlgOnWebFolderOK(HWND hDlg, SOpenDlg * podlg)
{
ASSERT(podlg);
HRESULT hr = S_OK;
HWND hWndOpenBox = GetDlgItem(hDlg, IDD_COMMAND);
ComboBox_GetText(hWndOpenBox, podlg->szURL, ARRAYSIZE(podlg->szURL));
PathRemoveBlanks(podlg->szURL);
// int iScheme = GetUrlScheme(podlg->szURL);
// if ((URL_SCHEME_HTTP != iScheme) &&
// (URL_SCHEME_HTTPS != iScheme))
// {
// no, we don't support that protocol!!
// WCHAR wszMessage[MAX_PATH];
// WCHAR wszTitle[MAX_PATH];
// WCHAR wszErrorMessage[MAX_PATH + MAX_URL_STRING + 1];
// MLLoadShellLangString(IDS_ERRORBADURL, wszMessage, ARRAYSIZE(wszMessage));
// MLLoadShellLangString(IDS_ERRORBADURLTITLE, wszTitle, ARRAYSIZE(wszTitle));
// wnsprintf(wszErrorMessage, ARRAYSIZE(wszErrorMessage), wszMessage, podlg->szURL);
// MessageBox(hDlg, wszErrorMessage, wszTitle, MB_OK | MB_ICONERROR);
// hr = E_FAIL;
// }
return hr;
}
#endif // UNIX
HRESULT OpenDlgOnOK(HWND hDlg, SOpenDlg * podlg)
{
ASSERT(podlg);
HRESULT hr = S_OK;
/*
Try to use newer parsing code if we have an AddressEditBox object
*/
if (podlg->paebox)
hr = podlg->paebox->ParseNow(SHURL_FLAGS_NONE);
else
{
HWND hWndOpenBox = GetDlgItem(hDlg, IDD_COMMAND);
ComboBox_GetText(hWndOpenBox, podlg->szURL, ARRAYSIZE(podlg->szURL));
PathRemoveBlanks(podlg->szURL);
}
return hr;
}
void CleanUpAutoComplete(SOpenDlg *podlg)
{
ATOMICRELEASE(podlg->paebox);
ATOMICRELEASE(podlg->pweh);
ATOMICRELEASE(podlg->pbs);
ZeroMemory((PVOID)podlg, SIZEOF(SOpenDlg));
}
BOOL_PTR CALLBACK CDocObjectHost::s_RunDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
SOpenDlg* podlg = (SOpenDlg*)GetWindowLongPtr(hDlg, DWLP_USER);
switch (uMsg)
{
case WM_DESTROY:
SHRemoveDefaultDialogFont(hDlg);
return FALSE;
case WM_INITDIALOG:
{
ASSERT(lParam);
HWND hWndOpenBox = GetDlgItem(hDlg, IDD_COMMAND);
HWND hWndEditBox = (HWND)SendMessage(hWndOpenBox, CBEM_GETEDITCONTROL, 0,0);
SetWindowLongPtr(hDlg, DWLP_USER, lParam);
podlg = (SOpenDlg *)lParam;
// cross-lang platform support
SHSetDefaultDialogFont(hDlg, IDD_COMMAND);
if (podlg->paebox)
{
if ( FAILED(podlg->paebox->Init(hWndOpenBox, hWndEditBox, AEB_INIT_DEFAULT | AEB_INIT_NOASYNC, podlg->pbs)) ||
FAILED(IUnknown_SetOwner(podlg->paebox, podlg->pbs)))
CleanUpAutoComplete(podlg);
}
#ifdef UNIX
// BUG BUG : Win32 should also verify and take this change.
// IEUNIX : Limiting text, faulting under stress test. Need to set
// this limit.
SendMessage(hWndOpenBox, CB_LIMITTEXT, CBEMAXSTRLEN-1, 0L);
#endif
EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
if (SHRestricted(REST_NORUN))
EnableWindow(GetDlgItem(hDlg, IDC_ASWEBFOLDER), FALSE); // disable open as web folder
break;
}
case WM_HELP:
SHWinHelpOnDemandWrap((HWND) ((LPHELPINFO) lParam)->hItemHandle, c_szHelpFile,
HELP_WM_HELP, (DWORD_PTR)(LPTSTR) c_aRunHelpIds);
break;
case WM_CONTEXTMENU: // right mouse click
SHWinHelpOnDemandWrap((HWND) wParam, c_szHelpFile, HELP_CONTEXTMENU,
(DWORD_PTR)(LPTSTR) c_aRunHelpIds);
break;
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wParam, lParam))
{
case IDHELP:
break;
case IDD_BROWSE:
BrowsePushed(hDlg);
break;
case IDD_COMMAND:
switch (GET_WM_COMMAND_CMD(wParam, lParam))
{
case CBN_SELCHANGE:
break;
case CBN_EDITCHANGE:
case CBN_SELENDOK:
if (podlg->pweh)
podlg->pweh->OnWinEvent(hDlg, uMsg, wParam, lParam, NULL);
EnableOKButtonFromID(hDlg, IDD_COMMAND);
break;
default:
if (podlg->pweh)
podlg->pweh->OnWinEvent(hDlg, uMsg, wParam, lParam, NULL);
break;
}
break;
case IDOK:
{
// UNIX doesn't support this checkbox -- so we don't need
// to check.
#ifndef UNIX
HWND hwndCheckBox = GetDlgItem(hDlg, IDC_ASWEBFOLDER);
if (hwndCheckBox)
{
LRESULT lrState = SendMessage(hwndCheckBox, BM_GETCHECK,
0, 0);
if (lrState == BST_CHECKED)
{
if (SUCCEEDED(OpenDlgOnWebFolderOK(hDlg, podlg)))
EndDialog(hDlg, IDC_ASWEBFOLDER);
break;
}
else
#endif
if (FAILED(OpenDlgOnOK(hDlg, podlg)))
break;
#ifndef UNIX
}
#endif
}
// Fall through to IDCANCEL to close dlg
case IDCANCEL:
EndDialog(hDlg, GET_WM_COMMAND_ID(wParam, lParam));
break;
default:
return FALSE;
}
break;
default:
return FALSE;
}
return TRUE;
}
void CDocObjectHost::_Navigate(LPCWSTR pwszURL)
{
IWebBrowser2* pwb2;
if (SUCCEEDED(IUnknown_QueryService(_psb, SID_SContainerDispatch, IID_IWebBrowser2, (void **)&pwb2)))
{
//
// HACK: We are not passing BSTR, but LPWSTR, which
// will work as far as IWebBrowser2 can handle
// NULL-terminated string correctly.
//
pwb2->Navigate((BSTR)pwszURL, NULL, NULL, NULL, NULL);
pwb2->Release();
}
}
HRESULT CDocObjectHost::_PrepFileOpenAddrBand(IAddressEditBox ** ppaeb, IWinEventHandler ** ppweh, IBandSite ** ppbs)
{
HRESULT hr;
*ppaeb = NULL;
*ppweh = NULL;
*ppbs = NULL;
// If our CoCreateInstance fails, s_rundlgproc will know because paebox
// will be NULL
hr = CoCreateInstance(CLSID_AddressEditBox, NULL, CLSCTX_INPROC_SERVER, IID_IAddressEditBox, (void **)ppaeb);
if (EVAL(SUCCEEDED(hr)))
{
IServiceProvider *pspT;
hr = (*ppaeb)->QueryInterface(IID_IWinEventHandler, (void **)ppweh);
// Travel up the object hierarchy, and obtain the same pointer that
// the address bar was ::Init'ed with
// WARNING: This is not optional. The addressband will fault if this fails.
if (EVAL(SUCCEEDED(hr) && _psp))
{
hr = _psp->QueryService(SID_SExplorerToolbar, IID_IServiceProvider, (void **)&pspT);
// In framed cases, CBaseBrowser2::QueryService() will filter out SID_SExplorerToolbar
// because it's afraid of Toolbars appearing in the frame. We won't have that problem,
// so we may need to go the TopLevelBrowser first and then ask around there.
if (FAILED(hr))
{
IServiceProvider *pspT2;
hr = _psp->QueryService(SID_STopLevelBrowser, IID_IServiceProvider, (void **)&pspT2);
if (EVAL(SUCCEEDED(hr)))
{
hr = pspT2->QueryService(SID_SExplorerToolbar, IID_IServiceProvider, (void **)&pspT);
pspT2->Release();
}
}
if (EVAL(SUCCEEDED(hr)))
{
if (EVAL(SUCCEEDED(hr = pspT->QueryService(IID_IBandSite, IID_IBandSite, (void **)ppbs))))
{
IDeskBand *pdbT;
// Had to include "ITBAR.H" to access CBIDX_ADDDRESS
// HACKHACK
#define CBIDX_ADDRESS 4
// If any of the following fails, I don't care because the MRU can be out of
// synch.
if (SUCCEEDED((*ppbs)->QueryBand(CBIDX_ADDRESS, &pdbT, NULL, NULL, 0)))
{
IUnknown_Exec(pdbT, &CGID_AddressEditBox, AECMDID_SAVE, 0, NULL, NULL);
pdbT->Release();
}
}
pspT->Release();
}
}
}
if (FAILED(hr))
{
ATOMICRELEASE(*ppaeb);
ATOMICRELEASE(*ppweh);
ATOMICRELEASE(*ppbs);
}
return hr;
}
void CDocObjectHost::_OnOpen(void)
{
HWND hwndFrame;
SOpenDlg odlg ={0};
_psb->GetWindow(&hwndFrame);
if (SHIsRestricted2W(_hwnd, REST_NoFileOpen, NULL, 0))
return;
if (EVAL(SUCCEEDED(_PrepFileOpenAddrBand(&(odlg.paebox), &odlg.pweh, &odlg.pbs))))
{
// TODO: Make it a helper member, which notifies up and down.
_psb->EnableModelessSB(FALSE);
#ifdef UNIX
UINT iRet;
if (TRUE || MwCurrentLook() == LOOK_MOTIF)
iRet = DialogBoxParam(MLGetHinst(),
MAKEINTRESOURCE(DLG_RUNMOTIF),
hwndFrame,
s_RunDlgProc,
(LPARAM)&odlg);
else
iRet = DialogBoxParam(MLGetHinst(),
MAKEINTRESOURCE(DLG_RUN),
hwndFrame,
s_RunDlgProc,
(LPARAM)&odlg);
#else
INT_PTR iRet = DialogBoxParam(MLGetHinst(), MAKEINTRESOURCE(DLG_RUN), hwndFrame, s_RunDlgProc, (LPARAM)&odlg);
#endif /* UNIX */
_psb->EnableModelessSB(TRUE);
if (iRet==IDOK)
{
if (g_dwStopWatchMode) // Perf mode to mark start time
StopWatch_MarkSameFrameStart(hwndFrame);
if (odlg.paebox)
odlg.paebox->Execute(SHURL_EXECFLAGS_NONE);
else
_Navigate(odlg.szURL);
}
#ifndef UNIX
if (iRet == IDC_ASWEBFOLDER)
{
BSTR bstrUrl = SysAllocString(odlg.szURL);
if (bstrUrl != NULL)
{
_NavigateFolder(bstrUrl);
SysFreeString(bstrUrl);
}
}
#endif /*!UNIX*/
IUnknown_SetOwner(odlg.paebox, NULL);
}
// Cleanup ref counts
CleanUpAutoComplete(&odlg);
}
void CDocObjectHost::_OnClose() const
{
ASSERT(_pwb);
if (!_HideBrowserBar())
{
HRESULT hr = E_FAIL;
HWND hwnd = NULL;
IOleWindow * pOleWindow;
if ( _pwb )
{
hr = _pwb->QueryInterface(IID_PPV_ARG(IOleWindow, &pOleWindow));
}
if (SUCCEEDED(hr))
{
pOleWindow->GetWindow(&hwnd);
pOleWindow->Release();
if ( hwnd )
{
PostMessage(hwnd, WM_CLOSE, 0, 0);
}
}
}
}
BOOL CDocObjectHost::_HideBrowserBar() const
{
ASSERT(_pwb);
// Get the proxy browser. We only have
// a proxy browser if we are in a band.
//
IShellBrowser * pPrxyBrowser;
HRESULT hr = IUnknown_QueryService(_pwb,
SID_SProxyBrowser,
IID_IShellBrowser,
(void**)&pPrxyBrowser);
if (SUCCEEDED(hr))
{
hr = IUnknown_ShowBrowserBar(_psb, CLSID_SearchBand, FALSE);
}
return (SUCCEEDED(hr) ? TRUE : FALSE);
}
void CDocObjectHost::_OnImportExport(HWND hwnd)
{
RunImportExportFavoritesWizard(hwnd);
}
UINT_PTR CALLBACK DocHostSaveAsOFNHook(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_INITDIALOG:
{
/* Hide the "Save as Type" text box */
CommDlg_OpenSave_HideControl(GetParent(hDlg), stc2);
/* Hide the listbox with save type extensions */
CommDlg_OpenSave_HideControl(GetParent(hDlg), cmb1);
/* Hide the Open as read-only control */
CommDlg_OpenSave_HideControl(GetParent(hDlg), chx1);
break;
}
default:
break;
}
return FALSE;
}
#define IDS_HELPURL_SUPPORT IDS_HELPMSWEB+4
#define SZ_REGKEY_HELPURL_OVERRIDE TEXT("Software\\Microsoft\\Internet Explorer\\Help_Menu_URLs")
#define SZ_REGVAL_HELPURL_SUPPORT TEXT("Online_Support")
#define SZ_REGVAL_HELPURL_TEMPLATE TEXT("%d")
void CDocObjectHost::_OnHelpGoto(UINT idRes)
{
HRESULT hr = E_FAIL;
WCHAR szURL[MAX_PATH]; // this is enough for our own
// First try to get a copy from the registry because this is where Admins (with the IEAK) over ride
// our default values.
// We special case the Online_Support URL because it was supported in IE3.
if (IDS_HELPURL_SUPPORT == idRes)
{
hr = URLSubRegQuery(SZ_REGKEY_HELPURL_OVERRIDE, SZ_REGVAL_HELPURL_SUPPORT, TRUE, szURL, ARRAYSIZE(szURL), URLSUB_ALL);
}
else
{
WCHAR szValue[MAX_PATH];
wnsprintfW(szValue, ARRAYSIZE(szValue), SZ_REGVAL_HELPURL_TEMPLATE, (idRes - IDS_HELPMSWEB + 1));
hr = URLSubRegQuery(SZ_REGKEY_HELPURL_OVERRIDE, szValue, TRUE, szURL, ARRAYSIZE(szURL), URLSUB_ALL);
}
if (FAILED(hr))
hr = URLSubLoadString(NULL, idRes, szURL, ARRAYSIZE(szURL), URLSUB_ALL);
if (SUCCEEDED(hr))
{
_Navigate(szURL);
}
}
STDAPI_(void) IEAboutBox( HWND hWnd );
// WM_COMMAND from _WndProc - execs are going down
void CDocObjectHost::_OnCommand(UINT wNotify, UINT id, HWND hwndControl)
{
if (_ShouldForwardMenu(WM_COMMAND, MAKEWPARAM(id, wNotify), (LPARAM)hwndControl))
{
_ForwardObjectMsg(WM_COMMAND, MAKEWPARAM(id, wNotify), (LPARAM)hwndControl);
return;
}
switch(id)
{
case DVIDM_HELPTUTORIAL:
_OnHelpGoto(IDS_HELPTUTORIAL);
break;
// ShabbirS (980917): BugFix# 34259 - Repair IE option.
case DVIDM_HELPREPAIR:
RepairIE();
break;
case DVIDM_HELPABOUT:
IEAboutBox( _hwnd );
break;
case DVIDM_HELPSEARCH:
{
#ifdef UNIX
ContentHelp(_psb);
break;
#else
uCLSSPEC ucs;
QUERYCONTEXT qc = { 0 };
ucs.tyspec = TYSPEC_CLSID;
ucs.tagged_union.clsid = CLSID_IEHelp;
HRESULT hres = FaultInIEFeature(_hwnd, &ucs, &qc, FIEF_FLAG_FORCE_JITUI);
if ( SUCCEEDED( hres ) )
{
// MLHtmlHelp runs on a separate thread and should therefore be
// safe against the kinds of message loops problems indicated above
ULONG_PTR uCookie = 0;
SHActivateContext(&uCookie);
MLHtmlHelp(_hwnd, TEXT("iexplore.chm > iedefault"), HH_DISPLAY_TOPIC, 0, ML_CROSSCODEPAGE);
if (uCookie)
{
SHDeactivateContext(uCookie);
}
}
else
{
TraceMsg( TF_WARNING, "CDocObjectHost::_OnCommand() - FaultInIEFeature() Failed with hr=0x%x!", hres );
}
break;
#endif
}
case DVIDM_DHFAVORITES:
_pmsoctBrowser->Exec(&CGID_Explorer, SBCMDID_ADDTOFAVORITES, OLECMDEXECOPT_PROMPTUSER, NULL, NULL);
break;
case DVIDM_GOHOME:
case DVIDM_GOSEARCH:
{
TCHAR szPath[MAX_URL_STRING];
LPITEMIDLIST pidl;
HRESULT hres = SHDGetPageLocation(_hwnd,
(id==DVIDM_GOSEARCH) ? IDP_SEARCH : IDP_START,
szPath, ARRAYSIZE(szPath), &pidl);
if (SUCCEEDED(hres))
{
_psb->BrowseObject(pidl, SBSP_ABSOLUTE | SBSP_SAMEBROWSER);
ILFree(pidl);
}
else
{
TCHAR szMessage[256];
BOOL fSuccess = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
NULL, hres, 0, szMessage, ARRAYSIZE(szMessage), (va_list *)&szPath);
if (!fSuccess)
szMessage[0] = 0;
MLShellMessageBox(_hwnd,
MAKEINTRESOURCE(IDS_CANTACCESSDOCUMENT),
szPath, MB_OK | MB_SETFOREGROUND | MB_ICONSTOP, szMessage);
}
}
break;
case DVIDM_STOPDOWNLOAD:
// We need to tell the container to cancel a pending navigation
// if any. Notice that the Cancel button serves for two purposes:
// (1) canceling a pending navigation
// (2) cancel any downloading
if (_psb)
_CancelPendingNavigation(FALSE);
goto TryDocument;
case DVIDM_NEWWINDOW:
// make sure the top level browser gets cloned, not an explorer bar
IShellBrowser* psbTop;
if (!SHIsRestricted2W(_hwnd, REST_NoOpeninNewWnd, NULL, 0) &&
!SHIsRestricted2W(_hwnd, REST_NoFileNew, NULL, 0) &&
EVAL(SUCCEEDED(_psp->QueryService(SID_STopLevelBrowser, IID_IShellBrowser, (void **)&psbTop))) && psbTop)
{
BOOL fHandled = FALSE;
if (_pWebOCUIHandler)
{
// Give the WebOC host a chance to handle this command, since it may
// not want an IE window to be created
if (S_OK == IUnknown_Exec(_pWebOCUIHandler, &CGID_DocHostCommandHandler,
IDM_NEW_TOPLEVELWINDOW, 0, NULL, NULL))
{
fHandled = TRUE;
}
}
if (!fHandled)
{
// tell the top level browser to save its window size to the registry so
// that our new window can pick it up and cascade properly
IUnknown_Exec(psbTop, &CGID_Explorer, SBCMDID_SUGGESTSAVEWINPOS, 0, NULL, NULL);
psbTop->BrowseObject(&s_idNull, SBSP_RELATIVE|SBSP_NEWBROWSER);
psbTop->Release();
}
}
break;
case DVIDM_OPEN:
_OnOpen();
break;
case DVIDM_SAVE:
if (!SHIsRestricted2W(_hwnd, REST_NoBrowserSaveAs, NULL, 0))
{
// There's a scenario where the XML MimeViewer, when aggregating Trident, answers "disabled" to
// OLECMDID_SAVE. However, since we don't call QueryStatus on the ACCELERATOR key sequence,
// we end up calling into Trident's IPersistFile::Save(), overwriting the XML file with the converted
// HTML.
//
// The right thing to do is to QS and beep if disabled.
//
if (_dwAppHack & BROWSERFLAG_MSHTML)
{
if (_pmsot)
{
OLECMD rgcmds[] = { { OLECMDID_SAVE, 0 }, };
_pmsot->QueryStatus(NULL, ARRAYSIZE(rgcmds), rgcmds, NULL);
if (!(rgcmds[0].cmdf & OLECMDF_ENABLED))
{
// If SAVE is disabled, provide user feedback.
MessageBeep(0);
break;
}
}
}
_OnSave();
}
break;
case DVIDM_DESKTOPSHORTCUT:
IUnknown_Exec(_psb, &CGID_Explorer, SBCMDID_CREATESHORTCUT, 0, NULL, NULL);
break;
case DVIDM_SENDPAGE:
IUnknown_Exec(_psb, &CGID_Explorer, SBCMDID_SENDPAGE, 0, NULL, NULL);
break;
case DVIDM_SENDSHORTCUT:
IUnknown_Exec(_psb, &CGID_Explorer, SBCMDID_SENDSHORTCUT, 0, NULL, NULL);
break;
case DVIDM_NEWMESSAGE:
#ifdef UNIX
if ( OEHandlesMail() )
_UnixSendDocToOE(NULL, 0, MAIL_ACTION_SEND);
else
SendDocToMailRecipient(NULL, 0, MAIL_ACTION_SEND);
#else
if (FAILED(DropOnMailRecipient(NULL, 0)))
{
SHRunIndirectRegClientCommand(_hwnd, NEW_MAIL_DEF_KEY);
}
#endif
break;
case DVIDM_NEWPOST:
#ifdef UNIX
if (!CheckAndExecNewsScript(_hwnd))
#endif
SHRunIndirectRegClientCommand(_hwnd, NEW_NEWS_DEF_KEY);
break;
case DVIDM_NEWCONTACT:
SHRunIndirectRegClientCommand(_hwnd, NEW_CONTACTS_DEF_KEY);
break;
case DVIDM_NEWAPPOINTMENT:
SHRunIndirectRegClientCommand(_hwnd, NEW_APPOINTMENT_DEF_KEY);
break;
case DVIDM_NEWMEETING:
SHRunIndirectRegClientCommand(_hwnd, NEW_MEETING_DEF_KEY);
break;
case DVIDM_NEWTASK:
SHRunIndirectRegClientCommand(_hwnd, NEW_TASK_DEF_KEY);
break;
case DVIDM_NEWTASKREQUEST:
SHRunIndirectRegClientCommand(_hwnd, NEW_TASKREQUEST_DEF_KEY);
break;
case DVIDM_NEWJOURNAL:
SHRunIndirectRegClientCommand(_hwnd, NEW_JOURNAL_DEF_KEY);
break;
case DVIDM_NEWNOTE:
SHRunIndirectRegClientCommand(_hwnd, NEW_NOTE_DEF_KEY);
break;
case DVIDM_CALL:
SHRunIndirectRegClientCommand(_hwnd, NEW_CALL_DEF_KEY);
break;
case DVIDM_SAVEASFILE:
//
// Handle the case where DocObject does not support "SaveAs"
// and we have enabled the menuitem anyway.
//
if (SHIsRestricted2W(_hwnd, REST_NoBrowserSaveAs, NULL, 0))
break;
if (_pmsot)
{
OLECMD rgcmds[] = { { OLECMDID_SAVEAS, 0 }, };
_pmsot->QueryStatus(NULL, ARRAYSIZE(rgcmds), rgcmds, NULL);
ASSERT(rgcmds[0].cmdf & OLECMDF_ENABLED);
if (!(rgcmds[0].cmdf & OLECMDF_ENABLED))
_OnSaveAs();
else
goto TryDocument;
}
break;
case DVIDM_IMPORTEXPORT:
_OnImportExport(_hwnd);
break;
default:
if (IsInRange(id, DVIDM_HELPMSWEB, DVIDM_HELPMSWEBLAST))
{
#ifndef UNIX
if (id == FCIDM_HELPNETSCAPEUSERS)
SHHtmlHelpOnDemandWrap(_hwnd, TEXT("iexplore.chm > iedefault"), HH_DISPLAY_TOPIC, (DWORD_PTR) TEXT("lvg_nscp.htm"), ML_CROSSCODEPAGE);
else
#else
if (id == FCIDM_HELPNETSCAPEUSERS)
UnixHelp(L"Netscape User Help", _psb);
else
#endif
_OnHelpGoto(IDS_HELPMSWEB + (id - DVIDM_HELPMSWEB));
}
else if (IsInRange(id, DVIDM_MSHTML_FIRST, DVIDM_MSHTML_LAST))
{
TraceMsg(DM_PREMERGEDMENU, "Processing merged menuitem %d", id - DVIDM_MSHTML_FIRST);
ASSERT(_pcmdMergedMenu);
if (_pcmdMergedMenu) {
HRESULT hresT=_pcmdMergedMenu->Exec(&CGID_MSHTML, id - DVIDM_MSHTML_FIRST, 0, NULL, NULL);
if (FAILED(hresT)) {
TraceMsg(DM_ERROR, "CDOH::_OnCommand _pcmdMergedMenu->Exec(%d) failed %x",
id - DVIDM_MSHTML_FIRST, hresT);
}
}
}
else if (IsInRange (id, DVIDM_MENUEXT_FIRST, DVIDM_MENUEXT_LAST))
{
// Menu Extensions
IUnknown_Exec(_pBrowsExt, &CLSID_ToolbarExtButtons, id, 0, NULL, NULL);
}
else
{
TryDocument:
if (_pmsot)
{
// Check if we need to call object's Exec.
UINT idMso = _MapToMso(id);
if (idMso != (UINT)-1)
{
// Yes. Call it.
_pmsot->Exec(NULL, idMso, OLECMDEXECOPT_PROMPTUSER, NULL, NULL);
}
else if (id == DVIDM_PRINTFRAME)
{
_pmsot->Exec(&CGID_ShellDocView, SHDVID_PRINTFRAME, OLECMDEXECOPT_PROMPTUSER, NULL, NULL);
}
}
}
break;
}
}
HRESULT CDocObjectHost::_OnSaveAs(void)
{
HRESULT hres = S_OK;
TraceMsg(DM_SAVEASHACK, "DOH::_OnSaveAs called");
ASSERT(_pole);
if (_dwAppHack & BROWSERFLAG_MSHTML)
{
SaveBrowserFile( _hwnd, _pole );
}
else // old dochost stuff
{
TCHAR szSaveTo[MAX_PATH]; // ok with MAX_PATH
MLLoadString(IDS_DOCUMENT, szSaveTo, ARRAYSIZE(szSaveTo));
TCHAR szDesktop[MAX_PATH];
SHGetSpecialFolderPath(_hwnd, szDesktop, CSIDL_DESKTOPDIRECTORY, FALSE);
OPENFILENAME OFN;
OFN.lStructSize = sizeof(OPENFILENAME);
OFN.hwndOwner = _hwnd;
OFN.lpstrFileTitle = 0;
OFN.nMaxCustFilter = 0;
OFN.nFilterIndex = 0;
OFN.nMaxFile = ARRAYSIZE(szSaveTo);
OFN.lpfnHook = DocHostSaveAsOFNHook;
OFN.Flags = 0L;/* for now, since there's no readonly support */
OFN.lpstrTitle = NULL;
OFN.lpstrInitialDir = szDesktop;
OFN.lpstrFile = szSaveTo;
OFN.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_ENABLEHOOK | OFN_EXPLORER |
OFN_NOREADONLYRETURN | OFN_PATHMUSTEXIST;
OFN.lpstrFilter = NULL;
OFN.lpstrCustomFilter = NULL;
OFN.lpstrDefExt = TEXT(""); // no extension
TCHAR szValue[MAX_PATH+1]; // +1 for for double-null
TCHAR szExt[40];
HKEY hkey = _GetUserCLSIDKey(_pole, NULL, NULL);
if (hkey)
{
LONG cb = SIZEOF(szValue);
if (RegQueryValue(hkey, TEXT("DefaultExtension"), szValue, &cb) == ERROR_SUCCESS)
{
TraceMsg(DM_SAVEASHACK, "DOH::_OnSaveAs DefExt is %s", szValue);
// It is suposed to be like ".xls, Excel Workbook (*.xls)"
if (szValue[0]==TEXT('.')) {
StrCpyN(szExt, szValue+1, ARRAYSIZE(szExt));
LPTSTR pszEnd = StrChr(szExt, TEXT(','));
if (pszEnd) {
*pszEnd = 0;
}
OFN.lpstrDefExt = szExt;
OFN.lpstrFilter = szValue;
OFN.Flags &= ~OFN_ENABLEHOOK;
TraceMsg(DM_SAVEASHACK, "DOH::_OnSaveAs OFN.lpstrDefExt is %s", OFN.lpstrDefExt);
}
}
}
if (GetSaveFileName(&OFN))
{
IPersistFile* ppf;
ASSERT(_pole);
hres = _pole->QueryInterface(IID_IPersistFile, (void **)&ppf);
if (SUCCEEDED(hres))
{
TraceMsg(DM_APPHACK, "APPHACK DOH SaveAs'ing to %s", szSaveTo);
hres = ppf->Save(szSaveTo, FALSE);
ppf->Release();
}
else
{
ASSERT(0);
}
}
else
{
hres = S_FALSE;
}
}
return hres;
}
#ifndef POSTPOSTSPLIT
HRESULT DropOnMailRecipient(IDataObject *pdtobj, DWORD grfKeyState)
{
IDropTarget *pdrop;
HRESULT hres = CoCreateInstance(CLSID_MailRecipient,
NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
IID_IDropTarget, (void**)&pdrop);
if (SUCCEEDED(hres))
{
hres = SimulateDrop(pdrop, pdtobj, grfKeyState, NULL, NULL);
pdrop->Release();
}
return hres;
}
HRESULT SendDocToMailRecipient(LPCITEMIDLIST pidl, UINT uiCodePage, DWORD grfKeyState)
{
#ifndef UNIX
IDataObject *pdtobj;
HRESULT hres = GetDataObjectForPidl(pidl, &pdtobj);
if (SUCCEEDED(hres))
{
IQueryCodePage * pQcp;
if (SUCCEEDED(pdtobj->QueryInterface(IID_IQueryCodePage, (void **)&pQcp)))
{
pQcp->SetCodePage(uiCodePage);
pQcp->Release();
}
hres = DropOnMailRecipient(pdtobj, grfKeyState);
pdtobj->Release();
}
return hres;
#else
return UnixSendDocToMailRecipient(pidl, uiCodePage, grfKeyState);
#endif
}
#endif
void _EnableRemoveMenuItem(HMENU hmenu, DWORD cmdf, UINT uCmd)
{
if (!(cmdf & (OLECMDF_SUPPORTED | OLECMDF_ENABLED)))
RemoveMenu(hmenu, uCmd, MF_BYCOMMAND);
else
_EnableMenuItem(hmenu, uCmd,
cmdf & OLECMDF_ENABLED);
}
void CDocObjectHost::_OnInitMenuPopup(HMENU hmInit, int nIndex, BOOL fSystemMenu)
{
if (!_hmenuCur)
return;
DEBUG_CODE( _DumpMenus(TEXT("on _OnInitMenuPopup"), TRUE); )
if (GetMenuFromID(_hmenuCur, FCIDM_MENU_VIEW) == hmInit) {
OLECMD rgcmd1[] = {
{ IDM_SCRIPTDEBUGGER, 0 },
};
DeleteMenu (hmInit, DVIDM_MSHTML_FIRST+IDM_SCRIPTDEBUGGER, MF_BYCOMMAND);
if (SUCCEEDED(QueryStatusDown(&CGID_MSHTML, ARRAYSIZE(rgcmd1), rgcmd1, NULL)) && (rgcmd1[0].cmdf & OLECMDF_ENABLED)) {
//
// We need the script debugger popup menu. We should check to see if this
// needs to be loaded.
//
HMENU hMenuDebugger;
MENUITEMINFO mii;
const UINT cchBuf = 128;
TCHAR szItem[cchBuf];
hMenuDebugger = LoadMenu(MLGetHinst(), MAKEINTRESOURCE(MENU_SCRDEBUG));
mii.fMask = MIIM_SUBMENU | MIIM_TYPE | MIIM_ID;
mii.fType = MFT_STRING;
mii.cch = cchBuf;
mii.dwTypeData = szItem;
mii.cbSize = sizeof(mii);
GetMenuItemInfo(hMenuDebugger, 0, TRUE, &mii);
mii.fMask |= MIIM_STATE;
mii.fState = MFS_ENABLED;
InsertMenuItem(hmInit, FCIDM_THEATER, FALSE, &mii);
OLECMD rgcmd[] = {
{ IDM_BREAKATNEXT, 0 },
{ IDM_LAUNCHDEBUGGER, 0 },
};
HRESULT hr = QueryStatusDown(&CGID_MSHTML, ARRAYSIZE(rgcmd), rgcmd, NULL);
_EnableMenuItem(mii.hSubMenu, DVIDM_MSHTML_FIRST+rgcmd[0].cmdID, SUCCEEDED(hr) && (rgcmd[0].cmdf & OLECMDF_ENABLED));
_EnableMenuItem(mii.hSubMenu, DVIDM_MSHTML_FIRST+rgcmd[1].cmdID, SUCCEEDED(hr) && (rgcmd[1].cmdf & OLECMDF_ENABLED));
}
if (_pmsot)
{
OLECMD rgcmd2[] = {
{ IDM_VIEWSOURCE, 0 },
};
if (SHRestricted2(REST_NoViewSource, NULL, 0) == 0)
{
// we only want to modify the state of the view source item
// if it isn't restricted by the IEAK. if it's restricted, we
// need to leave it disabled regardles of what the object
// we're hosting says
HRESULT hr = _pmsot->QueryStatus(&CGID_MSHTML, ARRAYSIZE(rgcmd2), rgcmd2, NULL);
_EnableMenuItem(hmInit, DVIDM_MSHTML_FIRST + rgcmd2[0].cmdID,
SUCCEEDED(hr) && (rgcmd2[0].cmdf & OLECMDF_ENABLED));
}
}
}
else if (GetMenuFromID(_hmenuCur, FCIDM_MENU_FILE) == hmInit)
{
if (_pmsot)
{
TraceMsg(0, "sdv TR _OnInitMenuPopup : step 5");
OLECMD rgcmds[] = {
{ OLECMDID_PRINT, 0 },
{ OLECMDID_PAGESETUP, 0 },
{ OLECMDID_PROPERTIES, 0 },
{ OLECMDID_SAVE, 0 },
{ OLECMDID_SAVEAS, 0 },
{ OLECMDID_PRINTPREVIEW, 0 },
};
_pmsot->QueryStatus(NULL, ARRAYSIZE(rgcmds), rgcmds, NULL);
// Adding a comment for my sanity: we use SHDVID_PRINTFRAME instead
// of OLECMDID_PRINT because IE40 is going to support the printing
// of entire framesets, instead of the current behavior or forwarding
// the command to the active frame.
//
OLECMD rgcmds1[] = {
{ SHDVID_PRINTFRAME, 0 },
};
_pmsot->QueryStatus(&CGID_ShellDocView, ARRAYSIZE(rgcmds1), rgcmds1, NULL);
//
// If OLECMDID_SAVEAS is not supported (neither ENABLED nor
// SUPPORTED is set) by the DocObject, check if the object
// support IPersistFile. If it does, enable it. Note that
// this mechanism allows the DocObject to disable this menu
// item (by setting only OLECMDF_SUPPORTED). (SatoNa)
//
ASSERT(rgcmds[4].cmdID == OLECMDID_SAVEAS);
// Only apply the save as restriction to the browser. If it is the
// browser, and save as is restricted, then make the item disappear.
if ( (_dwAppHack & BROWSERFLAG_MSHTML) &&
SHRestricted2( REST_NoBrowserSaveAs, NULL, 0 ))
rgcmds[4].cmdf &= ~(OLECMDF_ENABLED | OLECMDF_SUPPORTED);
else if (!(rgcmds[4].cmdf & (OLECMDF_ENABLED | OLECMDF_SUPPORTED)))
{
IPersistFile* ppf;
ASSERT(_pole);
HRESULT hresT = _pole->QueryInterface(IID_IPersistFile, (void **)&ppf);
if (SUCCEEDED(hresT))
{
TraceMsg(DM_APPHACK, "APPHACK DOH Enabling SaveAs menu for Excel95");
rgcmds[4].cmdf |= OLECMDF_ENABLED;
ppf->Release();
}
}
if (SHRestricted2(REST_NoPrinting, NULL, 0))
{
rgcmds[0].cmdf &= ~(OLECMDF_ENABLED | OLECMDF_SUPPORTED); // print
rgcmds[1].cmdf &= ~(OLECMDF_ENABLED | OLECMDF_SUPPORTED); // page setup
rgcmds[5].cmdf &= ~(OLECMDF_ENABLED | OLECMDF_SUPPORTED); // print preview
}
//
// APPHACK: Office apps do not enable "Save" correctly.
// Automatically enable it if the moniker is a FILE moniker
// AND the document has been altered by the user.
//
if (_fFileProtocol && _IsDirty(NULL))
{
if (!(rgcmds[3].cmdf & OLECMDF_ENABLED))
{
TraceMsg(DM_APPHACK, "APPHACK DOH Enabling Save for Office Apps");
}
rgcmds[3].cmdf |= OLECMDF_ENABLED;
}
// Remove/disable/enable the "Print" command as appropriate.
// Excel doesn't set SUPPORTED bit when it sets ENABLED bit
// so we have to check both bits.
_EnableRemoveMenuItem(hmInit, rgcmds[0].cmdf, DVIDM_PRINT);
_EnableMenuItem(hmInit, DVIDM_PAGESETUP,
(rgcmds[1].cmdf & OLECMDF_ENABLED));
_EnableMenuItem(hmInit, DVIDM_PROPERTIES,
(rgcmds[2].cmdf & OLECMDF_ENABLED));
_EnableRemoveMenuItem(hmInit, rgcmds[3].cmdf, DVIDM_SAVE);
_EnableRemoveMenuItem(hmInit, rgcmds[4].cmdf, DVIDM_SAVEASFILE);
_EnableRemoveMenuItem(hmInit, rgcmds[5].cmdf, DVIDM_PRINTPREVIEW);
_EnableRemoveMenuItem(hmInit, rgcmds1[0].cmdf, DVIDM_PRINTFRAME);
HMENU hmFileNew = SHGetMenuFromID(hmInit, DVIDM_NEW);
if (hmFileNew)
{
const static struct {
LPCTSTR pszClient;
UINT idCmd;
} s_Clients[] = {
{ NEW_MAIL_DEF_KEY, DVIDM_NEWMESSAGE },
{ NEW_CONTACTS_DEF_KEY, DVIDM_NEWCONTACT },
{ NEW_NEWS_DEF_KEY, DVIDM_NEWPOST },
{ NEW_APPOINTMENT_DEF_KEY, DVIDM_NEWAPPOINTMENT },
{ NEW_MEETING_DEF_KEY, DVIDM_NEWMEETING },
{ NEW_TASK_DEF_KEY, DVIDM_NEWTASK },
{ NEW_TASKREQUEST_DEF_KEY, DVIDM_NEWTASKREQUEST },
{ NEW_JOURNAL_DEF_KEY, DVIDM_NEWJOURNAL },
{ NEW_NOTE_DEF_KEY, DVIDM_NEWNOTE },
{ NEW_CALL_DEF_KEY, DVIDM_CALL }
};
BOOL bItemRemoved = FALSE;
for (int i = 0; i < ARRAYSIZE(s_Clients); i++)
{
if (!SHIsRegisteredClient(s_Clients[i].pszClient))
{
if (RemoveMenu(hmFileNew, s_Clients[i].idCmd, MF_BYCOMMAND))
bItemRemoved = TRUE;
}
}
// Menu item "File/New/Window Ctrl+N" needs to be disabled if the restriction is set
if( SHRestricted2W(REST_NoOpeninNewWnd, NULL, 0))
{
EnableMenuItem(hmFileNew, DVIDM_NEWWINDOW, MF_BYCOMMAND | MF_GRAYED);
}
if (bItemRemoved) // ensure the last item is not a separator
_SHPrettyMenu(hmFileNew);
}
}
}
else if (GetMenuFromID(_hmenuCur, FCIDM_VIEWFONTS) == hmInit
|| GetMenuFromID(_hmenuCur, FCIDM_ENCODING) == hmInit)
{
if (_pmsot)
{
// Handling fonts popup in view menu
OLECMD rgcmd[] = {
{ SHDVID_GETFONTMENU, 0 },
{ SHDVID_GETMIMECSETMENU, 0 },
};
_pmsot->QueryStatus(&CGID_ShellDocView, ARRAYSIZE(rgcmd), rgcmd, NULL);
int idx = (GetMenuFromID(_hmenuCur, FCIDM_VIEWFONTS) == hmInit ? 0 : 1);
if (rgcmd[idx].cmdf & OLECMDF_ENABLED)
{
VARIANTARG v = {0};
HRESULT hr;
hr = _pmsot->Exec(&CGID_ShellDocView, rgcmd[idx].cmdID, 0, NULL, &v);
if (S_OK == hr)
{
// (on NT/Unix) DestroyMenu(hmInit) shouldn't work, because
// we're inside the processing of WM_INITMENUPOPUP message
// for hmInit. DestroyMenu will make the hmInit handle
// invalid.
//
// Instead of that we'll empty hmInit and copy hmenuFonts
// over. hmenuFonts will be destroyed to prevent the
// memory leak.
//
//
MENUITEMINFO mii;
UINT uItem = 0;
//$ WIN64: mshtml\src\site\base\formmso.cxx needs to return VT_INT_PTR instead
// HMENU hmenuFonts = (HMENU)v.byref;
HMENU hmenuFonts = (HMENU)LongToHandle(v.lVal);
#ifndef UNIX
// deleting menu while processing WM_INITMENUPOPUP
// can cause assertion failure on NT. However, copying
// submenu using InsertMenuItem() doesn't work on Win9x.
// see the comments above and Menu_Replace() in menu.cpp
//
if (!g_fRunningOnNT)
DestroyMenu(hmInit);
#endif
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_ID|MIIM_SUBMENU;
while (GetMenuItemInfo(hmenuFonts, uItem, TRUE, &mii))
{
if (idx == 1 && mii.hSubMenu != NULL)
{
UINT uItemSub = 0;
HMENU hMenuSub = mii.hSubMenu;
while (GetMenuItemInfo(hMenuSub, uItemSub, TRUE, &mii))
{
mii.wID += DVIDM_MSHTML_FIRST;
SetMenuItemInfo(hMenuSub, uItemSub++, TRUE, &mii);
}
}
else
{
mii.wID += DVIDM_MSHTML_FIRST;
SetMenuItemInfo(hmenuFonts, uItem, TRUE, &mii);
}
uItem++;
}
#ifndef UNIX
if (!g_fRunningOnNT)
{
mii.cbSize = sizeof(mii);
mii.fMask = MIIM_SUBMENU;
mii.hSubMenu = hmenuFonts;
SetMenuItemInfo(_hmenuCur,
(idx == 0 ? FCIDM_VIEWFONTS:FCIDM_ENCODING),
FALSE, &mii);
}
else
#endif
{
Menu_Replace(hmInit, hmenuFonts);
DestroyMenu(hmenuFonts);
}
}
}
}
}
else if (GetMenuFromID(_hmenuCur, FCIDM_MENU_TOOLS) == hmInit ||
GetMenuFromID(_hmenuCur, FCIDM_MENU_HELP) == hmInit)
{
// Add Tools and help Menu Extensions
if (_pBrowsExt)
{
_pBrowsExt->OnCustomizableMenuPopup(_hmenuCur, hmInit);
}
}
}
//
// ATTEMPT: Handling WM_SETFOCUS message here caused several problems
// under IE 3.0. Since we can't find any code scenario that requires
// this code, I'm yanking out. If might introduce a new bug, but dealing
// with those bugs is probably better than dealing with this code.
// (SatoNa)
//
/*----------------------------------------------------------
Purpose: Determines if this message should be forwarded onto
the object.
Returns: TRUE if the message needs to be forwarded
*/
BOOL CDocObjectHost::_ShouldForwardMenu(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_MENUSELECT:
{
// In USER menu bars, the first menuselect will be sent for the
// selected top-level menu item, in which case hmenu == _hmenuCur.
// We expect menubands to behave similarly.
//
// We check that hmenu == _hmenuCur because we only keep a list
// of the top-level popup menus. We don't keep track of any
// cascading submenus. We should only need to check who owns
// the menu at the initial popup, all subsequent messages for
// that menu should go to the same destination (frame or object).
//
// The same goes for CShellBrowser::_ShouldForwardMenu().
//
HMENU hmenu = GET_WM_MENUSELECT_HMENU(wParam, lParam);
if (hmenu && (MF_POPUP & GET_WM_MENUSELECT_FLAGS(wParam, lParam)))
{
HMENU hmenuSub = GetSubMenu(hmenu, GET_WM_MENUSELECT_CMD(wParam, lParam));
if (hmenu == _hmenuCur)
{
// Normal case, where we just look at the topmost popdown menus
_fForwardMenu = _menulist.IsObjectMenu(hmenuSub);
}
else if (_menulist.IsObjectMenu(hmenuSub))
{
// This happens if the cascading submenu (micro-merged help menu for
// example) should be forwarded on, but the parent menu should
// not.
_fForwardMenu = TRUE;
}
else if (GetMenuFromID(_hmenuCur, FCIDM_MENU_HELP) == hmenu
&& !_menulist.IsObjectMenu(hmenu) )
{
// 80430 Appcompat: notice that our menu fowarding doesn't work for the
// micro-merged Help menu. If the user previously selected the merged
// submenu, and we end up here, it means a non-merged submenu was just
// selected and our _fForwardMenu was still set to TRUE. If we don't
// reset it, the next WM_INITMENUPOPUP gets forwarded, which crashes Visio.
//
// We know that a submenu of the Help menu has just popped up, and we know
// the submenu belongs to us. So don't forward to the docobj until the
// next popup.
_fForwardMenu = FALSE;
}
}
break;
}
case WM_COMMAND:
if (_fForwardMenu)
{
// Stop forwarding menu messages after WM_COMMAND
_fForwardMenu = FALSE;
// If it wasn't from an accelerator, forward it
if (0 == GET_WM_COMMAND_CMD(wParam, lParam))
return TRUE;
}
break;
}
return _fForwardMenu;
}
/*----------------------------------------------------------
Purpose: Forwards messages to the in-place object.
This is used to forward menu messages to the object for
menu bands, since the menu bands do not work with the
standard OLE FrameFilterWndProc.
Also, the help menu is sometimes a combination of the
object and the frame. This function will forward as
appropriate.
*/
LRESULT CDocObjectHost::_ForwardObjectMsg(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT lRet = 0L;
IOleInPlaceActiveObject *piact = _xao.GetObject();
ASSERT(IS_VALID_CODE_PTR(piact, IOleInPlaceActiveObject));
if (piact)
{
HWND hwnd;
piact->GetWindow(&hwnd);
ASSERT(IS_VALID_HANDLE(hwnd, WND));
if (hwnd)
{
if (uMsg == WM_COMMAND)
PostMessage(hwnd, uMsg, wParam, lParam);
else
lRet = SendMessage(hwnd, uMsg, wParam, lParam);
}
}
return lRet;
}
void CDocObjectHost::_OnMenuSelect(UINT id, UINT mf, HMENU hmenu)
{
if (_psb)
{
if (IsInRange(id, DVIDM_MSHTML_FIRST, DVIDM_MSHTML_LAST))
{
if (_pcmdMergedMenu) {
OLECMD rgcmd = { id - DVIDM_MSHTML_FIRST, 0 };
struct {
OLECMDTEXT cmdtxt;
WCHAR szExtra[MAX_PATH];
} cmdt;
cmdt.cmdtxt.cmdtextf = OLECMDTEXTF_STATUS;
cmdt.cmdtxt.cwActual = 0;
cmdt.cmdtxt.cwBuf = MAX_PATH;
cmdt.cmdtxt.rgwz[0] = 0;
HRESULT hresT=_pcmdMergedMenu->QueryStatus(&CGID_MSHTML, 1, &rgcmd, &cmdt.cmdtxt);
if (SUCCEEDED(hresT) && cmdt.cmdtxt.rgwz[0]) {
_psb->SetStatusTextSB(cmdt.cmdtxt.rgwz);
} else {
TraceMsg(DM_ERROR, "CDOH::_OnMenuSelect QueryStatus failed %x %d",
hresT, cmdt.cmdtxt.cwActual);
}
}
else
// An ASSERT was replaced with this TraceMsg to allow testing on Win9x.
// 70240 which reported the assert was pushed to IE6.
TraceMsg(TF_WARNING, "CDocObjectHost::_OnMenuSelect _pcmdMergedMenu == NULL");
}
else if (IsInRange(id, DVIDM_MENUEXT_FIRST, DVIDM_MENUEXT_LAST))
{
// Menu Extensions go here
if (_pBrowsExt)
{
_pBrowsExt->OnMenuSelect(id);
}
}
else
{
WCHAR wszT[MAX_STATUS_SIZE];
if (MLLoadStringW(IDS_HELP_OF(id), wszT, ARRAYSIZE(wszT)))
{
_psb->SetStatusTextSB(wszT);
}
}
}
}
LRESULT CDocObjectHost::v_WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT lRet = 0L;
switch(uMsg)
{
case WM_TIMER:
if (wParam == IDTIMER_PROGRESS)
{
_OnSetProgressPos(0, PROGRESS_TICK);
break;
}
else if (wParam == IDTIMER_PROGRESSFULL)
{
_OnSetProgressPos(-2, PROGRESS_RESET);
break;
}
else
{
#ifdef TEST_DELAYED_SHOWMSOVIEW
MessageBeep(0);
KillTimer(_hwnd, 100);
ActivateMe(NULL);
break;
#else
ASSERT(FALSE);
break;
#endif // TEST_DELAYED_SHOWMSOVIEW
}
/* WM_PICS_ASYNCCOMPLETE is posted by the async thread fetching ratings
* from label bureaus, etc.
*/
case WM_PICS_ASYNCCOMPLETE:
{
TraceMsg(DM_PICS, "CDOH::v_WndProc got WM_PICS_ASYNCCOMPLETE");
PicsQuery pq;
HRESULT hr;
LPVOID lpvRatingDetails;
DWORD dwSerialComplete = (DWORD)lParam;
CPicsProcessor * pPicsProc = NULL;
if (::_GetPicsQuery(dwSerialComplete, &pq)) {
::_RemovePicsQuery(dwSerialComplete);
hr = (HRESULT)wParam;
lpvRatingDetails = pq.lpvRatingDetails;
}
else {
hr = E_FAIL;
lpvRatingDetails = NULL;
}
pPicsProc = _GetPicsProcessorFromSerialNumber(dwSerialComplete);
if (pPicsProc)
{
pPicsProc->_GotLabel(hr, lpvRatingDetails, PICS_WAIT_FOR_ASYNC);
}
else
{
TraceMsg( DM_PICS, "CDOH::v_WndProc no PicsProc for ASYNCCOMPLETE %x", dwSerialComplete);
if ( lpvRatingDetails )
{
::RatingFreeDetails(lpvRatingDetails);
lpvRatingDetails = NULL;
}
}
break;
}
case WM_PICS_ROOTDOWNLOADCOMPLETE:
{
TraceMsg(DM_PICS, "CDOH::v_WndProc got WM_PICS_ROOTDOWNLOADCOMPLETE %x", lParam);
if (_PicsProcBase._pRootDownload != NULL)
{
_PicsProcBase._pRootDownload->CleanUp();
ATOMICRELEASET(_PicsProcBase._pRootDownload,CPicsRootDownload);
}
break;
}
/* WM_PICS_ALLCHECKSCOMPLETE is posted when we finally want to either
* cancel the navigation or go through with it, according to ratings
* checks. Posting a message allows all denial blocking message loops
* to unwind before we cancel navigation, which could otherwise delete
* objects that still have functions operating on them.
*/
case WM_PICS_ALLCHECKSCOMPLETE:
TraceMsg(DM_PICS, "CDOH::v_WndProc got WM_PICS_ALLCHECKSCOMPLETE, lParam=%x", lParam);
if (lParam == IDOK) {
if (!_fSetTarget)
{
TraceMsg(DM_PICS, "CDOH::v_WndProc(WM_PICS_ASYNCCOMPLETE) execing SHDVID_ACTIVATEMENOW");
_pmsoctBrowser->Exec(&CGID_ShellDocView, SHDVID_ACTIVATEMENOW, NULL, NULL, NULL);
}
else {
TraceMsg(DM_PICS, "CDOH::v_WndProc(WM_PICS_ASYNCCOMPLETE) not execing SHDVID_ACTIVATEMENOW");
}
}
else {
ASSERT(!_fSetTarget);
TraceMsg(DM_PICS, "CDOH::v_WndProc(WM_PICS_ASYNCCOMPLETE) calling _CancelPendingNavigation");
_CancelPendingNavigation(FALSE);
// _pmsoctBrowser->Exec(NULL, OLECMDID_STOP, NULL, NULL, NULL);
}
break;
/* WM_PICS_DOBLOCKINGUI is posted when we decide we need to put up
* denial UI. Posting a message allows download of this object and
* other frames to continue while we post the UI, which in turn allows
* any denials from other frames to be coalesced into the one dialog.
*/
case WM_PICS_DOBLOCKINGUI:
{
TraceMsg(DM_PICS, "CDOH::v_WndProc got WM_PICS_DOBLOCKINGUI %x", lParam);
CPicsProcessor * pPicsProc = _GetPicsProcessorFromKey(lParam);
if (pPicsProc)
{
UINT id = pPicsProc->_PicsBlockingDialog();
if (pPicsProc->_pPrivWindow)
{
ASSERT( _fDocCanNavigate );
TraceMsg(DM_PICS, "CDOH::v_WndProc(WM_PICS_DOBLOCKINGUI) posting WM_PICS_RESULTTOPRIVWIN");
if (!PostMessage( _hwnd, WM_PICS_RESULTTOPRIVWIN, id, lParam))
{
TraceMsg(DM_PICS, "CDOH::v_WndProc(WM_PICS_DOBLOCKINGUI) couldn't post message!");
}
}
else
{
TraceMsg(DM_PICS, "CDOH::v_WndProc(WM_PICS_DOBLOCKINGUI) posting WM_PICS_ALLCHECKSCOMPLETE");
if (!PostMessage(_hwnd, WM_PICS_ALLCHECKSCOMPLETE, 0, id))
{
TraceMsg(DM_PICS, "CDOH::v_WndProc(WM_PICS_DOBLOCKINGUI) couldn't post message!");
}
}
// We may have been terminated while the
// dialog was up -- finish cleaning up here.
if ( !pPicsProc->_fInDialog
&& pPicsProc->_fTerminated
&& pPicsProc != &_PicsProcBase)
{
delete pPicsProc;
}
}
else
{
TraceMsg( DM_PICS, "CDOH::v_WndProc no PicsProc for DOBLOCKINGUI");
}
}
break;
case WM_PICS_RESULTTOPRIVWIN:
{
ASSERT( _fDocCanNavigate );
TraceMsg(DM_PICS, "CDOH::v_WndProc got WM_PICS_RESULTTOPRIVWIN %x", lParam);
CPicsProcessor * pPicsProc = _GetPicsProcessorFromKey(lParam);
if (pPicsProc)
{
ASSERT( pPicsProc->_pPrivWindow );
IHTMLPrivateWindow * pPrivWindow = pPicsProc->_pPrivWindow;
pPrivWindow->AddRef();
if (pPicsProc->_pRootDownload != NULL)
{
pPicsProc->_pRootDownload->CleanUp();
ATOMICRELEASET(pPicsProc->_pRootDownload,CPicsRootDownload);
}
// Remove ourselves from the list
_RemovePicsProcessorByKey( lParam );
// Make Trident let go of our command target
pPrivWindow->SetPICSTarget(NULL);
// Tell the window the answer
pPrivWindow->PICSComplete( wParam == IDOK );
pPrivWindow->Release();
}
else
{
TraceMsg( DM_PICS, "CDOH::v_WndProc no PicsProc for WM_PICS_RESULTTOPRIVWIN");
}
}
break;
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
MessageBeep(0);
break;
case WM_MENUSELECT:
if (_ShouldForwardMenu(uMsg, wParam, lParam))
lRet = _ForwardObjectMsg(uMsg, wParam, lParam);
else
{
UINT uMenuFlags = GET_WM_MENUSELECT_FLAGS(wParam, lParam);
WORD wID = GET_WM_MENUSELECT_CMD(wParam, lParam);
HMENU hMenu = GET_WM_MENUSELECT_HMENU(wParam, lParam);
// Check for popup menus so we can display help strings for them
if (uMenuFlags & MF_POPUP)
{
MENUITEMINFO miiSubMenu;
miiSubMenu.cbSize = SIZEOF(MENUITEMINFO);
miiSubMenu.fMask = MIIM_SUBMENU|MIIM_ID;
if (GetMenuItemInfoWrap(hMenu, wID, TRUE, &miiSubMenu))
{
// Change the parameters to simulate a "normal" menu item
wID = (WORD)miiSubMenu.wID;
}
}
_OnMenuSelect(wID, uMenuFlags, hMenu);
}
break;
case WM_INITMENUPOPUP:
if (_ShouldForwardMenu(uMsg, wParam, lParam))
lRet = _ForwardObjectMsg(uMsg, wParam, lParam);
else
_OnInitMenuPopup((HMENU)wParam, LOWORD(lParam), HIWORD(lParam));
break;
case WM_DRAWITEM:
case WM_MEASUREITEM:
if (_ShouldForwardMenu(uMsg, wParam, lParam))
lRet = _ForwardObjectMsg(uMsg, wParam, lParam);
else
goto DoDefault;
break;
case WM_NOTIFY:
_OnNotify((LPNMHDR)lParam);
break;
case WM_COMMAND:
_OnCommand(HIWORD(wParam), LOWORD(wParam), (HWND)lParam);
break;
case WM_SIZE:
if (_pmsov)
{
RECT rcClient;
GetClientRect(_hwnd, &rcClient);
//
// We should call ResizeBorder only if the browser is
// not an IOleInPlaceUIWindow.
//
if (_pipu==NULL)
{
TraceMsg(TF_SHDUIACTIVATE, "DOH::WM_SIZE calling _piact->ResizeBorder");
_xao.ResizeBorder(&rcClient, &_dof, TRUE);
}
_rcView.left = _bwTools.left;
_rcView.top = _bwTools.top;
_rcView.right = rcClient.right - _bwTools.right;
_rcView.bottom = rcClient.bottom - _bwTools.bottom;
TraceMsg(TF_SHDUIACTIVATE, "DOH::WM_SIZE calling SetRect (%d, %d, %d, %d)", _rcView.left, _rcView.top, _rcView.right, _rcView.bottom);
_pmsov->SetRect(&_rcView);
}
_PlaceProgressBar(TRUE);
break;
//
// ATTEMPT: Handling WM_SETFOCUS message here caused several problems
// under IE 3.0. Since we can't find any code scenario that requires
// this code, I'm yanking out. If might introduce a new bug, but dealing
// with those bugs is probably better than dealing with this code.
// (SatoNa)
//
case WM_PRINT:
_OnPaint((HDC)wParam);
break;
case WM_QUERYNEWPALETTE:
case WM_PALETTECHANGED:
case WM_SYSCOLORCHANGE:
case WM_DISPLAYCHANGE:
case WM_ENTERSIZEMOVE:
case WM_EXITSIZEMOVE:
{
HWND hwndT;
if (_pole && SUCCEEDED(IUnknown_GetWindow(_pole, &hwndT)) && hwndT)
return SendMessage(hwndT, uMsg, wParam, lParam);
return 0;
}
case WM_PAINT:
PAINTSTRUCT ps;
HDC hdc;
hdc = BeginPaint(_hwnd, &ps);
// we don't need them to paint into our dc...
// docobj has own hwnd.
//_OnPaint(hdc);
EndPaint(_hwnd, &ps);
break;
case WM_LBUTTONUP:
if (_uState != SVUIA_ACTIVATE_FOCUS) {
SetFocus(_hwnd);
}
break;
case WM_ERASEBKGND:
// Checking _bsc._fBinding will catch the first page case.
if (_fDrawBackground ||
(!(_dwAppHack & BROWSERFLAG_NEVERERASEBKGND)
&& ((_pmsov && _uState!=SVUIA_DEACTIVATE)
|| _bsc._fBinding)))
{
PAINTMSG("WM_ERASEBKGND", this);
goto DoDefault;
}
// Don't draw WM_ERASEBKGND if we have no view activated.
return TRUE; // TRUE = fErased
case WM_HELP:
//
// Give it to the parent to do. we need to do this in case we're hosted as a
// control
//
{
IOleCommandTarget *pcmdtTop;
if (SUCCEEDED(QueryService(SID_STopLevelBrowser, IID_IOleCommandTarget, (void **)&pcmdtTop))) {
pcmdtTop->Exec(&CGID_ShellDocView, SHDVID_HELP, 0, NULL, NULL);
pcmdtTop->Release();
}
// do nothing in failure... let the parent own completely
}
break;
case WM_WININICHANGE:
_PlaceProgressBar(TRUE);
break;
default:
// Handle the MSWheel message
if ((uMsg == GetWheelMsg()) && _pole)
{
HWND hwndT;
// If for some reason our window has focus we just need to
// swallow the message. If we don't we may create an infinite loop
// because most clients send the message to the focus window.
if (GetFocus() == _hwnd)
return 1;
//
// try to find a window to forward along to
//
if (SUCCEEDED(IUnknown_GetWindow(_pole, &hwndT)))
{
PostMessage(hwndT, uMsg, wParam, lParam);
return 1;
}
// Fall through...
}
DoDefault:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return lRet;
}
const TCHAR c_szViewClass[] = TEXT("Shell DocObject View");
void CDocObjectHost::_RegisterWindowClass(void)
{
WNDCLASS wc = {0};
wc.style = CS_PARENTDC;
wc.lpfnWndProc = s_WndProc ;
//wc.cbClsExtra = 0;
wc.cbWndExtra = SIZEOF(CDocObjectHost*);
wc.hInstance = g_hinst ;
//wc.hIcon = NULL ;
//wc.hCursor = NULL;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
//wc.lpszMenuName = NULL ;
wc.lpszClassName = c_szViewClass;
SHRegisterClass(&wc);
}
void CDocObjectHost::_InitOleObject()
{
if (!_fClientSiteSet)
{
_fClientSiteSet = TRUE;
#ifdef DEBUG
IOleClientSite * pcliT = NULL;
if (SUCCEEDED(_pole->GetClientSite(&pcliT)) && pcliT)
{
// Trident now grabs the client site from the bind context.
// We don't want to hit this assertin this case (pcliT==this).
//
AssertMsg(IsSameObject(pcliT, SAFECAST(this, IOleClientSite*)),
TEXT("CDocObjectHost::_InitOleObject _pole (%x) already has a client site (%x) (this=%x)"),
_pole, pcliT, this);
pcliT->Release();
}
#endif
HRESULT hresT = _pole->SetClientSite(this);
if (FAILED(hresT))
{
TraceMsg(TF_SHDAPPHACK, "DOH::_InitOleObject SetClientSite failed (%x). Don't in-place navigate", hresT);
_dwAppHack |= BROWSERFLAG_DONTINPLACE;
}
ASSERT(NULL==_pvo);
_pole->QueryInterface(IID_IViewObject, (void **)&_pvo);
if (_pvo)
{
TraceMsg(DM_DEBUGTFRAME, "CDocObjectHost::_InitOleObject about call SetAdvise on %x (%x)", _pole, this);
_pvo->SetAdvise(DVASPECT_CONTENT, ADVF_PRIMEFIRST, this);
}
//
// According to SteveRa (Word developer), a word object has an
// internal flag which indicates whether or not it is created
// from a file. If that flag is set, UIActivate will open the
// window in Word. Calling SetHostName will reset that flag.
//
_GetAppHack(); // Make it sure that we have _dwAppHack
if (_fCallSetHostName())
{
TraceMsg(TF_SHDAPPHACK, "DOH::_InitOleObject calling SetHostName for Word95");
WCHAR wszTitle[128];
MLLoadStringW(IDS_TITLE, wszTitle, ARRAYSIZE(wszTitle));
_pole->SetHostNames(wszTitle, wszTitle);
}
}
}
BOOL CDocObjectHost::_OperationIsHyperlink()
{
_ChainBSC();
DWORD dw = 0;
BINDINFO bindinfo;
ZeroMemory(&bindinfo, sizeof(BINDINFO));
bindinfo.cbSize = sizeof(BINDINFO);
HRESULT hr = _bsc.GetBindInfo(&dw, &bindinfo);
if (SUCCEEDED(hr))
{
ReleaseBindInfo(&bindinfo);
return BOOLIFY(dw & BINDF_HYPERLINK);
}
return FALSE;
}
//+-----------------------------------------------------------------------------
//
// IE Media Bar hook
// -----------------
// At least one and only one of the args must be non-null.
//
// If pbc is non-null:
// If this bind ctx is for a media url and was delegated to shdocvw from trident,
// trident has already stored the mime-type as a string on the bind ctx.
//
// If pwzMimeType is non-null:
// The given mime type is used
//
// Ask the media bar if it wants to handle this mime-type and if it does,
// return true, else return false.
//
//-------------------------------------------------------------------------------
BOOL CDocObjectHost::_DelegateToMediaBar(IBindCtx * pbc, LPCWSTR pwzMimeType)
{
USES_CONVERSION;
LPWSTR pstrMime = NULL;
TCHAR szURL[MAX_URL_STRING];
BOOL fRet = FALSE;
CComVariant svarMime(pstrMime);
CComPtr<IUnknown> spUnkObj;
CComPtr<IOleCommandTarget> spOCTMimeType;
CComPtr<IOleCommandTarget> spOCTMediaBar;
// We want to delegate only under the following conditions:
// 1. In Explorer or IE
// 2. If autoplay is enabled
// 3. If autoplay is not turned off for the first navigation
// 4. If NOT NT4 or IA64 (WMP7/8 doesn't support these platforms)
// 5. If WMP7/8 is present (if it isn't, we will try to fault-in once.)
// 6. the media bar isn't restricted
if (!(IsInternetExplorerApp()
&& CMediaBarUtil::GetAutoplay()
&& !SHRestricted2(REST_No_LaunchMediaBar, NULL, 0)
&& CMediaBarUtil::IsWMP7OrGreaterCapable()
&& FAILED(QueryService(CLSID_MediaBand, IID_PPV_ARG(IUnknown, &spUnkObj)))))
{
goto done;
}
// release so we can reuse the pointer later
spUnkObj.Release();
// get the current URL
if (FAILED(_GetCurrentPage(szURL, ARRAYSIZE(szURL), TRUE)))
goto done;
// At least one and only one of the args must be non-null.
if (pbc && !pwzMimeType)
{
// get the custom parameter that trident uses to set the media mime-type
// QI for IOleCommandTarged to access the mime-type string.
// get the mime-type string
if (FAILED(pbc->GetObjectParam(L"MediaBarMime", &spUnkObj))
|| FAILED(spUnkObj->QueryInterface(IID_PPV_ARG(IOleCommandTarget, &spOCTMimeType)))
|| FAILED(spOCTMimeType->Exec(&CGID_DownloadObjectBindContext, 0, NULL, NULL, &svarMime)))
goto done;
}
else if (!pbc && pwzMimeType)
{
svarMime = pwzMimeType;
}
else
{
ASSERT(FALSE && L"Either one of bind ctx or mime-type is expected");
goto done;
}
// proceed only if mime type string is available
if ((VT_BSTR == svarMime.vt)
&& svarMime.bstrVal)
{
// Ask the media bar if it wants to handle this mime-type
BOOL fShouldPlay = FALSE;
BOOL fShow = TRUE;
CComVariant svarUrl;
HRESULT hr = S_OK;
svarUrl = !StrCmpNIW(WZ_RADIO_PROTOCOL, szURL, wcslen(WZ_RADIO_PROTOCOL)) ? (szURL + wcslen(WZ_RADIO_PROTOCOL)) : szURL;
if (!(V_BSTR(&svarMime) && V_BSTR(&svarUrl)))
goto done;
// check if we can play this mime type
hr = CMediaBarUtil::ShouldPlay(W2T(V_BSTR(&svarMime)), &fShouldPlay);
if (FAILED(hr)
|| (!fShouldPlay))
{
goto done;
}
// ShouldPlay returns S_FALSE if this is the first time playing this mime type.
if (S_FALSE == hr)
{
BOOL bSaveSetting = FALSE;
LONGLONG iRetCode = PLAYMIME_NO;
iRetCode = DialogBoxParam(MLGetHinst(), MAKEINTRESOURCE(IDD_MEDIA_MIME), _hwnd, s_MimeDlgProc, NULL);
switch (iRetCode)
{
case PLAYMIME_YES:
{
fShouldPlay = TRUE;
bSaveSetting = FALSE;
}
break;
case PLAYMIME_YESSAVE:
{
fShouldPlay = TRUE;
bSaveSetting = TRUE;
}
break;
case PLAYMIME_NO:
{
fShouldPlay = FALSE;
bSaveSetting = FALSE;
}
break;
case PLAYMIME_NOSAVE:
{
fShouldPlay = FALSE;
bSaveSetting = TRUE;
}
break;
default:
{
ASSERT(FALSE && L"Unexpected return value");
}
break;
}
// check if the setting needs to be saved
if (TRUE == bSaveSetting)
{
DWORD dwVal = fShouldPlay ? 0x1 : 0x0;
hr = CMediaBarUtil::SetMediaRegValue(V_BSTR(&svarMime), REG_BINARY, (void*) &dwVal, (DWORD) 1, TRUE);
ASSERT(SUCCEEDED(hr));
}
// Bail if user told us not to play this mime type
if (!fShouldPlay)
goto done;
}
else
{
// "don't prompt" means "play all types", unless autoplay is turned off
// in which case we have already bailed
}
// Yay. They want to try out our feature. But WAIT. We need WMP7/8. Is that installed? No? Well, let's go get it.
// If the user decides _not_ to install WMP7, though, we should disable ourselves.
if (!CMediaBarUtil::IsWMP7OrGreaterInstalled())
{
if (!IsOS(OS_WHISTLERORGREATER))
{
// Here we do the stuff to web-jit WMP7. ISSUE: We assume that it'll always be on Whistler
uCLSSPEC ucs;
QUERYCONTEXT qc = { 0 };
ucs.tyspec = TYSPEC_CLSID;
ucs.tagged_union.clsid = CLSID_JITWMP8;
hr = FaultInIEFeature(_hwnd, &ucs, &qc, FIEF_FLAG_FORCE_JITUI);
}
if (!CMediaBarUtil::IsWMP7OrGreaterInstalled())
{
// We give up. Delegate. We'll never try autoplay again.
MLShellMessageBox(_hwnd, MAKEINTRESOURCE(IDS_MEDIABAR_NOWMP7), MAKEINTRESOURCE(IDS_MEDIABAR_NOWMP7TITLE), MB_OK);
CMediaBarUtil::ToggleAutoplay(FALSE);
goto done;
}
}
// QS for the media bar
if (FAILED(QueryService(SID_SMediaBar, IID_PPV_ARG(IOleCommandTarget, &spOCTMediaBar))))
{
// The media bar may not have been created yet
_ForceCreateMediaBar();
// QS for the media bar once again
if (FAILED(QueryService(SID_SMediaBar, IID_PPV_ARG(IOleCommandTarget, &spOCTMediaBar))))
goto done;
fShow = FALSE;
}
hr = IUnknown_Exec(spOCTMediaBar, &CGID_MediaBar, MBID_PLAY, 0, &svarMime, &svarUrl);
ASSERT(SUCCEEDED(hr));
// The Media Bar clears the URL if it accepts the navigation
if (VT_BSTR != svarUrl.vt)
{
// Cancel the navigation
fRet = TRUE;
// Ensure that the media bar is visible
if (fShow)
{
CComVariant svarPopout;
if (SUCCEEDED(IUnknown_Exec(spOCTMediaBar, &CGID_MediaBar, MBID_POPOUT, 0, NULL, &svarPopout))
&& ((VT_BOOL!=svarPopout.vt)
|| (VARIANT_FALSE==svarPopout.boolVal)))
{
_ForceCreateMediaBar();
}
}
}
}
done:
return fRet;
}
//+---------------------------------------------------------------------------------
//
// IE Media Bar Hook
// ------------------
//
// Force the Media Bar to be displayed
//
//----------------------------------------------------------------------------------
void CDocObjectHost::_ForceCreateMediaBar()
{
IUnknown_ShowBrowserBar(_psp, CLSID_MediaBand, TRUE);
}
//+---------------------------------------------------------------------------------
//
// IE Media Bar Hook
// ------------------
//
// Dialog proc for Media Bar per-mime-type dialog
//
//----------------------------------------------------------------------------------
INT_PTR CALLBACK
CDocObjectHost::s_MimeDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
UINT id;
switch(uMsg)
{
case WM_INITDIALOG:
CheckDlgButton(hDlg, IDC_MEDIA_MIME_CHKBOX, TRUE); // The first option is always checked
if (!CMediaBarUtil::IsWMP7OrGreaterInstalled())
{
TCHAR szTemp[500];
if (MLLoadString(IDS_MEDIABAR_NEEDWMP7, szTemp, ARRAYSIZE(szTemp)))
{
SetDlgItemText(hDlg, IDC_MEDIABAR_EXTRA, szTemp);
}
}
return TRUE;
case WM_DESTROY:
return FALSE;
case WM_COMMAND:
{
id = GET_WM_COMMAND_ID(wParam, lParam);
switch (id)
{
case IDOK:
{
if (IsDlgButtonChecked(hDlg, IDC_MEDIA_MIME_CHKBOX))
{
id = PLAYMIME_YESSAVE;
}
else
{
id = PLAYMIME_YES;
}
EndDialog(hDlg, id);
}
break;
case IDCANCEL:
{
if (IsDlgButtonChecked(hDlg, IDC_MEDIA_MIME_CHKBOX))
{
id = PLAYMIME_NOSAVE;
}
else
{
id = PLAYMIME_NO;
}
EndDialog(hDlg, id);
}
break;
case IDC_MEDIA_MOREINFO:
{
SHHtmlHelpOnDemandWrap(GetParent(hDlg), TEXT("iexplore.chm > iedefault"), 0, (DWORD_PTR) TEXT("mediabar_settings.htm"), ML_CROSSCODEPAGE);
}
break;
}
}
break;
default:
return FALSE;
}
return TRUE;
}
HRESULT CDocObjectHost::SetTarget(IMoniker* pmk, UINT uiCP, LPCTSTR pszLocation, LPITEMIDLIST pidlKey,
IShellView* psvPrev, BOOL fFileProtocol)
{
HRESULT hres = NOERROR;
BOOL fLoadedHistory = FALSE;
ATOMICRELEASE(_pmkCur);
_pmkCur = pmk;
pmk->AddRef();
ILFree(_pidl);
_pidl = ILClone(pidlKey);
_fFileProtocol = fFileProtocol;
_pszLocation = pszLocation;
_uiCP = uiCP;
// this is only set if we did a successful LoadHistory()
_fIsHistoricalObject = FALSE;
if (_bsc._hszPostData)
{
GlobalFree(_bsc._hszPostData);
_bsc._hszPostData = NULL;
}
if (_bsc._pszHeaders)
{
LocalFree(_bsc._pszHeaders);
_bsc._pszHeaders = NULL;
}
ATOMICRELEASE(_bsc._pbc);
//
// this replaces the old style of caching.
// if something has been cached, it was cached
// way above us before we ever existed. now it is
// waiting for us.
//
IBrowserService * pbs;
IStream * pstm = NULL;
IBindCtx * pbcHistory = NULL;
if (SUCCEEDED(QueryService(SID_SShellBrowser, IID_IBrowserService, (void **)&pbs)))
{
// just in case there is one already there, like in the case of local anchor navigates
ATOMICRELEASE(_pole);
pbs->GetHistoryObject(&_pole, &pstm, &pbcHistory);
TraceMsg(TF_TRAVELLOG, "DOH::SetTarget History object: _pole = %X, pstm = %X, pbc = %X", _pole, pstm, pbcHistory);
pbs->Release();
}
if (_pole)
{
// some objects (MSHTML for one) need their clientsite before anything else.
// so we need to init first
_InitOleObject();
if (pstm)
{
IPersistHistory *pph;
if (SUCCEEDED(_pole->QueryInterface(IID_IPersistHistory, (void **)&pph)))
{
if (SUCCEEDED(pph->LoadHistory(pstm, pbcHistory)))
{
//
// this is to make sure that we wait for
// the pole to tell us when it is ready.
// when there is a pstm, that means that they may have
// to do a full reparse or whatever, and we cant make
// any assumptions about the readystate.
//
hres = S_FALSE;
_fIsHistoricalObject = TRUE;
_SetUpTransitionCapability();
fLoadedHistory = TRUE;
// we may need to redo the pics stuff too.
// PrepPicsForAsync();
TraceMsg(TF_TRAVELLOG, "DOH::SetTarget pph->LoadHistory Successful");
}
else
ATOMICRELEASE(_pole);
pph->Release();
}
ATOMICRELEASE(pstm);
}
else
hres = S_OK;
ATOMICRELEASE(pbcHistory);
// we shouldnt fail a load history, because the data in
// is just what the document gave us in SaveHistory...
AssertMsg(NULL != _pole, TEXT("DOH::SetTarget pph->LoadHistory Failed"));
// if we were already up and created, just scroll to it.
// if we were created DEACTIVATED, (possible in the ocx case)
// don't do this activation
if (_uState != SVUIA_DEACTIVATE && !DocCanHandleNavigation())
{
hres = _ActivateMsoView();
}
//
// allow navigation - when you're deactivated. So you can navigate an invisible WebOC.
// IE6 Bug# 5449
//
if ( DocCanHandleNavigation() ||
( _fDocCanNavigate &&
_uState == SVUIA_DEACTIVATE &&
!fLoadedHistory ) )
{
// If the _fPrevDocHost flag is not
// set, we better be deactivated.
//
AssertMsg((_fPrevDocHost || _uState == SVUIA_DEACTIVATE),
_T("_fPrevDocHost is not set and we are activated"));
// If the document is handling the navigation,
// we must store the user entered URL here.
// Otherwise, if the navigation fails,
// the user-entered URL is NULL by the time
// _bsc._HandleFailedNavigationSearch is called,
//
VariantClear(&_varUserEnteredUrl);
_GetSearchString(_psp, &_varUserEnteredUrl);
WCHAR wzUrl[MAX_URL_STRING];
hres = IEGetNameAndFlagsEx(_pidl, SHGDN_FORPARSING, IEGDN_NOFRAGMENT, wzUrl, ARRAYSIZE(wzUrl), NULL);
if (S_OK == hres)
{
_EnableModeless(FALSE);
WCHAR wzLocation[MAX_URL_STRING] = {0};
IEILGetFragment(_pidl, wzLocation, SIZECHARS(wzLocation));
hres = _NavigateDocument(CComBSTR(wzUrl), CComBSTR(wzLocation));
_EnableModeless(TRUE);
}
}
}
if (!_pole)
{
ASSERT(!pstm);
ASSERT(!pbcHistory);
IBindCtx * pbc = NULL;
TraceMsg(TF_TRAVELLOG, "DOH::SetTarget No obj from TravelLog, calling pmk->BindToObject");
if (_psp)
{
hres = _psp->QueryService(SID_SShellBrowser, IID_IBindCtx, (void **)&pbc);
}
if (pbc == NULL)
{
hres = CreateBindCtx(0, &pbc);
}
else
{
hres = S_OK;
}
if (SUCCEEDED(hres))
{
HRESULT hr = E_FAIL;
IBindCtx * pbcAsync = NULL;
IBindCtx * pbcWrapper = NULL;
if (pbc)
{
// If the bind context supports IAsyncBindCtx, then it
// is a bind context that was created by UrlMon and passed
// to us to reuse. We must pass this bind context to UrlMon
// as-is and not wrapped in a BCW object.
// Note: IAsyncBindCtx has identicial interface as IBindCtx
//
hr = pbc->QueryInterface(IID_IAsyncBindCtx, (void**)&pbcAsync);
ATOMICRELEASE(pbcAsync);
}
if (SUCCEEDED(hr))
{
// If this is a media url delegated to shdocvw from trident,
// and if the IE Media Bar wants to handle the url, cancel
// the navigation, else continue the navigation.
if (_DelegateToMediaBar(pbc, NULL))
{
_CancelPendingNavigation(TRUE);
if (_pwb)
{
_pwb->SetNavigateState(BNS_NORMAL);
}
ATOMICRELEASE(pbc);
return S_FALSE;
}
else
{
ATOMICRELEASE(_pbcCur);
_fDelegatedNavigation = TRUE;
_pbcCur = pbc; // No need to AddRef - pbc is AddRef'ed by QS.
}
}
else
{
pbcWrapper = BCW_Create(pbc);
if (pbcWrapper == NULL)
{
pbcWrapper = pbc;
}
else
{
pbc->Release();
}
pbc = NULL;
hres = CreateAsyncBindCtxEx(pbcWrapper, 0, NULL, NULL, &pbcAsync, 0);
if (SUCCEEDED(hres))
{
ASSERT(pbcAsync);
ATOMICRELEASE(_pbcCur);
_pbcCur = pbcAsync;
_pbcCur->AddRef();
pbcWrapper->Release();
pbcWrapper = pbcAsync;
}
}
if (SUCCEEDED(hres))
{
#ifdef DEBUG
DWORD dwMksys;
hres = pmk->IsSystemMoniker(&dwMksys);
ASSERT((SUCCEEDED(hres) && dwMksys!=MKSYS_FILEMONIKER));
#endif
ASSERT(FALSE == _fSetTarget);
BOOL fWindowOpen = FALSE;
if (_pbcCur)
{
IUnknown * punkBindCtxParam = NULL;
hres = _pbcCur->GetObjectParam(KEY_BINDCONTEXTPARAM, &punkBindCtxParam);
if (SUCCEEDED(hres))
{
fWindowOpen = TRUE;
punkBindCtxParam->Release();
}
}
// If we are being called in response to window.open
// we create the document here and call InitNew on it
// to load about:blank. We do this so that the user won't
// see a transparent window while the URL is being
// located. When Trident calls Invoke to get the
// new window object, we will then load the real document.
//
if (fWindowOpen)
{
hres = _CreatePendingDocObject(TRUE, TRUE);
ASSERT(S_OK == hres && _punkPending);
if (_punkPending)
{
ATOMICRELEASE(_pole);
hres = _punkPending->QueryInterface(IID_IOleObject, (void**)&_pole);
ASSERT(SUCCEEDED(hres));
// IE Media Bar: turn auto-play off for the first navigation
// This is needed to make the target="_blank" feature work.
CMediaBarHelper::DisableFirstAutoPlay(_punkPending);
}
}
else
{
// Hack: The AddRef & Release protect against an error page
// navigation from freeing the pdoh out from under us (edwardp)
AddRef();
_fSetTarget = TRUE;
hres = _StartAsyncBinding(pmk, _pbcCur, psvPrev);
_fSetTarget = FALSE;
// Hack: Matching Release()
//
Release();
if (SUCCEEDED(hres))
{
hres = S_FALSE;
}
}
}
ATOMICRELEASE(pbcWrapper); // CreateAsyncBindCtx addrefs as well.
}
}
return hres;
}
#define USE_HISTBMOFFSET 0
#define USE_MYBMOFFSET 1
#define USE_STDBMOFFSET 2
void CDocObjectHost::_MergeToolbarSB()
{
}
HICON _LoadSmallIcon(int id)
{
return (HICON)LoadImage(HINST_THISDLL, MAKEINTRESOURCE(id),
IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0);
}
void _InitIcons(void)
{
ENTERCRITICAL;
if (g_hiconScriptErr == NULL)
{
g_hiconScriptErr = _LoadSmallIcon(IDI_STATE_SCRIPTERROR);
if (IS_BIDI_LOCALIZED_SYSTEM())
MirrorIcon(&g_hiconScriptErr, NULL);
}
if (!g_hiconSSL)
{
g_hiconSSL = _LoadSmallIcon(IDI_SSL);
if (IS_BIDI_LOCALIZED_SYSTEM())
MirrorIcon(&g_hiconSSL, NULL);
}
if (!g_hiconFortezza)
{
g_hiconFortezza = _LoadSmallIcon(IDI_FORTEZZA);
if (IS_BIDI_LOCALIZED_SYSTEM())
MirrorIcon(&g_hiconFortezza, NULL);
}
for (UINT id = IDI_STATE_FIRST; id <= IDI_STATE_LAST; id++)
{
if (!g_ahiconState[id-IDI_STATE_FIRST])
{
g_ahiconState[id-IDI_STATE_FIRST]= _LoadSmallIcon(id);
if (IS_BIDI_LOCALIZED_SYSTEM())
MirrorIcon(&g_ahiconState[id-IDI_STATE_FIRST], NULL);
}
}
if (!g_hiconOffline)
{
g_hiconOffline = _LoadSmallIcon(IDI_OFFLINE);
if (IS_BIDI_LOCALIZED_SYSTEM())
MirrorIcon(&g_hiconOffline, NULL);
}
if (!g_hiconPrinter)
{
g_hiconPrinter = _LoadSmallIcon(IDI_PRINTER);
if (IS_BIDI_LOCALIZED_SYSTEM())
MirrorIcon(&g_hiconPrinter, NULL);
}
if (!g_hiconPrivacyImpact)
{
g_hiconPrivacyImpact = _LoadSmallIcon( IDI_PRIVACY_IMPACT);
if (IS_BIDI_LOCALIZED_SYSTEM())
MirrorIcon(&g_hiconPrivacyImpact, NULL);
}
LEAVECRITICAL;
}
// This function initializes whatever the Class needs for manipulating the history
// we try to delay this till absolutely needed in order to not load
// wininet till the end
IUnknown *
CDocObjectHost::get_punkSFHistory()
{
if (_pocthf && !_punkSFHistory)
{
VARIANT var;
VariantInit(&var);
if (SUCCEEDED(_pocthf->Exec(&CGID_Explorer, SBCMDID_HISTSFOLDER, TRUE, NULL, &var)))
{
if (VT_UNKNOWN == var.vt && NULL != var.punkVal)
{
_punkSFHistory = var.punkVal;
_punkSFHistory->AddRef();
}
}
VariantClearLazy(&var);
}
return _punkSFHistory;
}
//
// This function (re)initializes CDocObjectHost object with the buddy
// IShellView (which is always CShellDocView) and the IShellBrowser.
// If this is the first time (_hwnd==NULL), it creates the view window
// and other associated windows as well. Otherwise (_hwnd!=NULL) -- it
// means this object is passed from one CDocViewObject to another because
// of intra-page jump -- we move it to the specified location (prcView)
// to make it really sure that we show it at the right place.
//
BOOL CDocObjectHost::InitHostWindow(IShellView* psv, IShellBrowser* psb,
LPRECT prcView)
{
HWND hwndParent;
IServiceProvider * pspTop;
IOleObject * pTopOleObject;
IOleClientSite * pOleClientSite;
_ResetOwners();
ASSERT(psv);
_psv = psv;
_psv->AddRef();
ASSERT(NULL==_pmsoctView);
_psv->QueryInterface(IID_IOleCommandTarget, (void **)&_pmsoctView);
ASSERT(NULL==_pdvs);
_psv->QueryInterface(IID_IDocViewSite, (void **)&_pdvs);
ASSERT(psb);
_psb = psb;
_psb->AddRef();
ASSERT(NULL==_pwb);
_psb->QueryInterface(IID_IBrowserService, (void **)&_pwb);
ASSERT(NULL==_pmsoctBrowser);
_psb->QueryInterface(IID_IOleCommandTarget, (void **)&_pmsoctBrowser);
ASSERT(NULL==_psp);
_psb->QueryInterface(IID_IServiceProvider, (void **)&_psp);
ASSERT(NULL==_pipu);
_psb->QueryInterface(IID_IOleInPlaceUIWindow, (void **)&_pipu);
ASSERT(_pipu);
_Init();
ASSERT(_psp);
if (_psp)
{
// Get the object that manages the extended buttons from the top-level browser
// But only if we don't already have it.
if (NULL == _pBrowsExt)
_psp->QueryService(SID_STopLevelBrowser, IID_IToolbarExt, (void **)&_pBrowsExt);
//
// LATER: I don't like that CDocObjectHost is directly accessing
// the automation service object to fire events. We should
// probably move all the progress UI code above IShellBrowser
// so that we don't need to do this shortcut. (SatoNa)
//
ASSERT(NULL==_peds);
_psp->QueryService(IID_IExpDispSupport, IID_IExpDispSupport, (void **)&_peds);
ASSERT(_peds);
ASSERT(NULL==_pedsHelper);
_peds->QueryInterface(IID_IExpDispSupportOC, (void **)&_pedsHelper);
ASSERT(NULL==_phf);
_psp->QueryService(SID_SHlinkFrame, IID_IHlinkFrame, (void **)&_phf);
if (_phf)
{
_phf->QueryInterface(IID_IUrlHistoryNotify, (void **)&_pocthf);
}
// _punkSFHistory was being initialized here - but in order to delay the load of wininet.dll
// we initialize it just before we use it
ASSERT(_pWebOCUIHandler == NULL);
ASSERT(_pWebOCUIHandler2 == NULL);
ASSERT(_pWebOCInPlaceSiteEx == NULL);
ASSERT(_fDocCanNavigate || _fWebOC == FALSE);
if (SUCCEEDED(_psp->QueryService(SID_STopLevelBrowser, IID_IServiceProvider, (void **)&pspTop)) && pspTop)
{
if (SUCCEEDED(pspTop->QueryService(SID_SContainerDispatch, IID_IOleObject, (void **)&pTopOleObject)) && pTopOleObject)
{
_fWebOC = TRUE; // there was a container so we're a WebOC
pTopOleObject->GetClientSite(&pOleClientSite);
if (pOleClientSite)
{
pOleClientSite->QueryInterface(IID_IDocHostUIHandler, (void**)&_pWebOCUIHandler);
pOleClientSite->QueryInterface(IID_IDocHostUIHandler2, (void**)&_pWebOCUIHandler2);
pOleClientSite->QueryInterface(IID_IDocHostShowUI, (void**)&_pWebOCShowUI);
pOleClientSite->QueryInterface(IID_IOleInPlaceSiteEx, (void**)&_pWebOCInPlaceSiteEx);
pOleClientSite->Release();
}
pTopOleObject->Release();
}
pspTop->Release();
}
}
_dhUIHandler.SetSite( (IDocHostUIHandler *) this); // Apparently we need to disamiguate the IUnknown reference.
_psb->GetWindow(&hwndParent);
if (!_hwnd) {
// There are several things we don't attempt to do when
// we're not toplevel. Frameset type DOH should never
// try to menu merge or dork with the statusbar.
// Do this before the CreateWindowEx call 'cuz during
// creation we party on the status bar.
{
IOleInPlaceSite* pparentsite = _GetParentSite();
if (pparentsite) {
_fHaveParentSite = TRUE;
pparentsite->Release();
}
}
_RegisterWindowClass();
// really create the window
DWORD dwStyle = WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_VISIBLE | WS_TABSTOP;
//
// In Office 95, Excel and PowerPoint don't draw the client edge,
// while Word does draw the client edge. To avoid having double edges,
// we remove it for now. SriniK (Office) will find out which will be
// the standard for Office 96. (SatoNa)
//
_hwnd = SHNoFusionCreateWindowEx(0 /* WS_EX_CLIENTEDGE */,
c_szViewClass, NULL,
dwStyle,
prcView->left, prcView->top, prcView->right-prcView->left, prcView->bottom-prcView->top,
hwndParent,
(HMENU)0,
HINST_THISDLL,
(LPVOID)SAFECAST(this, CImpWndProc*));
if (!_hwnd) {
goto Bail;
}
UINT uiAcc = ACCEL_DOCVIEW;
if (SHRestricted(REST_NOFILEMENU))
uiAcc = ACCEL_DOCVIEW_NOFILEMENU;
if (_hacc)
{
DestroyAcceleratorTable(_hacc);
_hacc = NULL;
}
_hacc = LoadAccelerators(MLGetHinst(), MAKEINTRESOURCE(uiAcc));
_InitIcons();
} else {
ASSERT(GetParent(_hwnd) == hwndParent);
MoveWindow(_hwnd, prcView->left, prcView->top,
prcView->right-prcView->left, prcView->bottom - prcView->top, TRUE);
}
Bail:
return (bool) _hwnd;
}
void CDocObjectHost::_CleanupProgress(void)
{
TraceMsg(TF_SHDPROGRESS, "CDOH::CleanupProgress fTimer = %d, fFull = %d, hwndProg = %X", _fProgressTimer, _fProgressTimerFull, _hwndProgress);
if (_fProgressTimer)
{
KillTimer(_hwnd, IDTIMER_PROGRESS);
_fProgressTimer = FALSE;
}
if (_fProgressTimerFull)
{
// we are being stopped, and the hwnd is destroyed
// before we clear the status bar. zekel - 22-JUL-97
_OnSetProgressPos(-2, PROGRESS_RESET);
KillTimer(_hwnd, IDTIMER_PROGRESSFULL);
ASSERT(!_fProgressTimerFull);
}
_OnSetProgressMax(0);
_hwndProgress = NULL;
}
void CDocObjectHost::DestroyHostWindow()
{
// Turn off the simple mode when we are leaving.
if (_psb)
_psb->SendControlMsg(FCW_STATUS, SB_SIMPLE, 0, 0, NULL);
// really destroy the window
_fCanceledByBrowser = TRUE;
_bsc.AbortBinding();
_RemoveAllPicsProcessors();
_CloseMsoView();
//
// Notes: We need to delete OLE object from this side (container),
// otherwise, we leak because of circular reference.
//
_UnBind();
_CleanupProgress();
if (_hwndTooltip) {
DestroyWindow(_hwndTooltip);
_hwndTooltip = NULL;
}
//
// Note that we need to destroy the parent after destroying children.
//
// OLE seems to recurse back into this function when we destroy the hwnd
// and we try to destroy it a second time causing a RIP. Avoid this RIP
// by NULLing out our internal variables before we destroy the hwnds.
if (_hwnd) {
HWND hwndT = _hwnd;
_hwnd = NULL;
DestroyWindow(hwndT);
}
ATOMICRELEASE(_psp);
_ResetOwners();
}
//
// This member creates a view (IOleDocumentView) of the DocObject we have (_pole).
// This function is called only once from ::CreateViewWindow.
//
HRESULT CDocObjectHost::_CreateMsoView(void)
{
ASSERT(_pmsov == NULL);
ASSERT(_pmsoc == NULL);
HRESULT hres = OleRun(_pole);
if (SUCCEEDED(hres))
{
//// WARNING:
// if you add anything to here, you should also pass it along
// in _CreateDocObjHost
//
IOleDocument* pmsod = NULL;
hres = _pole->QueryInterface(IID_IOleDocument, (void **)&pmsod);
if (SUCCEEDED(hres)) {
hres = pmsod->CreateView(this, NULL ,0,&_pmsov);
if (SUCCEEDED(hres)) {
//
// HACK: Working about MSHTML bug (#28756). We really
// want to take this hack out before we ship. (SatoNa)
//
_pmsov->SetInPlaceSite(this);
} else {
TraceMsg(DM_ERROR, "DOH::_CreateMsoView pmsod->CreateView() ##FAILED## %x", hres);
}
if (SUCCEEDED(hres) && !_pmsot) {
_pmsov->QueryInterface(IID_IOleCommandTarget, (void **)&_pmsot);
}
if (SUCCEEDED(hres) && !_pmsoc) {
_pmsov->QueryInterface(IID_IOleControl, (void **)&_pmsoc);
}
#ifdef HLINK_EXTRA
if (_pihlbc)
{
if (_phls)
{
_phls->SetBrowseContext(_pihlbc);
}
ASSERT(_pmkCur);
hres = HlinkOnNavigate(this, _pihlbc, 0,
_pmkCur, NULL, NULL);
// TraceMsg(0, "sdv TR : _CreateMsoView HlinkOnNavigate returned %x", hres);
}
#endif // HLINK_EXTRA
pmsod->Release();
} else {
TraceMsg(DM_ERROR, "DOH::_CreateMsoView _pole->QI(IOleDocument) ##FAILED## %x", hres);
}
} else {
TraceMsg(DM_ERROR, "DOH::_CreateMsoView OleRun ##FAILED## %x", hres);
}
return hres;
}
HRESULT CDocObjectHost::_ForwardSetSecureLock(int lock)
{
HRESULT hr = E_FAIL;
TraceMsg(DM_SSL, "[%X}DOH::ForwardSecureLock() lock = %d",this, lock, hr);
VARIANT va = {0};
va.vt = VT_I4;
va.lVal = lock;
// we should only suggest if we are not the topframe
if (_psp && _psb && !IsTopFrameBrowser(_psp, _psb))
{
IOleCommandTarget *pmsoct;
if (SUCCEEDED(_psp->QueryService(SID_STopFrameBrowser, IID_IOleCommandTarget, (void **)&pmsoct)))
{
ASSERT(pmsoct);
if (lock < SECURELOCK_FIRSTSUGGEST)
va.lVal += SECURELOCK_FIRSTSUGGEST;
hr = pmsoct->Exec(&CGID_Explorer, SBCMDID_SETSECURELOCKICON, 0, &va, NULL);
pmsoct->Release();
}
}
else
if (_pmsoctBrowser)
hr = _pmsoctBrowser->Exec(&CGID_Explorer, SBCMDID_SETSECURELOCKICON, 0, &va, NULL);
return hr;
}
//
// This is the only method of IOleDocumentSite, which we MUST implement.
//
HRESULT CDocObjectHost::ActivateMe(IOleDocumentView *pviewToActivate)
{
TraceMsg(TF_SHDUIACTIVATE, "DOC::ActivateMe called when _pmsov is %x", _pmsov);
HRESULT hres = S_OK;
if (_pmsov==NULL) {
hres = _CreateMsoView();
#ifdef TEST_DELAYED_SHOWMSOVIEW
SetTimer(_hwnd, 100, 1500, NULL);
MessageBeep(0);
return hres;
#endif // TEST_DELAYED_SHOWMSOVIEW
}
if (SUCCEEDED(hres))
{
_ShowMsoView();
_MergeToolbarSB();
_InitToolbarButtons();
ASSERT(_pmsoctBrowser);
if (_fSetSecureLock)
_ForwardSetSecureLock(_eSecureLock);
}
return hres;
}
//Helper routine for QueryStatus for status messages
ULONG ulBufferSizeNeeded(wchar_t *wsz, int ids, ULONG ulBufferLen)
{
TraceMsg(0, "sdv TR ulBufferSizeNeeded called with (%x)", ids);
DWORD dwLen;
WCHAR szTemp[MAX_STATUS_SIZE+1];
dwLen = MLLoadStringW(ids, szTemp, MAX_STATUS_SIZE);
dwLen += 1; // for NULL terminator
if (dwLen <= (DWORD)ulBufferLen)
MoveMemory(wsz, szTemp, dwLen * sizeof(WCHAR));
else
*wsz = 0;
return ((ULONG)dwLen);
}
HRESULT CDocObjectHost::OnQueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext, HRESULT hres)
{
if (pguidCmdGroup == NULL)
{
ULONG i;
if (rgCmds == NULL)
return E_INVALIDARG;
for (i=0 ; i<cCmds ; i++)
{
// ONLY say that we support the stuff we support in ::OnExec
switch (rgCmds[i].cmdID)
{
case OLECMDID_OPEN:
case OLECMDID_SAVE:
case OLECMDID_UPDATECOMMANDS:
case OLECMDID_SETPROGRESSMAX:
case OLECMDID_SETPROGRESSPOS:
case OLECMDID_SETPROGRESSTEXT:
case OLECMDID_SETTITLE:
rgCmds[i].cmdf = OLECMDF_ENABLED;
break;
default:
if (SUCCEEDED(hres))
{
// _pmsoctBrowser already filled this in
}
else
{
rgCmds[i].cmdf = 0;
}
break;
}
}
/* for now we deal only with status text*/
if (pcmdtext)
{
switch (rgCmds[i].cmdID)
{
case OLECMDID_OPEN:
case OLECMDID_SAVE:
pcmdtext->cwActual = ulBufferSizeNeeded(pcmdtext->rgwz,
IDS_HELP_OF(_MapFromMso(rgCmds[0].cmdID)),
pcmdtext->cwBuf);
break;
default:
if (SUCCEEDED(hres))
{
// _pmsoctBrowser already filled this in
}
else
{
pcmdtext->cmdtextf = OLECMDTEXTF_NONE;
pcmdtext->cwActual = 0;
if (pcmdtext->rgwz && pcmdtext->cwBuf>0)
*pcmdtext->rgwz = TEXT('\0');
}
break;
}
}
hres = S_OK;
}
else if (IsEqualGUID(*pguidCmdGroup, CLSID_InternetButtons) ||
IsEqualGUID(*pguidCmdGroup, CLSID_MSOButtons))
{
for (UINT i = 0 ; i < cCmds ; i++)
{
// CommandIDs from DVIDM_MENUEXT_FIRST to DVIDM_MENUEXT_LAST are reserved for toolbar extension buttons
// Do NOT use this range for constants within the scope of CLSID_InternetButtons/CLSID_MSOButtons!
if (IsInRange(rgCmds[i].cmdID, DVIDM_MENUEXT_FIRST, DVIDM_MENUEXT_LAST))
{
// We'll pass specificially this OLECMD through to the custom button
IUnknown_QueryStatus(_pBrowsExt, &CLSID_ToolbarExtButtons, 1, &rgCmds[i], pcmdtext);
}
else
{
switch (rgCmds[i].cmdID)
{
case DVIDM_PRINT:
case DVIDM_PRINTPREVIEW:
if (_pmsoctBrowser)
{
OLECMD ocButton;
static const int tbtab[] =
{
DVIDM_PRINT,
DVIDM_FONTS,
DVIDM_PRINTPREVIEW,
};
static const int cttab[] =
{
OLECMDID_PRINT,
OLECMDID_ZOOM,
OLECMDID_PRINTPREVIEW,
};
ocButton.cmdID = SHSearchMapInt(tbtab, cttab, ARRAYSIZE(tbtab), rgCmds[i].cmdID);
ocButton.cmdf = 0;
_pmsoctBrowser->QueryStatus(NULL, 1, &ocButton, NULL);
rgCmds[i].cmdf = ocButton.cmdf;
}
break;
case DVIDM_FONTS: // Always enable for IE5B2
case DVIDM_CUT:
case DVIDM_COPY:
case DVIDM_PASTE:
case DVIDM_ENCODING:
rgCmds[i].cmdf = OLECMDF_ENABLED;
break;
case DVIDM_SHOWTOOLS:
if (_ToolsButtonAvailable())
rgCmds[i].cmdf = OLECMDF_ENABLED;
break;
case DVIDM_MAILNEWS:
if (_MailButtonAvailable())
rgCmds[i].cmdf = OLECMDF_ENABLED;
break;
case DVIDM_DISCUSSIONS:
// In addition to enabled/disabled, discussions button is checked/unchecked
rgCmds[i].cmdf = _DiscussionsButtonCmdf();
break;
case DVIDM_EDITPAGE:
if (_psp)
{
// Temp code -- forward to itbar
// itbar edit code is moving here soon
IExplorerToolbar* pxtb;
if (SUCCEEDED(_psp->QueryService(SID_SExplorerToolbar, IID_IExplorerToolbar, (void **)&pxtb)))
{
OLECMD ocButton = { CITIDM_EDITPAGE, 0 };
IUnknown_QueryStatus(pxtb, &CGID_PrivCITCommands, 1, &ocButton, NULL);
rgCmds[i].cmdf = ocButton.cmdf;
pxtb->Release();
}
}
break;
}
}
}
hres = S_OK;
}
return hres;
}
HRESULT CDocObjectHost::QueryStatus(
/* [unique][in] */ const GUID *pguidCmdGroup,
/* [in] */ ULONG cCmds,
/* [out][in][size_is] */ OLECMD rgCmds[ ],
/* [unique][out][in] */ OLECMDTEXT *pcmdtext)
{
HRESULT hres = OLECMDERR_E_UNKNOWNGROUP;
// Now that BaseBrowser understands that CGID_MSHTML should be directed to the DocObject, we'll
// get caught in a loop if we send those Execs through here. Cut it off at the pass.
if (pguidCmdGroup && IsEqualGUID(CGID_MSHTML, *pguidCmdGroup))
return hres;
if (_pmsoctBrowser)
hres = _pmsoctBrowser->QueryStatus(pguidCmdGroup, cCmds, rgCmds, pcmdtext);
return OnQueryStatus(pguidCmdGroup, cCmds, rgCmds, pcmdtext, hres);
}
void CDocObjectHost::_OnSave(void)
{
if (_pole && _fFileProtocol)
{
IPersistFile * ppf = 0;
HRESULT hres = _pole->QueryInterface(IID_IPersistFile, (void **)&ppf);
if (SUCCEEDED(hres))
{
LPOLESTR pszDisplayName = NULL;
hres = _GetCurrentPageW(&pszDisplayName);
if (SUCCEEDED(hres))
{
// fRemember = TRUE for normal case
hres = ppf->Save(pszDisplayName, !_fCantSaveBack);
if (FAILED(hres))
{
TraceMsg(DM_ERROR, "DOH::_OnSave ppf->Save(psz, FALSE) failed with %x", hres);
}
OleFree(pszDisplayName);
}
ppf->Release();
}
}
}
HRESULT CDocObjectHost::_OnContentDisposition()
{
HRESULT hr = S_OK;
TCHAR szURL[MAX_URL_STRING];
HRESULT hresT;
hresT = _GetCurrentPage(szURL, ARRAYSIZE(szURL), TRUE);
if (SUCCEEDED(hresT))
{
TCHAR * pszURL;
UINT uRet;
IUnknown * punk;
if (_bsc._pszRedirectedURL && lstrlen(_bsc._pszRedirectedURL))
{
pszURL = _bsc._pszRedirectedURL;
}
else
{
pszURL = szURL;
}
hresT = QueryInterface(IID_IUnknown, (void**)&punk);
if (SUCCEEDED(hresT))
{
uRet = OpenSafeOpenDialog(_hwnd, DLG_SAFEOPEN, NULL, pszURL, NULL, NULL, NULL, _uiCP, punk);
switch(uRet)
{
case IDOK:
//
// Set this flag to avoid poppping this dialog box twice.
//
_fConfirmed = TRUE;
break; // continue download
case IDD_SAVEAS:
CDownLoad_OpenUI(_pmkCur, _bsc._pbc, FALSE, TRUE, NULL, NULL, NULL, NULL, NULL, _bsc._pszRedirectedURL, _uiCP, punk);
// fall thru to AbortBinding
case IDCANCEL:
_CancelPendingNavigation(FALSE);
hr = E_ABORT;
break;
}
punk->Release();
}
}
return hr;
}
void CDocObjectHost::_OnSetProgressPos(DWORD dwPos, DWORD state)
{
// trident will reset with -1
if (dwPos == -1)
state = PROGRESS_RESET;
switch(state)
{
case PROGRESS_RESET:
TraceMsg(TF_SHDPROGRESS, "DOH::OnSetProgressPos() RESET, timer = %d", _fProgressTimer);
if (_fProgressTimer)
{
KillTimer(_hwnd, IDTIMER_PROGRESS);
_fProgressTimer = FALSE;
}
if (_dwProgressMax)
{
// this will always finish up the progress bar
// so that when trident doesnt send us the last update
// we do it anyway
if (_fProgressTimerFull && dwPos == -2)
{
_fProgressTimerFull = FALSE;
KillTimer(_hwnd, IDTIMER_PROGRESSFULL);
_dwProgressPos = 0;
_OnSetProgressMax(0);
_fShowProgressCtl = FALSE;
_PlaceProgressBar(TRUE);
}
else if (!_fProgressTimerFull)
{
_OnSetProgressPos(0, PROGRESS_FULL);
_fProgressTimerFull = TRUE;
SetTimer(_hwnd, IDTIMER_PROGRESSFULL, 500, NULL);
}
}
else
{
_fShowProgressCtl = FALSE;
_PlaceProgressBar(TRUE);
}
break;
case PROGRESS_FINDING:
//this covers the first 10%
TraceMsg(TF_SHDPROGRESS, "DOH::OnSetProgressPos() FINDING, timer = %d", _fProgressTimer);
ASSERT(!dwPos);
if (!_fProgressTimer)
SetTimer(_hwnd, IDTIMER_PROGRESS, 500, NULL);
_fProgressTimer = TRUE;
_OnSetProgressMax(10000);
_dwProgressInc = PROGRESS_INCREMENT;
_dwProgressPos = 100;
_dwProgressTicks = 0;
_dwProgressMod = (PROGRESS_FINDMAX - _dwProgressPos) / (2 * _dwProgressInc);
break;
case PROGRESS_SENDING:
TraceMsg(TF_SHDPROGRESS, "DOH::OnSetProgressPos() SENDING, timer = %d, dwPos = %d", _fProgressTimer, dwPos);
ASSERT(!dwPos);
if (!_fProgressTimer)
SetTimer(_hwnd, IDTIMER_PROGRESS, 500, NULL);
_fProgressTimer = TRUE;
_OnSetProgressMax(10000);
_dwProgressInc = PROGRESS_INCREMENT;
_dwProgressTicks = 0;
//dwProgressPos is already set from FINDING
_dwProgressMod = (PROGRESS_SENDMAX - _dwProgressPos) / (2 * _dwProgressInc);
break;
case PROGRESS_RECEIVING:
TraceMsg(TF_SHDPROGRESS, "DOH::OnSetProgressPos() RECEIVING, timer = %d, dwPos = %d", _fProgressTimer, dwPos);
if (_fProgressTimer)
{
KillTimer(_hwnd, IDTIMER_PROGRESS);
_fProgressTimer = FALSE;
// this is the base spot on the progress bar for trident
_dwProgressBase = _dwProgressPos / PROGRESS_REBASE;
TraceMsg(TF_SHDPROGRESS, "DOH::OnSetProgressPos() Rebasing at %d%%", _dwProgressPos * 100/ PROGRESS_TOTALMAX);
}
// progress max should be set from outside of here....
_dwProgressPos = ADJUSTPROGRESSPOS(dwPos);
break;
case PROGRESS_TICK:
if (_fProgressTimer)
{
if (_dwProgressInc)
_dwProgressPos += _dwProgressInc;
// Else we post the still waiting message...
//
if (_dwProgressMod && 0 == (++_dwProgressTicks % _dwProgressMod))
{
// this means we are about half way.
_dwProgressInc /= 2;
}
TraceMsg(TF_SHDPROGRESS, "DOH::OnSetProgressPos() TICK, dwPos = %d, ticks = %d, inc = %d", _dwProgressPos, _dwProgressTicks, _dwProgressInc);
}
else
TraceMsg(TF_SHDPROGRESS, "DOH::OnSetProgressPos() TICKNOT");
break;
case PROGRESS_FULL:
{
_dwProgressPos = _dwProgressMax;
// if there are script errors, make sure the status
// bar is properly set (re: icon and text)
if (_pScriptErrList != NULL &&
!_pScriptErrList->IsEmpty())
{
TCHAR szMsg[MAX_PATH];
// set the script error icon
if (g_hiconScriptErr != NULL)
{
if (_psb != NULL)
{
_psb->SendControlMsg(FCW_STATUS,
SB_SETICON,
STATUS_PANE_NAVIGATION,
(LPARAM)g_hiconScriptErr,
NULL);
}
}
// set the script error text
MLLoadString(IDS_DONE_WITH_SCRIPT_ERRORS, szMsg, ARRAYSIZE(szMsg));
_SetPriorityStatusText(szMsg);
}
TraceMsg(TF_SHDPROGRESS, "DOH::OnSetProgressPos() FULL");
}
break;
default:
ASSERT(FALSE);
}
if (_hwndProgress)
{
_psb->SendControlMsg(FCW_PROGRESS, PBM_SETPOS, _dwProgressPos, 0, NULL);
TraceMsg(TF_SHDPROGRESS, "DOH::OnSetProgressPos() updating, pos = %d, %d%% full", _dwProgressPos, _dwProgressMax ? _dwProgressPos * 100/ _dwProgressMax : 0);
}
// fire an event that progress has changed
if (_peds)
{
// if we are sent a -1, we must forward the event on so that
// our host gets it too. some containers rely on this.
// specifically DevStudio's HTMLHelp
//
if (dwPos != -1)
dwPos = _dwProgressPos;
if (!_fUIActivatingView)
{
FireEvent_DoInvokeDwords(_peds,DISPID_PROGRESSCHANGE,dwPos,_dwProgressMax);
}
}
}
void CDocObjectHost::_OnSetProgressMax(DWORD dwMax)
{
// remember the maximum range so we have it when we want to fire progress events
if (_dwProgressMax != dwMax && _psb)
{
_dwProgressMax = dwMax;
TraceMsg(TF_SHDPROGRESS, "DOH::OnSetProgressMax() max = %d", _dwProgressMax);
if (!_hwndProgress) {
_psb->GetControlWindow(FCW_PROGRESS, &_hwndProgress);
}
if (_hwndProgress) {
_psb->SendControlMsg(FCW_PROGRESS, PBM_SETRANGE32, 0, dwMax, NULL);
TraceMsg(TF_SHDPROGRESS, "DOH::OnSetProgressMax() updating (%d of %d)", _dwProgressPos, _dwProgressMax);
}
else
TraceMsg(TF_SHDPROGRESS, "DOH::OnSetProgressMax() No hwndProgress");
}
}
UINT CDocObjectHost::_MapCommandID(UINT id, BOOL fToMsoCmd)
{
// HEY, this maps OLECMDID commands *only*
static const UINT s_aicmd[][2] = {
{ DVIDM_PROPERTIES, OLECMDID_PROPERTIES },
{ DVIDM_PRINT, OLECMDID_PRINT },
{ DVIDM_PRINTPREVIEW, OLECMDID_PRINTPREVIEW },
{ DVIDM_PAGESETUP, OLECMDID_PAGESETUP},
{ DVIDM_SAVEASFILE, OLECMDID_SAVEAS },
{ DVIDM_CUT, OLECMDID_CUT },
{ DVIDM_COPY, OLECMDID_COPY },
{ DVIDM_PASTE, OLECMDID_PASTE },
{ DVIDM_REFRESH, OLECMDID_REFRESH },
{ DVIDM_STOPDOWNLOAD, OLECMDID_STOP },
// subset - above this line document handles
{ DVIDM_OPEN, OLECMDID_OPEN },
{ DVIDM_SAVE, OLECMDID_SAVE },
{ DVIDM_SHOWTOOLS, OLECMDID_HIDETOOLBARS },
};
#define CCMD_MAX (sizeof(s_aicmd)/sizeof(s_aicmd[0]))
UINT iFrom = fToMsoCmd ? 0 : 1;
for (UINT i = 0; i < CCMD_MAX; i++) {
if (s_aicmd[i][iFrom]==id) {
return s_aicmd[i][1-iFrom];
}
}
return (UINT)-1;
#undef CCMD_MAX
}
void CDocObjectHost::_InitToolbarButtons()
{
OLECMD acmd[] = {
{ OLECMDID_ZOOM, 0 }, // Notes: This must be the first one
{ OLECMDID_PRINT, 0 },
{ OLECMDID_CUT, 0 },
{ OLECMDID_COPY, 0 },
{ OLECMDID_PASTE, 0 },
{ OLECMDID_REFRESH, 0 },
{ OLECMDID_STOP, 0 }, // Notes: This must be the last one
};
if (_pmsot) {
_pmsot->QueryStatus(NULL, ARRAYSIZE(acmd), acmd, NULL);
}
if (_pmsoctBrowser) {
// the browser may support stop also, so override the document
// with what the browser says. this is okay because the browser
// forwards stop back down the chain.
_pmsoctBrowser->QueryStatus(NULL, 1, &acmd[ARRAYSIZE(acmd)-1], NULL);
}
if (_psb)
{
for (int i=1; i<ARRAYSIZE(acmd); i++)
{
UINT idCmd = _MapFromMso(acmd[i].cmdID);
_psb->SendControlMsg(FCW_TOOLBAR, TB_ENABLEBUTTON, idCmd,
(LPARAM)acmd[i].cmdf, NULL);
}
}
// Check if ZOOM command is supported.
if (acmd[0].cmdf)
{
VARIANTARG var;
VariantInit(&var);
var.vt = VT_I4;
var.lVal = 0;
// get the current zoom depth
_pmsot->Exec(NULL, OLECMDID_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, NULL, &var);
if (var.vt == VT_I4)
{
_iZoom = var.lVal;
}
else
{
VariantClear(&var);
}
// get the current zoom range
var.vt = VT_I4;
var.lVal = 0;
_pmsot->Exec(NULL, OLECMDID_GETZOOMRANGE, OLECMDEXECOPT_DONTPROMPTUSER, NULL, &var);
if (var.vt == VT_I4)
{
_iZoomMin = (int)(short)LOWORD(var.lVal);
_iZoomMax = (int)(short)HIWORD(var.lVal);
}
else
{
VariantClear(&var);
}
}
}
void CDocObjectHost::_OnSetStatusText(VARIANTARG* pvarIn)
{
LPCWSTR pwch = VariantToStrCast(pvarIn);
if (pwch && _psb)
{
IShellView *psvActive;
_psb->QueryActiveShellView(&psvActive);
if (psvActive)
{
// Suppress sending status messages if we aren't the active view - else
// we could be reporting nasties from unapproved PICS pages
if (IsSameObject(_psv, psvActive))
{
TCHAR szHint[256];
if (pwch)
SHUnicodeToTChar(pwch, szHint, ARRAYSIZE(szHint));
else
szHint[0] = 0;
_SetStatusText(szHint);
}
psvActive->Release();
}
}
}
//
// This function returns TRUE if
// (1) the DocObject supports IPersistFile and
// (2) IPersistFile::IsDirty returns S_OK.
// Caller may pass pppf to retrieve IPersistFile*, which will be AddRef'ed
// and returned only when this function returns TRUE.
//
BOOL CDocObjectHost::_IsDirty(IPersistFile** pppf)
{
BOOL fDirty = FALSE; // Assume non-dirty
if (pppf)
*pppf = NULL;
if (_pole)
{
IPersistFile* ppf;
HRESULT hresT = _pole->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
if (SUCCEEDED(hresT))
{
if (ppf->IsDirty()==S_OK)
{
fDirty = TRUE;
if (pppf)
{
*pppf = ppf;
ppf->AddRef();
}
}
ppf->Release();
}
}
return fDirty;
}
void CDocObjectHost::_OnSetTitle(VARIANTARG *pvTitle)
{
LPCWSTR pwch = VariantToStrCast(pvTitle);
if (pwch)
{
if (_pwb)
{
_pwb->SetTitle(_psv, pwch);
}
}
// tell our parent DocObjectView about this as well
if (_pdvs)
_pdvs->OnSetTitle(pvTitle);
}
void CDocObjectHost::_OnCodePageChange(const VARIANTARG* pvarargIn)
{
if (pvarargIn && pvarargIn->vt == VT_I4)
{
TraceMsg(DM_DOCCP, "CDOH::OnExec SHDVID_ONCOEPAGECHANGE got %d", pvarargIn->lVal);
VARIANT var = *pvarargIn;
//
// Since the UI (View->Fond) does not say "default codepage",
// we don't need to be smart about it.
//
// if ((UINT)var.lVal == GetACP()) {
// var.lVal = CP_ACP;
// }
//
// Change the 'current' codepage.
//
IBrowserService *pbs;
if (SUCCEEDED(QueryService(SID_STopLevelBrowser, IID_PPV_ARG(IBrowserService, &pbs))))
{
pbs->GetSetCodePage(&var, NULL);
pbs->Release();
}
//
// Write the codepage to the URL history
//
IUniformResourceLocator * purl = NULL;
HRESULT hresT = CoCreateInstance(CLSID_InternetShortcut, NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARG(IUniformResourceLocator, &purl));
if (SUCCEEDED(hresT))
{
TCHAR szURL[MAX_URL_STRING];
_GetCurrentPage(szURL, ARRAYSIZE(szURL), TRUE);
_ValidateURL(szURL, UQF_DEFAULT);
hresT = purl->SetURL(szURL, 0);
if (SUCCEEDED(hresT))
{
IPropertySetStorage *ppropsetstg;
hresT = purl->QueryInterface(IID_PPV_ARG(IPropertySetStorage, &ppropsetstg));
if (SUCCEEDED(hresT))
{
IPropertyStorage *ppropstg;
hresT = ppropsetstg->Open(FMTID_InternetSite, STGM_READWRITE, &ppropstg);
if (SUCCEEDED(hresT))
{
const static PROPSPEC c_aprop[] = {
{ PRSPEC_PROPID, PID_INTSITE_CODEPAGE},
};
PROPVARIANT prvar = { 0 };
prvar.vt = VT_UI4;
prvar.lVal = var.lVal;
hresT = ppropstg->WriteMultiple(1, c_aprop, &prvar, 0);
TraceMsg(DM_DOCCP, "CDOH::_OnCodePageChange WriteMultile returned %x", hresT);
ppropstg->Commit(STGC_DEFAULT);
ppropstg->Release();
}
else
{
TraceMsg(DM_WARNING, "CDOH::_OnCodePageChange Open failed %x", hresT);
}
ppropsetstg->Release();
}
else
{
TraceMsg(DM_WARNING, "CDOH::_OnCodePageChange QI failed %x", hresT);
}
}
else
{
TraceMsg(DM_WARNING, "CDOH::_OnCodePageChange SetURL failed %x", hresT);
}
purl->Release();
}
else
{
TraceMsg(DM_WARNING, "CDOH::_OnCodePageChange CoCreate failed %x", hresT);
}
}
else
{
ASSERT(0);
}
}
void CDocObjectHost::_MappedBrowserExec(DWORD nCmdID, DWORD nCmdexecopt)
{
if (_pmsoctBrowser)
{
DWORD nCmdIDCT = _MapToMso(nCmdID);
ASSERT(nCmdIDCT != -1); // if this rips, need to add missing case to _MapCommandID
OLECMD rgcmd = {nCmdIDCT, 0};
// Trident sometimes executes commands that are disabled (cut, paste) so
// ensure that the command is enabled first
BOOL fEnabled = (S_OK == _pmsoctBrowser->QueryStatus(NULL, 1, &rgcmd, NULL)) &&
(rgcmd.cmdf & OLECMDF_ENABLED);
// APPHACK - 80104 Visio doesn't return OLECMDF_ENABLED, but we need to
// be able to execute the command to show the toolbars because they start off hidden.
if (!fEnabled && (nCmdID == DVIDM_SHOWTOOLS) &&
(_GetAppHack() & BROWSERFLAG_ENABLETOOLSBTN))
{
fEnabled = TRUE;
}
if (fEnabled)
{
_pmsoctBrowser->Exec(NULL, nCmdIDCT, nCmdexecopt, NULL, NULL);
}
}
}
HRESULT CDocObjectHost::OnExec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
{
if (pguidCmdGroup == NULL)
{
// _InitToolbarButtons and _OnSetStatusText reference _psb directly
if (!_psb)
return E_FAIL;
switch (nCmdID)
{
//
// The containee has found an http-equiv meta tag; handle it
// appropriately (client pull, PICS, etc)
//
case OLECMDID_HTTPEQUIV:
case OLECMDID_HTTPEQUIV_DONE:
if (_pwb)
{
_pwb->OnHttpEquiv(_psv, (nCmdID == OLECMDID_HTTPEQUIV_DONE), pvarargIn, pvarargOut);
// Always return S_OK so that we don't try other codepath.
}
return S_OK;
case OLECMDID_PREREFRESH:
_fShowProgressCtl = TRUE;
_PlaceProgressBar(TRUE);
_OnSetProgressPos(0, PROGRESS_FINDING);
if (IsGlobalOffline())
{
// This is pointing to a web address and we're offline
// Ask the user if (s)he wants to go online
TCHAR szURL[MAX_URL_STRING];
if (SUCCEEDED(_GetCurrentPage(szURL, ARRAYSIZE(szURL), TRUE)) &&
UrlHitsNet(szURL))
{
if (InternetGoOnline(szURL, _hwnd, TRUE) && _psb)
{
// Tell all browser windows to update their title and status pane
SendShellIEBroadcastMessage(WM_WININICHANGE,0,0, 1000);
}
}
}
return S_OK;
case OLECMDID_REFRESH:
if (_pmsot)
_pmsot->Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
return S_OK;
case OLECMDID_OPEN:
_OnOpen();
return S_OK;
case OLECMDID_SAVE:
_OnSave();
return S_OK;
case OLECMDID_CLOSE:
_OnClose();
return S_OK;
case OLECMDID_UPDATECOMMANDS:
_InitToolbarButtons();
return E_FAIL; // lie and say we don't do anything to forward the command on
case OLECMDID_SETPROGRESSMAX:
ASSERT(pvarargIn->vt == VT_I4);
TraceMsg(TF_SHDPROGRESS, "DOH::Exec() SETPROGRESSMAX = %d", pvarargIn->lVal );
if (pvarargIn->lVal)
_OnSetProgressMax(ADJUSTPROGRESSMAX((DWORD) pvarargIn->lVal));
return S_OK;
case OLECMDID_SETPROGRESSPOS:
ASSERT(pvarargIn->vt == VT_I4);
TraceMsg(TF_SHDPROGRESS, "DOH::Exec() SETPROGRESSPOS = %d", pvarargIn->lVal );
if (pvarargIn->lVal)
_OnSetProgressPos((DWORD) pvarargIn->lVal, PROGRESS_RECEIVING);
return S_OK;
case OLECMDID_SETPROGRESSTEXT:
_OnSetStatusText(pvarargIn);
return S_OK;
case OLECMDID_SETTITLE:
if (!pvarargIn)
return E_INVALIDARG;
_OnSetTitle(pvarargIn); // We are guaranteed to get atleast 1 OLECMDID_SETTITLE.
return S_OK;
// case OLECMDID_PRINT:
// In the up direction, this case is handled by the outermost frame as
// a request to print from the docobj. It handles it by sending an OLECMDID_PRINT
// back to the docobj to print. (Or, as in Binder, to all the docobjects.)
default:
return OLECMDERR_E_NOTSUPPORTED;
}
}
else if (IsEqualGUID(CGID_ShellDocView, *pguidCmdGroup))
{
switch (nCmdID)
{
case SHDVID_SSLSTATUS:
{
// Ask the user if (s)he wants to go online
TCHAR szURL[MAX_URL_STRING];
if (SUCCEEDED(_GetCurrentPage(szURL, ARRAYSIZE(szURL), TRUE)))
{
if (_bsc._pszRedirectedURL && *_bsc._pszRedirectedURL)
StrCpyN(szURL, _bsc._pszRedirectedURL, ARRAYSIZE(szURL));
ULONG_PTR uCookie = 0;
SHActivateContext(&uCookie);
InternetShowSecurityInfoByURL(szURL, _hwnd);
if (uCookie)
{
SHDeactivateContext(uCookie);
}
}
break;
}
case SHDVID_ZONESTATUS:
{
// Load the current url into the properties page
if (!SHRestricted2W(REST_NoBrowserOptions, NULL, 0))
{
TCHAR szBuf[MAX_URL_STRING];
_GetCurrentPage(szBuf, ARRAYSIZE(szBuf));
ULONG_PTR uCookie = 0;
SHActivateContext(&uCookie);
ZoneConfigureW(_hwnd, szBuf);
if (uCookie)
{
SHDeactivateContext(uCookie);
}
}
return S_OK;
}
case SHDVID_PRIVACYSTATUS:
{
IEnumPrivacyRecords *pEnum = NULL;
LPOLESTR pszName = NULL;
BOOL fReportAllSites = (nCmdexecopt == TRUE);
if(_pmkCur)
{
if(FAILED(_pmkCur->GetDisplayName(_pbcCur, NULL, &pszName)))
{
pszName = NULL;
}
}
if(_psp && SUCCEEDED(_psp->QueryService(IID_IEnumPrivacyRecords, IID_IEnumPrivacyRecords, (void **)&(pEnum))))
{
BOOL fImpacted;
if(fReportAllSites ||
(SUCCEEDED(pEnum->GetPrivacyImpacted(&fImpacted)) && fImpacted))
{
DoPrivacyDlg(_hwnd, pszName, pEnum, fReportAllSites);
}
pEnum->Release();
}
if(pszName)
{
OleFree(pszName);
}
return S_OK;
}
case SHDVID_QUERYMERGEDHELPMENU:
if (_hmenuMergedHelp)
{
pvarargOut->vt = VT_INT_PTR;
pvarargOut->byref = _hmenuMergedHelp;
return S_OK;
}
return S_FALSE;
case SHDVID_QUERYOBJECTSHELPMENU:
if (_hmenuObjHelp)
{
pvarargOut->vt = VT_INT_PTR;
pvarargOut->byref = _hmenuObjHelp;
return S_OK;
}
return S_FALSE;
case SHDVID_GETSYSIMAGEINDEX:
if (_dwAppHack & BROWSERFLAG_MSHTML) {
ASSERT(pvarargOut->vt==0);
pvarargOut->vt = VT_I4;
pvarargOut->lVal = _GetIEHTMLImageIndex();
return S_OK;
}
return E_FAIL;
case SHDVID_AMBIENTPROPCHANGE:
// An ambient property above us has changed, let the docobj know
if (_pmsoc)
{
ASSERT(pvarargIn->vt == VT_I4);
return(_pmsoc->OnAmbientPropertyChange(pvarargIn->lVal));
}
return E_FAIL;
case SHDVID_CANDOCOLORSCHANGE:
return IUnknown_Exec(_pole, pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
case SHDVID_ONCOLORSCHANGE:
// this comes from trident and needs passing back up to our parent ...
if ( _pmsoctBrowser )
{
return _pmsoctBrowser->Exec( pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut );
}
else
return E_FAIL;
case SHDVID_GETOPTIONSHWND:
if ( _pmsoctBrowser )
{
return _pmsoctBrowser->Exec( pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut );
}
else
{
return E_FAIL;
}
case SHDVID_DOCWRITEABORT:
// pending DocObject wants to us to abort any binding and activate
// it directly
if (_bsc._pib && _bsc._fBinding && _punkPending && !_pole)
{
_bsc._fDocWriteAbort = 1;
_bsc.OnObjectAvailable(IID_IUnknown, _punkPending);
_bsc.AbortBinding();
}
// report READYSTATE_COMPLETE so that when document.open() falls
// back to READYSTATE_INTERACTIVE Trident doesn't get confused...
//
// chrisfra 4/15/97, is this the only way to force TRIDENT
// to not lose fact of download complete when document.open()
// falls back to READYSTATE_INTERACTIVE.
//
// During the above OnObjectAvailable call, we fire a READYSTATE_COMPLETE
// event if (1) object doesn't support it or (2) object already at it.
// (Neither of these should be the case, but we should be careful, eh?)
// We want to force a READYSTATE_COMPLETE here in other cases, so unhook
// the IPropertyNotifySink (to prevent multiple _COMPLETE events). If we
// unhook the sink, then we didn't fire _COMPLETE above, so fire it now.
//
if (_dwPropNotifyCookie)
{
_OnReadyState(READYSTATE_COMPLETE);
}
return S_OK;
case SHDVID_CANACTIVATENOW:
{
HRESULT hres = (_PicsProcBase._fPicsAccessAllowed && !_PicsProcBase._fbPicsWaitFlags && _pole && _fReadystateInteractiveProcessed) ? S_OK : S_FALSE;
TraceMsg(DM_PICS, "CDOH::OnExec(SHDVID_CANACTIVATENOW) returning %ls", (hres == S_OK) ? "S_OK" : "S_FALSE");
return hres;
}
case SHDVID_SETSECURELOCK:
{
//
// if we are already active, then we need to go ahead
// and forward this up the browser. otherwise, cache it
// and wait until activated to forward it
//
TraceMsg(DM_SSL, "[%X]DOH::Exec() SETSECURELOCK lock = %d", this, pvarargIn->lVal);
_fSetSecureLock = TRUE;
_eSecureLock = pvarargIn->lVal;
IShellView *psvActive;
if (_psb && SUCCEEDED(_psb->QueryActiveShellView(&psvActive) ))
{
if (psvActive && IsSameObject(_psv, psvActive))
_ForwardSetSecureLock(pvarargIn->lVal);
ATOMICRELEASE(psvActive);
}
return S_OK;
}
case SHDVID_FORWARDSECURELOCK:
_ForwardSetSecureLock(_fSetSecureLock ? _eSecureLock : SECURELOCK_SET_UNSECURE);
return S_OK;
case SHDVID_ONCODEPAGECHANGE:
_OnCodePageChange(pvarargIn);
return S_OK;
case SHDVID_DISPLAYSCRIPTERRORS:
case SHDVID_NAVIGATIONSTATUS:
{
// if we're a weboc then this script err list should be null
ASSERT(!_fWebOC || _pScriptErrList == NULL);
if (_pScriptErrList != NULL && !_pScriptErrList->IsEmpty())
{
// do the script error info dialog
_ScriptErr_Dlg(TRUE);
}
return S_OK;
}
break;
case SHDVID_RESETSTATUSBAR:
{
_ResetStatusBar();
return S_OK;
}
break;
default:
return OLECMDERR_E_NOTSUPPORTED;
}
}
else if (IsEqualGUID(CGID_Explorer, *pguidCmdGroup))
{
switch (nCmdID) {
case SBCMDID_MAYSAVECHANGES:
return _OnMaySaveChanges();
case SBCMDID_GETPANE:
switch(nCmdexecopt)
{
case PANE_NAVIGATION:
V_I4(pvarargOut) = STATUS_PANE_NAVIGATION;
return S_OK;
case PANE_PROGRESS:
V_I4(pvarargOut) = STATUS_PANE_PROGRESS;
return S_OK;
case PANE_ZONE:
V_I4(pvarargOut) = STATUS_PANE_ZONE;
return S_OK;
case PANE_OFFLINE:
V_I4(pvarargOut) = STATUS_PANE_OFFLINE;
return S_OK;
case PANE_PRINTER:
V_I4(pvarargOut) = STATUS_PANE_PRINTER;
return S_OK;
case PANE_SSL:
V_I4(pvarargOut) = STATUS_PANE_SSL;
return S_OK;
case PANE_PRIVACY:
V_I4(pvarargOut) = STATUS_PANE_PRIVACY;
return S_OK;
default:
V_I4(pvarargOut) = PANE_NONE;
return S_OK;
}
case SBCMDID_ONCLOSE:
_fClosing = TRUE;
return S_OK;
default:
return OLECMDERR_E_NOTSUPPORTED;
} // switch
}
else if (IsEqualGUID(CGID_DocHostCommandHandler, *pguidCmdGroup))
{
switch(nCmdID)
{
case OLECMDID_SAVEAS:
_OnSaveAs();
return S_OK;
case OLECMDID_SHOWSCRIPTERROR:
{
HRESULT hr;
hr = S_OK;
if (_fWebOC)
{
// we're a web oc.
// pass the handling of this script error to
// an appropriate CDocHostUIHandler
if (_pWebOCUIHandler != NULL)
{
IOleCommandTarget * pioct;
ASSERT(IS_VALID_CODE_PTR(_pWebOCUIHandler, IDocHostUIHandler));
hr = _pWebOCUIHandler->QueryInterface(IID_IOleCommandTarget, (void **) &pioct);
if (SUCCEEDED(hr))
{
hr = pioct->Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
pioct->Release();
}
}
else
{
hr = _dhUIHandler.Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
}
}
else
{
ASSERT(IS_VALID_READ_PTR(pvarargIn, VARIANTARG));
ASSERT(IS_VALID_WRITE_PTR(pvarargOut, VARIANTARG));
// we're not a web oc so we have to handle this
// ourselves, so cache the errors for later
// display in the new script error dialog
if (pvarargIn == NULL || pvarargOut == NULL)
{
hr = E_INVALIDARG;
}
if (SUCCEEDED(hr))
{
if (_pScriptErrList == NULL)
{
// create a new script error list
_pScriptErrList = new CScriptErrorList;
if (_pScriptErrList == NULL)
{
hr = E_OUTOFMEMORY;
}
}
if (SUCCEEDED(hr))
{
TCHAR szMsg[MAX_PATH];
// stuff the error icon into the status bar
if (g_hiconScriptErr != NULL)
{
if (_psb != NULL)
{
_psb->SendControlMsg(FCW_STATUS,
SB_SETICON,
STATUS_PANE_NAVIGATION,
(LPARAM)g_hiconScriptErr,
NULL);
}
}
// stuff the error text into the status bar
MLLoadString(IDS_SCRIPT_ERROR_ON_PAGE, szMsg, ARRAYSIZE(szMsg));
_SetPriorityStatusText(szMsg);
// stuff the error data into the cache
_ScriptErr_CacheInfo(pvarargIn);
// pop up the dialog
_ScriptErr_Dlg(FALSE);
V_VT(pvarargOut) = VT_BOOL;
if (_pScriptErrList->IsFull())
{
// stop running scripts
V_BOOL(pvarargOut) = VARIANT_FALSE;
}
else
{
// keep running scripts
V_BOOL(pvarargOut) = VARIANT_TRUE;
}
}
}
}
return hr;
}
break;
case OLECMDID_SHOWMESSAGE:
case OLECMDID_SHOWFIND:
case OLECMDID_SHOWPAGESETUP:
case OLECMDID_SHOWPRINT:
case OLECMDID_PROPERTIES:
{
return _dhUIHandler.Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
}
break;
//
// Refresh the original page if an error page is dispalyed.
//
case IDM_REFRESH:
case IDM_REFRESH_TOP:
case IDM_REFRESH_TOP_FULL:
case IDM_REFRESH_THIS:
case IDM_REFRESH_THIS_FULL:
{
HRESULT hr = OLECMDERR_E_NOTSUPPORTED;
if (_pScriptErrList != NULL)
{
// clear out the script error list
_pScriptErrList->ClearErrorList();
_SetPriorityStatusText(NULL);
// reset the text and icon
_ResetStatusBar();
}
//
// If there is a refresh url for this object use it for the refresh.
// Otherwise fall through and let the client handle it.
//
if (_pwszRefreshUrl)
{
_fRefresh = TRUE;
_DoAsyncNavigation(_pwszRefreshUrl);
hr = S_OK;
}
else
{
//
// Non http errors (syntax, DNS, etc) are handled by a async nav
// to res://shdocvw/error.htm#originalurl. Handle the refresh
// for those pages here.
//
if (_pmkCur)
{
LPOLESTR pstrUrl;
if (SUCCEEDED(_pmkCur->GetDisplayName(_pbcCur, NULL, &pstrUrl)))
{
if (IsErrorUrl(pstrUrl) && _pszLocation && *_pszLocation)
{
//
// The error url has the form:
// "res://shdocvw.dll/http404.htm#http://foo.bar"
// Where foo.bar is the the url the user tried to navigate to.
// _pszLocation points to "#foo.bar"
DWORD dwScheme = GetUrlScheme(_pszLocation + 1);
BOOL fDoNavigation = ((URL_SCHEME_HTTP == dwScheme) ||
(URL_SCHEME_HTTPS == dwScheme) ||
(URL_SCHEME_FTP == dwScheme) ||
(URL_SCHEME_GOPHER == dwScheme));
//
if (fDoNavigation) // otherwise it's a security problem !
{
_fRefresh = TRUE;
_DoAsyncNavigation(_pszLocation + 1);
}
hr = S_OK;
}
OleFree(pstrUrl);
}
}
}
return hr;
break;
}
default:
return OLECMDERR_E_NOTSUPPORTED;
}
}
else if (IsEqualGUID(*pguidCmdGroup, CLSID_InternetButtons) ||
IsEqualGUID(*pguidCmdGroup, CLSID_MSOButtons))
{
UEMFireEvent(&UEMIID_BROWSER, UEME_UITOOLBAR, UEMF_XEVENT, UIG_OTHER, nCmdID);
if (nCmdexecopt == OLECMDEXECOPT_PROMPTUSER) {
// the user hit the drop down
if (_pmsoctBrowser && pvarargIn && pvarargIn->vt == VT_INT_PTR)
{
// v.vt = VT_INT_PTR;
POINT pt;
RECT* prc = (RECT*)pvarargIn->byref;
pt.x = prc->left;
pt.y = prc->bottom;
switch (nCmdID)
{
case DVIDM_MAILNEWS:
{
VARIANTARG v = {VT_I4};
v.lVal = MAKELONG(prc->left, prc->bottom);
_pmsoctBrowser->Exec(&CGID_Explorer, SBCMDID_DOMAILMENU, 0, &v, NULL);
break;
}
case DVIDM_FONTS:
{
VARIANTARG v = {VT_I4};
v.lVal = MAKELONG(prc->left, prc->bottom);
_pmsoctBrowser->Exec(&CGID_ShellDocView, SHDVID_FONTMENUOPEN, 0, &v, NULL);
break;
}
case DVIDM_ENCODING:
{
VARIANTARG v = {VT_I4};
v.lVal = MAKELONG(prc->left, prc->bottom);
_pmsoctBrowser->Exec(&CGID_ShellDocView, SHDVID_MIMECSETMENUOPEN, 0, &v, NULL);
break;
}
}
}
return S_OK;
}
// CommandIDs from DVIDM_MENUEXT_FIRST to DVIDM_MENUEXT_LAST are reserved for toolbar extension buttons
// Do NOT use this range for constants within the scope of CLSID_InternetButtons/CLSID_MSOButtons!
if (InRange(nCmdID, DVIDM_MENUEXT_FIRST, DVIDM_MENUEXT_LAST))
{
IUnknown_Exec(_pBrowsExt, &CLSID_ToolbarExtButtons, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
}
else
{
switch(nCmdID) {
case DVIDM_DISCUSSIONS:
if (_pmsoctBrowser)
_pmsoctBrowser->Exec(&CGID_Explorer, SBCMDID_DISCUSSIONBAND, OLECMDEXECOPT_DONTPROMPTUSER, NULL, NULL);
break;
case DVIDM_CUT:
case DVIDM_COPY:
case DVIDM_PASTE:
_MappedBrowserExec(nCmdID, 0);
break;
case DVIDM_PRINT:
case DVIDM_PRINTPREVIEW:
case DVIDM_SHOWTOOLS:
_MappedBrowserExec(nCmdID, OLECMDEXECOPT_DONTPROMPTUSER);
break;
case DVIDM_EDITPAGE:
if (_psp) {
// temp code -- forward to itbar
// itbar edit code is moving here soon
IExplorerToolbar* pxtb;
if (SUCCEEDED(_psp->QueryService(SID_SExplorerToolbar, IID_IExplorerToolbar, (void **)&pxtb))) {
IUnknown_Exec(pxtb, &CGID_PrivCITCommands, CITIDM_EDITPAGE, nCmdexecopt, pvarargIn, pvarargOut);
pxtb->Release();
}
}
break;
}
}
return S_OK;
}
else if (IsEqualGUID(IID_IExplorerToolbar, *pguidCmdGroup))
{
switch (nCmdID)
{
case ETCMDID_GETBUTTONS:
{
int nNumExtButtons = 0;
if (_pBrowsExt)
{
_pBrowsExt->GetNumButtons((UINT*)&nNumExtButtons);
}
int nNumButtons = nNumExtButtons + ARRAYSIZE(c_tbStd);
if ((_nNumButtons != nNumButtons) && (_ptbStd != NULL))
{
delete [] _ptbStd;
_ptbStd = NULL;
}
if (_ptbStd == NULL)
{
_ptbStd = new TBBUTTON[nNumButtons];
if (_ptbStd == NULL)
{
return E_OUTOFMEMORY;
}
_nNumButtons = nNumButtons;
}
memcpy(_ptbStd, c_tbStd, SIZEOF(TBBUTTON) * ARRAYSIZE(c_tbStd));
// Init the string ids
ASSERT(_ptbStd[6].idCommand == DVIDM_CUT);
ASSERT(_ptbStd[7].idCommand == DVIDM_COPY);
ASSERT(_ptbStd[8].idCommand == DVIDM_PASTE);
ASSERT(_ptbStd[9].idCommand == DVIDM_ENCODING);
ASSERT(_ptbStd[10].idCommand == DVIDM_PRINTPREVIEW);
if (-1 != _iString)
{
_ptbStd[6].iString = _iString;
_ptbStd[7].iString = _iString + 1;
_ptbStd[8].iString = _iString + 2;
_ptbStd[9].iString = _iString + 3;
_ptbStd[10].iString = _iString + 4;
}
else
{
_ptbStd[6].iString = _ptbStd[7].iString = _ptbStd[8].iString = _ptbStd[9].iString = _ptbStd[10].iString = -1;
}
if (_pBrowsExt)
{
_pBrowsExt->GetButtons(&_ptbStd[ARRAYSIZE(c_tbStd)], nNumExtButtons, FALSE);
}
ASSERT(_ptbStd[0].idCommand == DVIDM_SHOWTOOLS);
if (!_ToolsButtonAvailable())
_ptbStd[0].fsState |= TBSTATE_HIDDEN;
ASSERT(_ptbStd[1].idCommand == DVIDM_MAILNEWS);
if (!_MailButtonAvailable())
_ptbStd[1].fsState |= TBSTATE_HIDDEN;
ASSERT(_ptbStd[5].idCommand == DVIDM_DISCUSSIONS);
if (!_DiscussionsButtonAvailable())
_ptbStd[5].fsState |= TBSTATE_HIDDEN;
nNumButtons = RemoveHiddenButtons(_ptbStd, nNumButtons);
pvarargOut->vt = VT_BYREF;
pvarargOut->byref = (LPVOID)_ptbStd;
*pvarargIn->plVal = nNumButtons;
break;
}
case ETCMDID_RELOADBUTTONS:
_AddButtons(TRUE);
break;
}
return S_OK;
}
else if (IsEqualGUID(CGID_InternetExplorer, *pguidCmdGroup))
{
switch (nCmdID) {
case IECMDID_BEFORENAVIGATE_GETSHELLBROWSE:
case IECMDID_BEFORENAVIGATE_DOEXTERNALBROWSE:
case IECMDID_BEFORENAVIGATE_GETIDLIST:
if ( _pmsoctBrowser )
{
return _pmsoctBrowser->Exec( pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut );
}
else
{
return E_FAIL;
}
default:
return OLECMDERR_E_NOTSUPPORTED;
}
}
return OLECMDERR_E_UNKNOWNGROUP;
}
HRESULT CDocObjectHost::_OnMaySaveChanges(void)
{
HRESULT hres = S_OK;
//
// ASSUMPTIONS:
// 1. Not supporting IPersistFile indicates we don't need to worry
// about prompting the user for "save as".
// 2. DocObject which returns S_OK for IPersistFile::S_OK implements
// OLECMDID_SAVEAS.
//
if (_fFileProtocol || _pmsot)
{
IPersistFile* ppf;
if (_IsDirty(&ppf))
{
ASSERT(ppf);
TCHAR szBuf[MAX_URL_STRING];
UINT id;
_GetCurrentPage(szBuf, ARRAYSIZE(szBuf));
id = MLShellMessageBox(_hwnd,
MAKEINTRESOURCE(IDS_MAYSAVEDOCUMENT), szBuf, MB_YESNOCANCEL);
switch(id) {
case IDCANCEL:
hres = S_FALSE;
break;
case IDYES:
if (_fFileProtocol) {
// 80105 APPHACK: Due to valid fixes in Urlmon, Visio is unable to save
// because we are loading the object with read-only flags. So we show
// the Save As dialog to let the user choose another filename.
if (_GetAppHack() & BROWSERFLAG_SAVEASWHENCLOSING)
{
if (_OnSaveAs() != S_OK)
hres = S_FALSE;
}
else
_OnSave();
} else {
HRESULT hresT=_pmsot->Exec(NULL, OLECMDID_SAVEAS, OLECMDEXECOPT_PROMPTUSER, NULL, NULL);
SAVEMSG("Exec(OLECMDID_SAVEAS) returned", hresT);
// Cancel the navigation if it failed.
if (FAILED(hresT)) {
// Beep if it is not canceled by the end user.
TraceMsg(DM_WARNING, "CDOH::_OnMaySaveChanges Exec(OELCMDID_SAVEAS) returned %x", hresT);
if (hresT != OLECMDERR_E_CANCELED) {
MessageBeep(0);
}
hres = S_FALSE;
}
}
break;
case IDNO:
//
// If user says 'No' to save changes to this page,
// we should remove it from the cache so that
// the user won't see that discarded change.
//
// (pri-2) This object discarding mechanism
// does not work for POSTed result, which is cached
// in the travel log.
//
break;
}
ppf->Release();
} else {
ASSERT(ppf==NULL);
}
}
//
// In addition, we give a chance to save the contents of the page (when
// the document is acted as a form -- data-bound Trident page is a good
// example) to the backend database.
//
if (hres == S_OK && _pmsot && (!_fDocCanNavigate || _fClosing))
{
VARIANT varOut = {0};
HRESULT hresT = _pmsot->Exec(NULL, OLECMDID_ONUNLOAD, OLECMDEXECOPT_PROMPTUSER, NULL, &varOut);
if (varOut.vt == VT_BOOL && varOut.boolVal != VARIANT_TRUE)
{
hres = S_FALSE;
}
}
return hres;
}
BOOL _ExecNearest(const GUID *pguidCmdGroup, DWORD nCmdID, BOOL fDown)
{
// Some commands we want to do in the closest frame to the docobj,
// some in the farthest-away frame, and some we want to handle
// in the top-most dochost. Look at the command to figure out
// the routing and then do it.
BOOL fNearest = FALSE; // most everything goes to the farthest-away frame
if (pguidCmdGroup==NULL)
{
switch(nCmdID)
{
case OLECMDID_OPEN:
case OLECMDID_SAVE:
case OLECMDID_SETTITLE:
case OLECMDID_HTTPEQUIV:
case OLECMDID_HTTPEQUIV_DONE:
fNearest = TRUE;
break;
// some are top-most down, so nearest depends on direction.
case OLECMDID_REFRESH:
// say top-most for commands that only work on the topmost guy.
// (ie, these probably should be implemented in CShellBrowser!)
// do this even though these are really "upwards-only" commands.
case OLECMDID_UPDATECOMMANDS:
case OLECMDID_SETPROGRESSMAX:
case OLECMDID_SETPROGRESSPOS:
case OLECMDID_SETPROGRESSTEXT:
case OLECMDID_SHOWSCRIPTERROR:
fNearest = fDown;
break;
}
}
else if (IsEqualGUID(CGID_ShellDocView, *pguidCmdGroup))
{
switch (nCmdID)
{
case SHDVID_AMBIENTPROPCHANGE:
case SHDVID_GETSYSIMAGEINDEX:
case SHDVID_DOCWRITEABORT:
case SHDVID_ONCODEPAGECHANGE:
case SHDVID_CANDOCOLORSCHANGE:
case SHDVID_SETSECURELOCK:
case SHDVID_QUERYMERGEDHELPMENU:
case SHDVID_QUERYOBJECTSHELPMENU:
fNearest = TRUE;
break;
case SHDVID_DISPLAYSCRIPTERRORS:
fNearest = fDown;
break;
}
}
else if (IsEqualGUID(CGID_Explorer, *pguidCmdGroup))
{
switch(nCmdID)
{
case SBCMDID_MAYSAVECHANGES: // since OLECMDID_SAVE is to the nearest frame
fNearest = TRUE;
break;
}
}
else if (IsEqualGUID(IID_IExplorerToolbar, *pguidCmdGroup) ||
IsEqualGUID(CLSID_InternetButtons, *pguidCmdGroup) ||
IsEqualGUID(CLSID_MSOButtons, *pguidCmdGroup))
{
fNearest = TRUE;
}
return fNearest;
}
HRESULT CDocObjectHost::Exec(const GUID * pguidCmdGroup,
DWORD nCmdID,
DWORD nCmdexecopt,
VARIANTARG * pvarargIn,
VARIANTARG * pvarargOut)
{
HRESULT hres = OLECMDERR_E_UNKNOWNGROUP;
if (pguidCmdGroup)
{
// Now that BaseBrowser understands that CGID_MSHTML should be directed to the DocObject, we'll
// get caught in a loop if we send those Execs through here. Cut it off at the pass.
if (IsEqualGUID(CGID_MSHTML, *pguidCmdGroup))
{
return hres;
}
else if (IsEqualGUID(CGID_DocHostCommandHandler, *pguidCmdGroup))
{
BOOL fHandled = FALSE;
HRESULT hr = _HandleDocHostCmds(nCmdID,
nCmdexecopt,
pvarargIn,
pvarargOut,
&fHandled);
if (fHandled)
{
return hr;
}
}
else if (IsEqualGUID(CGID_DocHostCmdPriv, *pguidCmdGroup))
{
BOOL fHandled = FALSE;
HRESULT hr = _HandleDocHostCmdPriv(nCmdID,
nCmdexecopt,
pvarargIn,
pvarargOut,
&fHandled);
if (fHandled)
{
return hr;
}
}
else if (IsEqualGUID(CGID_ShellDocView, *pguidCmdGroup))
{
if (_HandleShdocvwCmds(nCmdID, nCmdexecopt, pvarargIn, pvarargOut))
{
return S_OK;
}
}
}
BOOL fNearest = _ExecNearest(pguidCmdGroup, nCmdID, FALSE);
if (fNearest)
hres = OnExec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
if (FAILED(hres) && _pmsoctBrowser)
{
hres = _pmsoctBrowser->Exec(pguidCmdGroup, nCmdID, nCmdexecopt,
pvarargIn, pvarargOut);
}
// If this is a command that puts up UI and the user presses
// cancel in the above call, we may try to handle the call here,
// and that would be bad. Steal OleCmdHRHandled() from MSHTML.
if (FAILED(hres) && !fNearest)
hres = OnExec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
return hres;
}
//+---------------------------------------------------------------------------
//
// Member : CDocObjectHost::_HandleDocHostCmds
//
// Synopsis : Handles the CMD IDs for CGID_DocHostCommandHandler.
//
//+---------------------------------------------------------------------------
HRESULT
CDocObjectHost::_HandleDocHostCmds(DWORD nCmdID,
DWORD nCmdexecopt,
VARIANTARG * pvarargIn,
VARIANTARG * pvarargOut,
BOOL * pfHandled)
{
HRESULT hres = E_FAIL;
ASSERT(pfHandled);
*pfHandled = TRUE;
switch(nCmdID)
{
// Call from Trident printing with the page # of the currently spooling page.
// Convert it to a bool indicating whether or not to draw the print icon in the browser.
case OLECMDID_UPDATEPAGESTATUS:
hres = E_INVALIDARG;
if (pvarargIn && pvarargIn->vt == VT_I4)
{
VARIANTARG varIn;
V_VT(&varIn) = VT_BOOL;
V_BOOL(&varIn)= (pvarargIn->lVal > 0) ? VARIANT_TRUE : VARIANT_FALSE;
hres = _pmsoctBrowser->Exec(&CGID_ShellDocView,
SHDVID_SETPRINTSTATUS,
0,
&varIn,
NULL);
}
break;
case OLECMDID_REFRESH:
// if the print Preview template is up, then we need to block refresh. IE bug (99685)
hres = _dhUIHandler.Exec(&CGID_DocHostCommandHandler,
OLECMDID_REFRESH,
nCmdexecopt, pvarargIn, pvarargOut);
break;
// Allow the command ID to be passed down the Exec chain.
//
default:
*pfHandled = FALSE;
break;
}
return hres;
}
//+---------------------------------------------------------------------------
//
// Member : CDocObjectHost::_HandleDocHostCmdPriv
//
// Synopsis : Handles the CMD IDs for CGID_DocHostCmdPriv
//
//+---------------------------------------------------------------------------
HRESULT
CDocObjectHost::_HandleDocHostCmdPriv(DWORD nCmdID,
DWORD nCmdexecopt,
VARIANTARG * pvarargIn,
VARIANTARG * pvarargOut,
BOOL * pfHandled)
{
HRESULT hres = E_FAIL;
ASSERT(pfHandled);
*pfHandled = TRUE;
switch(nCmdID)
{
case DOCHOST_DOCCANNAVIGATE:
{
// We only set the navigation window for the top-level browser.
// Even though the WebOC is no longer in frames, it can still
// be present on a web page as a view link or control.
//
DWORD dwFlags = 0;
if ( _pwb )
{
hres = _pwb->GetFlags(&dwFlags);
}
if ((dwFlags & BSF_TOPBROWSER) || _IsInBrowserBand())
{
ATOMICRELEASE(_pHTMLWindow);
if (pvarargIn && VT_UNKNOWN == V_VT(pvarargIn) && V_UNKNOWN(pvarargIn))
{
_fDocCanNavigate = TRUE;
hres = V_UNKNOWN(pvarargIn)->QueryInterface(IID_IHTMLWindow2,
(void**)&_pHTMLWindow);
}
else
{
_fDocCanNavigate = FALSE;
}
}
// Pass to the parent shell browser.
if (_pmsoctBrowser)
{
hres = _pmsoctBrowser->Exec(&CGID_DocHostCmdPriv, nCmdID,
nCmdexecopt, pvarargIn, pvarargOut);
}
}
break;
case DOCHOST_READYSTATE_INTERACTIVE:
if ( _pScriptErrList != NULL)
ClearScriptError();
hres = S_OK;
break;
case DOCHOST_NAVIGATION_ERROR:
hres = _HandleFailedNavigation(pvarargIn, pvarargOut );
break;
case DOCHOST_NOTE_ERROR_PAGE:
_fErrorPage = TRUE;
break;
case DOCHOST_CONTENTDISPOSITIONATTACH:
hres = _OnContentDisposition();
break;
case DOCHOST_RESETSEARCHINFO:
// Reset search info.
_bsc._SetSearchInfo(this, 0, FALSE, FALSE, FALSE);
break;
case DOCHOST_SENDINGREQUEST:
_OnSetProgressPos(0, PROGRESS_SENDING);
_fShowProgressCtl = TRUE;
_PlaceProgressBar(TRUE);
hres = S_OK;
break;
case DOCHOST_FINDINGRESOURCE:
_OnSetProgressPos(0, PROGRESS_FINDING);
_fShowProgressCtl = TRUE;
_PlaceProgressBar(TRUE);
hres = S_OK;
break;
// Allow the command ID to be passed down the Exec chain.
//
default:
*pfHandled = FALSE;
break;
}
return (S_FALSE == hres) ? S_OK : hres;
}
//+---------------------------------------------------------------------------
//
// Member : CDocObjectHost::_HandleShdocvwCmds
//
// Synopsis : Handles the CMD IDs for CGID_ShellDocView.
//
//+---------------------------------------------------------------------------
BOOL
CDocObjectHost::_HandleShdocvwCmds(DWORD nCmdID,
DWORD nCmdexecopt,
VARIANTARG * pvarargIn,
VARIANTARG * pvarargOut)
{
BOOL fHandled = TRUE;
switch(nCmdID)
{
case SHDVID_STARTPICSFORWINDOW:
_StartPicsForWindow(pvarargIn, pvarargOut);
break;
case SHDVID_CANCELPICSFORWINDOW:
_CancelPicsForWindow(pvarargIn);
break;
case SHDVID_ISPICSENABLED:
_IsPicsEnabled(pvarargOut);
break;
default:
fHandled = FALSE;
}
return fHandled;
}
//+-----------------------------------------------------------------------------
//
// Member : CDocObjectHost::_StartPicsForWindow
//
//+-----------------------------------------------------------------------------
void
CDocObjectHost::_StartPicsForWindow(VARIANTARG * pvarargIn, VARIANTARG * pvarargOut)
{
ASSERT(pvarargIn);
ASSERT(VT_UNKNOWN == V_VT(pvarargIn));
ASSERT(V_UNKNOWN(pvarargIn));
ASSERT(pvarargOut);
ASSERT(V_VT(pvarargOut) == VT_EMPTY);
IHTMLPrivateWindow * pPrivWin;
V_VT(pvarargOut) = VT_BOOL;
V_BOOL(pvarargOut) = VARIANT_FALSE;
if (SUCCEEDED(V_UNKNOWN(pvarargIn)->QueryInterface(IID_IHTMLPrivateWindow, (void**)&pPrivWin)))
{
// Ignore the HR
//
if (_StartSecondaryPicsProcessor(pPrivWin) == S_OK)
{
V_BOOL(pvarargOut) = VARIANT_TRUE;
}
pPrivWin->Release();
}
}
BOOL
CDocObjectHost::_IsInBrowserBand() const
{
if (_psp)
{
IShellBrowser * pShlBrowser;
HRESULT hr = _psp->QueryService(SID_SProxyBrowser,
IID_PPV_ARG(IShellBrowser, &pShlBrowser));
if (SUCCEEDED(hr))
{
pShlBrowser->Release();
return TRUE;
}
}
return FALSE;
}
//+-----------------------------------------------------------------------------
//
// Member : CDocObjectHost::_CancelPicsForWindow
//
//+-----------------------------------------------------------------------------
void
CDocObjectHost::_CancelPicsForWindow(VARIANTARG * pvarargIn)
{
ASSERT(pvarargIn);
ASSERT(VT_UNKNOWN == V_VT(pvarargIn));
ASSERT(V_UNKNOWN(pvarargIn));
IUnknown * pUnkPrivWin;
if (SUCCEEDED(V_UNKNOWN(pvarargIn)->QueryInterface(IID_IUnknown, (void**)&pUnkPrivWin)))
{
_RemovePicsProcessorByPrivWindowUnk(pUnkPrivWin);
pUnkPrivWin->Release();
}
}
//+-----------------------------------------------------------------------------
//
// Member : CDocObjectHost::_IsPicsEnabled
//
// Synopsis : Returns a variant that specifies whether or not PICS is enabled.
//
//+-----------------------------------------------------------------------------
void
CDocObjectHost::_IsPicsEnabled(VARIANTARG * pvarargOut)
{
ASSERT(pvarargOut);
ASSERT(VT_EMPTY == V_VT(pvarargOut));
V_VT(pvarargOut) = VT_BOOL;
V_BOOL(pvarargOut) = VARIANT_FALSE;
IRatingNotification* pRatingNotify;
HRESULT hres = QueryService(SID_SRatingNotification, IID_PPV_ARG(IRatingNotification, &pRatingNotify));
if (SUCCEEDED(hres))
{
V_BOOL(pvarargOut) = pRatingNotify->IsRatingsEnabled() ? VARIANT_FALSE : VARIANT_TRUE;
pRatingNotify->Release();
}
else
{
if (IS_RATINGS_ENABLED() && S_OK == ::RatingEnabledQuery())
{
V_BOOL(pvarargOut) = VARIANT_TRUE;
}
}
}
//+-----------------------------------------------------------------------------
//
// Member : CDocObjectHost::_UpdateState
//
// Synopsis : Updates the state of the dochost.
//
//+-----------------------------------------------------------------------------
HRESULT
CDocObjectHost::_UpdateState(LPITEMIDLIST pidl, BOOL fIsErrorUrl)
{
HRESULT hres;
if (_fSetSecureLock)
{
_ForwardSetSecureLock(_eSecureLock);
}
ASSERT(_pwb);
if (!fIsErrorUrl)
{
ResetRefreshUrl();
}
IMoniker * pmk;
BOOL fFileProtocol;
hres = ::_URLMONMonikerFromPidl(pidl, &pmk, &fFileProtocol);
if (S_OK == hres)
{
ATOMICRELEASE(_pmkCur);
_pmkCur = pmk;
DEBUG_CODE(_TraceMonikerDbg(_pmkCur, _T("CDocObjectHost::_UpdateState")));
}
_fFileProtocol = fFileProtocol;
// This is only set if we did a successful LoadHistory()
//
_fIsHistoricalObject = FALSE;
// This flag used to be set to false in IE5
// for each navigation because the dochost was
// destroyed and a new one was created. Now that Trident
// knows how to navigate, this flag doesn't get reset. This
// prevents activation of the view in the case where a
// modal dialog is being displayed.
//
_fReadystateInteractiveProcessed = FALSE;
return hres;
}
//+-----------------------------------------------------------------------------
//
// Member : CDocObjectHost::_Init
//
// Synopsis : Clears the cached redirection URL in case the previous navigation
// was a redirection. THis is needed so that further navigations to https://
// sites don't get the redirection URL as their SSL base.
//+-----------------------------------------------------------------------------
void
CDocObjectHost::_Init()
{
_fDelegatedNavigation = FALSE;
_fErrorPage = FALSE;
if (_bsc._pszRedirectedURL)
{
LocalFree(_bsc._pszRedirectedURL);
_bsc._pszRedirectedURL = NULL;
}
}
#ifdef DEBUG
void
CDocObjectHost::_TraceMonikerDbg(IMoniker * pmk, TCHAR * pszCaller) const
{
ASSERT(pmk);
LPOLESTR pstrDisplayName;
HRESULT hr = pmk->GetDisplayName(NULL, NULL, &pstrDisplayName);
if (S_OK == hr)
{
TraceMsg(DM_TRACE, "%ws - Moniker=\"%ws\"", pszCaller, pstrDisplayName);
OleFree(pstrDisplayName);
}
}
#endif
//+-------------------------------------------------------------------------
//
// Member : CDocObjectHost::_HandleFailedNavigation
//
// Synopsis : Handles a failed navigation by initiating
// the AutoSearch function or displaying an
// error page.
//
// Scenario : - The user navigates to a bogus URL such as "sdfg".
// - _HandleFailedNavigation is called via Exec with
// DOCHOST_NAVIGATION_ERROR.
// - The AutoSearch initiates with a search index of 0.
// - AutoSearch expands the URL with the first
// UrlTemplate from the registry (usually www.%s.com).
// - Navigation to the new URL is attempted.
// - Upon failure, this method is called again with
// an error code of HTTP_STATUS_BAD_GATEWAY or
// HTTP_STATUS_GATEWAY_TIMEOUT.
// - The search index in the property bag is incremented (by
// two if it is currently 0.)
// - AutoSearch then tries the next UrlTemplate, and so on.
//
// If this method is called with an error code other than
// HTTP_STATUS_BAD_GATEWAY, HTTP_STATUS_GATEWAY_TIMEOUT,
// INET_E_RESOURCE_NOT_FOUND, INET_E_DATA_NOT_AVAILABLE or
// if the error code is INET_E_RESOURCE_NOT_FOUND or
// INET_E_DATA_NOT_AVAILABLEthe and the URL entered by the
// user contains a protocol identifier (e.g., http://) an
// error page contained in shdoclc.dll is displayed.
//
// Input : pvarargIn - a SafeArray that contains the following
// data in this order.
// 0 - Binding error or HTTP status code. (VT_I4)
// 1 - URL being navigated to. (VT_BSTR)
// 2 - IBinding interface (VT_UNKNOWN)
// 3 - IHTMLWindow2 of the current window (VT_UNKNOWN)
//
//-------------------------------------------------------------------------
HRESULT
CDocObjectHost::_HandleFailedNavigation(VARIANTARG * pvarargIn, VARIANTARG* pvarargOut /*=NULL*/)
{
HRESULT hr = E_FAIL;
ASSERT(pvarargIn);
ASSERT(_fDocCanNavigate);
if (pvarargIn && VT_ARRAY == V_VT(pvarargIn) && V_ARRAY(pvarargIn))
{
// Get the error code from the SafeArray.
//
CComVariant cvarErrorCode;
CComVariant cvarAddrBarNav;
CComVariant cvarRefresh;
long lIdx = 0;
DWORD dwError = ERRORPAGE_DNS;
BOOL fShouldDisplayError = TRUE;
BOOL fDidSuperNavigate = TRUE;
//
// We use to use the window only in one place. To avoid QI several times
// We QI just before the first use, and keep track if we were successful.
//
CComVariant cvarWindow;
IHTMLWindow2 * pCurrentWindow = NULL;
HRESULT hrWinQI = E_FAIL;
hr = SafeArrayGetElement(V_ARRAY(pvarargIn), &lIdx, &cvarErrorCode);
if (SUCCEEDED(hr) && V_VT(&cvarErrorCode) == VT_I4)
{
lIdx = 4;
hr = SafeArrayGetElement(V_ARRAY(pvarargIn), &lIdx, &cvarAddrBarNav);
if (SUCCEEDED(hr) && V_VT(&cvarAddrBarNav) == VT_BOOL)
{
// We fire NavigateError and if the host wishes to cancel
// we can skip the rest of this method.
BOOL fCancelAutoSearch = FALSE;
DWORD dwStatusCode = V_I4(&cvarErrorCode);
CComVariant cvarWindow;
// Get the pending URL from the SafeArray.
//
CComVariant cvarUrl;
lIdx = 1;
hr = SafeArrayGetElement(V_ARRAY(pvarargIn), &lIdx, &cvarUrl);
if (SUCCEEDED(hr) && (VT_BSTR == V_VT(&cvarUrl)) && V_BSTR(&cvarUrl))
{
// Get the current window from the SafeArray.
//
lIdx = 3;
hr = SafeArrayGetElement(V_ARRAY(pvarargIn), &lIdx, &cvarWindow);
if (SUCCEEDED(hr) && V_VT(&cvarWindow) == VT_UNKNOWN && V_UNKNOWN(&cvarWindow))
{
hr = V_UNKNOWN(&cvarWindow)->QueryInterface(IID_IHTMLWindow2, (void**)&pCurrentWindow);
_FireNavigateErrorHelper(SUCCEEDED(hr) ? pCurrentWindow : NULL,
dwStatusCode,
&fCancelAutoSearch,
V_BSTR(&cvarUrl));
hrWinQI = hr;
}
}
if (fCancelAutoSearch)
{
if (!_fCanceledByBrowser)
{
_CancelPendingNavigation(FALSE, TRUE);
}
return hr;
}
lIdx = 0;
switch (V_I4(&cvarErrorCode))
{
case HTTP_STATUS_BAD_GATEWAY:
case HTTP_STATUS_GATEWAY_TIMEOUT:
if (VARIANT_TRUE == V_BOOL(&cvarAddrBarNav))
{
hr = _DoAutoSearch(pvarargIn,
++lIdx,
V_I4(&cvarErrorCode),
FALSE,
&fShouldDisplayError);
if ( fShouldDisplayError )
{
fDidSuperNavigate = FALSE;
}
}
break;
// Only autosearch if the error code is
// INET_E_RESOURCE_NOT_FOUND or INET_E_DATA_NOT_AVAILABLE
//
case INET_E_RESOURCE_NOT_FOUND:
case INET_E_DATA_NOT_AVAILABLE:
if (VARIANT_TRUE == V_BOOL(&cvarAddrBarNav))
{
hr = _DoAutoSearch(pvarargIn,
++lIdx,
0,
TRUE,
&fShouldDisplayError);
// We must reset here so that the index will be
// correct the next time around.
//
}
// Intentional fall-through
case INET_E_DOWNLOAD_FAILURE:
if (IsGlobalOffline())
break;
// otherwise fall through to do default handling
default:
if (hr || fShouldDisplayError)
{
if ( V_I4(&cvarErrorCode) >= HTTP_STATUS_BAD_REQUEST
&& V_I4(&cvarErrorCode) <= HTTP_STATUS_LAST)
{
dwError = V_I4(&cvarErrorCode);
}
// Get the pending URL from the SafeArray.
//
CComVariant cvarUrl;
lIdx = 1;
hr = SafeArrayGetElement(V_ARRAY(pvarargIn), &lIdx, &cvarUrl);
if (SUCCEEDED(hr) && V_VT(&cvarUrl) == VT_BSTR && V_BSTR(&cvarUrl))
{
if (SUCCEEDED(hrWinQI))
{
//
// Get the refresh flag - indicating whether this is a refresh.
// ( this was originally set when we called SuperNavigate )
//
lIdx = 5;
hr = SafeArrayGetElement(V_ARRAY(pvarargIn), &lIdx, &cvarRefresh);
if (SUCCEEDED(hr))
{
hr = _DisplayHttpErrorPage(pCurrentWindow,
V_BSTR(&cvarUrl),
dwError,
V_BOOL(&cvarAddrBarNav),
V_BOOL(&cvarRefresh ) );
ATOMICRELEASE(pCurrentWindow);
}
}
}
}
break;
} // switch
if ( pvarargOut && ( V_VT( pvarargOut ) == VT_BOOL ) )
{
V_BOOL( pvarargOut ) = fDidSuperNavigate ? VARIANT_TRUE : VARIANT_FALSE;
}
} // if (SUCCEEDED(hr) && V_VT(&cvarAddrBarNav) == VT_BOOL)
} // if (SUCCEEDED(hr) && V_VT(&cvarErrorCode) == VT_I4)
}
return (S_FALSE == hr ? S_OK : hr);
}
//+------------------------------------------------------------------
//
// Member : CDocObjectHost::_DoAutoSearch
//
// Synopsis : Performs the autosearch function.
//
// Input : pvarargIn - a SafeArray of arguments. See
// _HandleFailedNavigation for info about
// the format of pvarargIn.
// lStartIdx - the position in the SafeArray where
// the data begins.
// dwStatusCode - the HTTP status code.
//
// Output : pfShouldDisplayError - TRUE if an error page
// should be displayed.
//
//------------------------------------------------------------------
HRESULT
CDocObjectHost::_DoAutoSearch(VARIANTARG * pvarargIn,
long lStartIdx,
DWORD dwStatusCode,
BOOL fAddMRU,
BOOL * pfShouldDisplayError)
{
ASSERT(pvarargIn);
ASSERT(_fDocCanNavigate);
ASSERT(pfShouldDisplayError);
*pfShouldDisplayError = TRUE;
// Url
CComVariant cvarUrl;
HRESULT hr = SafeArrayGetElement(V_ARRAY(pvarargIn), &lStartIdx, &cvarUrl);
if (SUCCEEDED(hr) && V_VT(&cvarUrl) == VT_BSTR && V_BSTR(&cvarUrl))
{
CComVariant cvarBinding;
IBinding * pBinding;
// Binding interface pointer
lStartIdx++;
hr = SafeArrayGetElement(V_ARRAY(pvarargIn), &lStartIdx, &cvarBinding);
if (SUCCEEDED(hr) && V_VT(&cvarBinding) == VT_UNKNOWN && V_UNKNOWN(&cvarBinding))
{
hr = V_UNKNOWN(&cvarBinding)->QueryInterface(IID_IBinding, (void**)&pBinding);
if (SUCCEEDED(hr))
{
hr = _bsc._HandleFailedNavigationSearch(pfShouldDisplayError, dwStatusCode, this, 0, V_BSTR(&cvarUrl),
L"Resource Not Found", pBinding, fAddMRU, TRUE);
if (hr == S_FALSE)
_fErrorPage = TRUE; // Don't update the history if no auto-search
ATOMICRELEASE(pBinding);
}
}
}
return (S_FALSE == hr ? S_OK : hr);
}
//+---------------------------------------------------------------
//
// Member : CDocObjectHost::_DisplayHttpErrorPage
//
// Synopsis : Displays the HTML page that corresponds to
// the given error code.
//
// Input : pCurrentWindow - the window to use for navigation.
// dwError - the error code.
//
//---------------------------------------------------------------
HRESULT
CDocObjectHost::_DisplayHttpErrorPage(IHTMLWindow2 * pCurrentWindow,
BSTR bstrUrl,
DWORD dwError,
BOOL fAddrBarNav,
BOOL fRefresh /*=FALSE*/)
{
HRESULT hr = E_FAIL;
TCHAR szErrorUrl[MAX_URL_STRING];
const WCHAR * const pszFmt = L"#%s";
IHTMLPrivateWindow * pPrivWindow = NULL;
ASSERT(pCurrentWindow);
ASSERT(_fDocCanNavigate);
_bsc._DontAddToMRU(this);
if (IsErrorHandled(dwError))
{
_fErrorPage = TRUE;
if (_bsc._DisplayFriendlyHttpErrors())
{
hr = MLBuildResURLWrap(_T("shdoclc.dll"),
HINST_THISDLL,
ML_CROSSCODEPAGE,
(TCHAR*)c_aErrorUrls[EUIndexFromError(dwError)].pszUrl,
szErrorUrl,
ARRAYSIZE(szErrorUrl),
_T("shdocvw.dll"));
if (SUCCEEDED(hr))
{
LPOLESTR pwszLocation = OLE2W(bstrUrl);
if (!IsFrameWindow(pCurrentWindow))
{
// Save the url the user attempted to navigate to. It will be used
// to refresh the page.
//
OleFree(_pwszRefreshUrl);
hr = SHStrDupW(OLE2W(bstrUrl), &_pwszRefreshUrl);
}
if (SUCCEEDED(hr))
{
int nLenWritten = lstrlen(szErrorUrl);
// Append the #<refresh URL>
//
wnsprintf(szErrorUrl + nLenWritten,
ARRAYSIZE(szErrorUrl) - nLenWritten,
pszFmt,
pwszLocation ? pwszLocation : L"");
hr = pCurrentWindow->QueryInterface(IID_IHTMLPrivateWindow,
(void**)&pPrivWindow);
if (SUCCEEDED(hr))
{
// Navigate to the URL
//
BSTR bstrErrorUrl = SysAllocString(szErrorUrl);
DWORD dwFlags = (fAddrBarNav ?
DOCNAVFLAG_DONTUPDATETLOG | DOCNAVFLAG_HTTPERRORPAGE :
DOCNAVFLAG_HTTPERRORPAGE) ;
if ( fRefresh )
dwFlags |= DOCNAVFLAG_REFRESH;
if (bstrErrorUrl)
{
hr = pPrivWindow->SuperNavigate(bstrErrorUrl,
NULL,
NULL,
NULL,
NULL,
NULL,
dwFlags);
SysFreeString(bstrErrorUrl);
}
pPrivWindow->Release();
}
}
}
}
}
return hr;
}
HRESULT CDocObjectHost::_GetUrlVariant(VARIANT *pvarargOut)
{
ASSERT( pvarargOut);
if (_pmkCur)
{
LPOLESTR pszDisplayName = NULL;
LPTSTR pszRedirectedURL = NULL;
if (_bsc._pszRedirectedURL && *_bsc._pszRedirectedURL)
pszRedirectedURL = _bsc._pszRedirectedURL;
if (pszRedirectedURL || SUCCEEDED(_GetCurrentPageW(&pszDisplayName, TRUE)))
{
pvarargOut->bstrVal = SysAllocString(pszRedirectedURL ? pszRedirectedURL : pszDisplayName);
if (pvarargOut->bstrVal)
pvarargOut->vt = VT_BSTR;
if (pszDisplayName)
OleFree(pszDisplayName);
}
}
return (pvarargOut->bstrVal == NULL) ? E_FAIL : S_OK;
}
HRESULT CDocObjectHost::_CoCreateHTMLDocument(REFIID riid, void ** ppvOut)
{
IOleCommandTarget* pcmd;
HRESULT hres = QueryService(SID_STopLevelBrowser, IID_IOleCommandTarget, (void **)&pcmd);
if (SUCCEEDED(hres)) {
VARIANT varOut = { 0 };
hres = pcmd->Exec(&CGID_Explorer, SBCMDID_COCREATEDOCUMENT, 0, NULL, &varOut);
if (SUCCEEDED(hres) && varOut.vt == VT_UNKNOWN) {
hres = varOut.punkVal->QueryInterface(riid, ppvOut);
// Clean it up by ourself so that we don't load OLEAUT32
varOut.punkVal->Release();
} else {
ASSERT(varOut.vt == VT_EMPTY);
VariantClear(&varOut);
}
pcmd->Release();
}
return hres;
}
HRESULT CDocObjectHost::_CreatePendingDocObject(BOOL fMustInit, BOOL fWindowOpen /* = FALSE */)
{
HRESULT hres = S_OK;
if (_punkPending == NULL)
{
hres = _CoCreateHTMLDocument(IID_IUnknown, (void **)&_punkPending);
_fPendingNeedsInit = 1; // lazy InitNew only if absolutely necessary
if (fWindowOpen)
{
IUnknown_Exec(_punkPending, &CGID_ShellDocView, SHDVID_WINDOWOPEN, 0, NULL, NULL);
}
}
if (_fPendingNeedsInit && fMustInit && SUCCEEDED(hres))
{
IOleObject * polePending;
#ifdef TRIDENT_NEEDS_LOCKRUNNING
IRunnableObject * pro;
#endif
_fCreatingPending = 1; // we are creating _punkPending
_fAbortCreatePending = 0;
_fPendingNeedsInit = 0;
IPersistStreamInit * pipsi;
hres = _punkPending->QueryInterface(IID_IPersistStreamInit, (void**)&pipsi);
if (SUCCEEDED(hres))
{
hres = pipsi->InitNew();
pipsi->Release();
}
// if the InitNew is a re-entrant request (such as doing execDown to get a securityctx
// while in the process of loading the document), trident will respond with E_PENDING
// since there is already a load in progress, this call/init is a timing issue, and
// we can use the exisitng one.
//
if (SUCCEEDED(hres) || hres==E_PENDING)
{
hres = _punkPending->QueryInterface(IID_IOleObject, (void**)&polePending);
if (SUCCEEDED(hres))
{
hres = polePending->SetClientSite(this);
polePending->Release();
}
#ifdef TRIDENT_NEEDS_LOCKRUNNING
// TRIDENT NO LONGER SUPPORTS IRunnableObject
// RegisterObjectParam/RevokeObjectParam calls LockRunning on object being
// registered. LockRunning(FALSE,FALSE) implied in the Revoke will result
// in OleClose being called on _punkPending if we haven't activated it
// by end of binding. Thus we must call LockRunning ourself
if (SUCCEEDED(hres))
{
hres = _punkPending->QueryInterface(IID_IRunnableObject, (void**)&pro);
if (SUCCEEDED(hres))
{
hres = pro->LockRunning(TRUE, TRUE);
pro->Release();
}
}
#endif
}
_fCreatingPending = 0;
_fPendingWasInited = 1;
if (FAILED(hres))
{
SAFERELEASE(_punkPending);
}
else if (_fAbortCreatePending)
{
// Detect AOL pumping messages and reentering and attempting to release
// _punkPending
_fAbortCreatePending = 0;
_ReleasePendingObject();
hres = E_FAIL;
}
else
{
// Pass URL for pending object to it in advance of IPersistMoniker::Load
//
// Notes: We don't want to call _GetUrlVariant which will load
// OLEAUT32.DLL
//
LPOLESTR pszDisplayName = NULL;
LPTSTR pszURL = NULL;
if (_bsc._pszRedirectedURL && *_bsc._pszRedirectedURL)
pszURL = _bsc._pszRedirectedURL;
if (pszURL || SUCCEEDED(_GetCurrentPageW(&pszDisplayName, TRUE)))
{
LBSTR::CString strDisplay;
strDisplay = ( pszURL ? pszURL : pszDisplayName );
VARIANT varIn;
varIn.vt = VT_BSTR;
varIn.bstrVal = strDisplay;
IUnknown_Exec(_punkPending, &CGID_ShellDocView, SHDVID_SETPENDINGURL, 0, &varIn, NULL);
if (pszDisplayName)
OleFree(pszDisplayName);
}
}
_fAbortCreatePending = 0;
}
return hres;
}
HRESULT
CDocObjectHost::_LoadDocument()
{
if (!_punkPending)
return E_FAIL;
IPersistMoniker * pPersistMk;
HRESULT hres = _punkPending->QueryInterface(IID_IPersistMoniker, (void**)&pPersistMk);
if (SUCCEEDED(hres))
{
ITridentService * pTridentSvc;
if ( _pwb )
{
hres = _pwb->QueryInterface(IID_ITridentService, (void**)&pTridentSvc);
}
else
{
TraceMsg( TF_ERROR, "CDocObjectHost::_LoadDocument() - _pwb is NULL!" );
hres = E_FAIL;
}
if (SUCCEEDED(hres))
{
BSTR bstrUrl;
hres = pTridentSvc->GetPendingUrl(&bstrUrl);
if (SUCCEEDED(hres))
{
IMoniker * pMoniker;
TCHAR *pstr;
// Parse the URL, removing any location info
pstr = wcsrchr(bstrUrl, '#');
if (pstr)
{
*pstr = '\0';
}
hres = CreateURLMoniker(NULL, bstrUrl, &pMoniker);
if (SUCCEEDED(hres))
{
ATOMICRELEASE(_pole);
hres = _punkPending->QueryInterface(IID_IOleObject, (void**)&_pole);
if (SUCCEEDED(hres))
{
_GetAppHack();
// Call _SetUpTransitionCapability() to set up the advisory sinks
// and set readystate to complete. If we don't do this here, the
// view will never be activated after the first navigation
// which means that the view will never switched and the
// new document will not be displayed. Also, setting readystate
// to complete here, is what prevents the window from being transparent
// when it is first opened.
//
_SetUpTransitionCapability(TRUE);
}
hres = pPersistMk->Load(TRUE, pMoniker, _pbcCur, 0);
pMoniker->Release();
}
SysFreeString(bstrUrl);
}
pTridentSvc->Release();
}
pPersistMk->Release();
}
return S_OK;
}
// called from CDocObjectView to exec and forward these calls down
//
HRESULT CDocObjectHost::ExecDown(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
{
HRESULT hres = OLECMDERR_E_UNKNOWNGROUP;
// Special case Exec's that are used to fetch info on pending docobject
// for scripting access before OnObjectAvailable
if (pguidCmdGroup && IsEqualGUID(CGID_ShellDocView, *pguidCmdGroup))
{
switch(nCmdID)
{
case SHDVID_GETPENDINGOBJECT:
ASSERT( pvarargOut);
VariantClearLazy(pvarargOut);
if (_pole)
{
_pole->QueryInterface(IID_IUnknown, (void **) &(pvarargOut->punkVal));
// Check to see if this is a window.open case. If so,
// the document was created and Init'ed in SetTarget
// and the real Url will be loaded now.
//
if (_pbcCur)
{
IUnknown * punkBindCtxParam = NULL;
hres = _pbcCur->GetObjectParam(KEY_BINDCONTEXTPARAM, &punkBindCtxParam);
if (SUCCEEDED(hres))
{
punkBindCtxParam->Release();
_fWindowOpen = TRUE;
hres = _LoadDocument();
_ActivateMsoView();
}
}
}
else
{
_CreatePendingDocObject(TRUE);
if (_punkPending)
{
pvarargOut->punkVal = _punkPending;
_punkPending->AddRef();
}
else if (_pole)
{
_pole->QueryInterface(IID_IUnknown, (void **) &(pvarargOut->punkVal));
}
}
if (pvarargOut->punkVal != NULL)
{
pvarargOut->vt = VT_UNKNOWN;
hres = S_OK;
}
else
{
hres = E_FAIL;
}
return hres;
break;
case SHDVID_GETPENDINGURL:
ASSERT( pvarargOut);
VariantClearLazy(pvarargOut);
hres = _GetUrlVariant(pvarargOut);
return hres;
break;
default:
break;
}
}
BOOL fNearest = _ExecNearest(pguidCmdGroup, nCmdID, TRUE);
if (fNearest)
hres = OnExec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
if (FAILED(hres) && _pmsot) {
hres = _pmsot->Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
//
// APPHACK:
// PPT in Office 97 fails to print if we pass PRINTFLAG_PROMPTUSER
// and returns E_INVALIDARG. If we detect this case, we should retry
// without this flag. PPT will popup the print dialog. (SatoNa)
//
if (hres == E_INVALIDARG
&& (_dwAppHack & BROWSERFLAG_PRINTPROMPTUI)
&& pguidCmdGroup == NULL
&& nCmdID == OLECMDID_PRINT)
{
TraceMsg(TF_SHDAPPHACK, "DOH::ExecDown(OLECMDID_PRINT) removing PRINTFLAG_PROMPTUSER");
nCmdexecopt &= ~OLECMDEXECOPT_DONTPROMPTUSER;
hres = _pmsot->Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
}
}
if (FAILED(hres) && !fNearest)
hres = OnExec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
return hres;
}
HRESULT CDocObjectHost::QueryStatusDown(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
{
HRESULT hres;
if (_pmsot)
hres = _pmsot->QueryStatus(pguidCmdGroup, cCmds, rgCmds, pcmdtext);
else if (pguidCmdGroup && IsEqualGUID(CGID_ShellDocView, *pguidCmdGroup))
hres = IUnknown_QueryStatus(_pole, pguidCmdGroup, cCmds, rgCmds, pcmdtext);
return OnQueryStatus(pguidCmdGroup, cCmds, rgCmds, pcmdtext, hres);
}
HRESULT CDocObjectHost::Invoke(DISPID dispidMember, REFIID iid, LCID lcid, WORD wFlags, DISPPARAMS FAR* pdispparams,
VARIANT FAR* pVarResult,EXCEPINFO FAR* pexcepinfo,UINT FAR* puArgErr)
{
if (!_peds)
return(E_NOTIMPL);
return _peds->OnInvoke(dispidMember, iid, lcid, wFlags, pdispparams, pVarResult,pexcepinfo,puArgErr);
}
//*** IOleControlSite {
HRESULT CDocObjectHost::OnControlInfoChanged()
{
HRESULT hres = E_NOTIMPL;
if (_pedsHelper)
{
hres = _pedsHelper->OnOnControlInfoChanged();
}
return(hres);
}
//*** CDOH::TranslateAccelerator (IOCS::TranslateAccelerator)
// NOTES
// trident (or any other DO that uses IOCS::TA) calls us back when TABing
// off the last link. to handle it, we flag it for our original caller
// (IOIPAO::TA), and then pretend we handled it by telling trident S_OK.
// trident returns S_OK to IOIPAO::TA, which checks the flag and says
// 'trident did *not* handle it' by returning S_FALSE. that propagates
// way up to the top where it sees it was a TAB so it does a CycleFocus.
//
// that's how we do it when we're top-level. when we're a frameset, we
// need to do it the 'real' way, sending it up to our parent IOCS.
HRESULT CDocObjectHost::TranslateAccelerator(MSG __RPC_FAR *pmsg,DWORD grfModifiers)
{
HRESULT hres = S_FALSE;
if (_peds) {
// try it the real way in case we're in a frameset
// top level: we'll do CImpIExpDispSupport::OnTA which does E_NOTIMPL,
// frameset: we'll do CWebBrowserOC::OnTA which talks to trident
// What if trident (or OC?) gives back E_NOTIMPL too?
TraceMsg(DM_FOCUS, "DOH::IOCS::TA peds!=NULL forward");
hres = _peds->OnTranslateAccelerator(pmsg, grfModifiers);
}
if (hres != S_OK) {
// we're at top level (E_NOTIMPL), so we can fake it
// (or alternately we're not, but our parent said S_FALSE)
#ifdef DEBUG
if (_peds && SUCCEEDED(hres)) {
// i'm curious if we ever hit this
TraceMsg(DM_WARNING, "DOH::IOCS::TA parent hres=%x (!=S_OK)", hres);
}
#endif
hres = S_FALSE;
if (IsVK_TABCycler(pmsg)) {
TraceMsg(TF_SHDUIACTIVATE, "DOH::TranslateAccelerator called with VK_TAB");
TraceMsg(DM_FOCUS, "DOH::IOCS::TA(wParam=VK_TAB) ret _fCycleFocus=TRUE hr=S_OK (lie)");
// defer it, set flag for CDOH::IOIPAO::TA, and pretend we handled it
ASSERT(!_fCycleFocus);
_fCycleFocus = TRUE;
hres = S_OK;
}
}
return hres;
}
// }
//========================================================================
// CDocObjectHost::CPicsProcessor
//========================================================================
CDocObjectHost::CPicsProcessor::CPicsProcessor()
{
_fPicsAccessAllowed = TRUE; /* assume no ratings checks unless we download */
_fInDialog = FALSE;
_fTerminated = FALSE;
_fbPicsWaitFlags = 0;
}
STDMETHODIMP CDocObjectHost::CPicsProcessor::QueryInterface(REFIID riid, void ** ppvObj)
{
if (IsEqualIID(riid, IID_IOleCommandTarget) ||
IsEqualIID(riid, IID_IUnknown))
{
*ppvObj = SAFECAST(this, IOleCommandTarget *);
}
else
{
*ppvObj = NULL;
return E_NOINTERFACE;
}
CDocObjectHost* pdoh = _pdoh;
return pdoh->AddRef();
}
STDMETHODIMP_(ULONG) CDocObjectHost::CPicsProcessor::AddRef(void)
{
return _pdoh->AddRef();
}
STDMETHODIMP_(ULONG) CDocObjectHost::CPicsProcessor::Release(void)
{
return _pdoh->Release();
}
STDMETHODIMP CDocObjectHost::CPicsProcessor::QueryStatus(const GUID *pguidCmdGroup,
ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
{
return E_NOTIMPL;
}
STDMETHODIMP CDocObjectHost::CPicsProcessor::Exec(const GUID *pguidCmdGroup,
DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
{
if (IsEqualGUID(CGID_ShellDocView, *pguidCmdGroup))
{
switch (nCmdID)
{
case SHDVID_PICSLABELFOUND:
if (pvarargIn->vt == (VT_BSTR)) {
_dwPicsLabelSource = PICS_LABEL_FROM_PAGE;
_HandleInDocumentLabel(pvarargIn->bstrVal);
}
return NOERROR;
case SHDVID_PICSLABELFOUNDINHTTPHEADER:
if (pvarargIn->vt == (VT_BSTR)) {
_dwPicsLabelSource = PICS_LABEL_FROM_HEADER;
_HandleInDocumentLabel(pvarargIn->bstrVal);
}
return NOERROR;
case SHDVID_NOMOREPICSLABELS:
_HandleDocumentEnd();
return NOERROR;
default:
return OLECMDERR_E_NOTSUPPORTED;
}
}
return OLECMDERR_E_UNKNOWNGROUP;
}
UINT CDocObjectHost::CPicsProcessor::_PicsBlockingDialog()
{
LPCTSTR pszURL = _pszPicsURL;
TraceMsg(DM_PICS, "CDOH::CPP::_PicsBlockingDialog() %s", pszURL);
_StartPicsRootQuery(pszURL);
_pdoh->_fDrawBackground = TRUE;
::InvalidateRect(_pdoh->_hwnd, NULL, TRUE); /* mega cheesy, but only way to get browser window erased */
/* This message loop is used to block in non-HTML cases, where we really
* want to block the download process until ratings are checked. In the
* HTML case, this function is never called until the wait flags are all
* clear, so the message loop is skipped and we go straight to the denial
* dialog.
*/
while (_fbPicsWaitFlags) {
TraceMsg(DM_PICS, "CDOH::CPP::_PicsBlockingDialog entering msg loop, waitflags=%x", (DWORD)_fbPicsWaitFlags);
MSG msg;
if (GetMessage(&msg, NULL, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
if (!_fPicsAccessAllowed) {
TraceMsg(DM_PICS, "CDOH::CPP::_PicsBlockingDialog, access denied");
BOOL fOldInDialog;
// If this is silent-mode (no UI == screensaver), always deny access
// without any dialog.
BOOL fFrameIsSilent = FALSE; // Assume non-silent
_pdoh->_GetOfflineSilent(NULL, &fFrameIsSilent);
if (fFrameIsSilent) {
TraceMsg(DM_PICS, "CDOH::CPP::_PicsBlockingDialog access denied in silent mode, aborting");
return IDCANCEL;
}
// Protect against us getting whacked out
// from under ourselves
fOldInDialog = _fInDialog;
_fInDialog = TRUE;
// This doesn't get down to trident to stop it from navigating.
// We need some sort of navigation freeze mechanism.
_pdoh->_EnableModeless(FALSE);
HRESULT hres = S_OK;
IOleCommandTarget *pcmdtTop;
if (SUCCEEDED(_pdoh->QueryService(SID_STopLevelBrowser, IID_IOleCommandTarget, (void **)&pcmdtTop))) {
VARIANTARG v = { 0 };
v.vt = VT_INT_PTR;
v.byref = _pRatingDetails;
hres = pcmdtTop->Exec(&CGID_ShellDocView, SHDVID_PICSBLOCKINGUI, 0, &v, NULL);
pcmdtTop->Release();
}
UINT uRet = (hres == S_OK) ? IDOK : IDCANCEL;
_pdoh->_EnableModeless(TRUE);
_fPicsAccessAllowed = (uRet == IDOK);
_fInDialog = fOldInDialog;
TraceMsg(DM_PICS, "CDOH::CPP::_PicsBlockingDialog returning %d", uRet);
return uRet;
}
else {
TraceMsg(DM_PICS, "CDOH::CPP::_PicsBlockingDialog, access allowed");
return IDOK;
}
}
HRESULT CDocObjectHost::CPicsProcessor::_StartPicsQuery(LPCOLESTR pwszRawURL)
{
#ifdef DEBUG
HRESULT hres;
IRatingNotification* pRatingNotify;
hres = _pdoh->QueryService(SID_SRatingNotification, IID_PPV_ARG(IRatingNotification, &pRatingNotify));
if (SUCCEEDED(hres))
{
ASSERT(S_OK == pRatingNotify->IsRatingsEnabled());
pRatingNotify->Release();
}
else
{
ASSERT(IS_RATINGS_ENABLED() && ::RatingEnabledQuery() == S_OK);
}
#endif DEBUG
TraceMsg(DM_PICS, "CDOH::CPP::_StartPicsQuery entered with ratings enabled");
BOOL fEnforce = TRUE;
if (_pszPicsURL != NULL) {
delete _pszPicsURL;
_pszPicsURL = NULL;
}
{
/* We have to call CoInternetGetSecurityUrl to convert pluggable
* protocols into known schemes, so we know whether we need to
* enforce ratings on them.
*/
LPOLESTR pwszSecurityURL = NULL;
if (SUCCEEDED(CoInternetGetSecurityUrl(pwszRawURL, &pwszSecurityURL,
PSU_SECURITY_URL_ONLY, 0)))
{
// List of protocols for which we never enforce ratings.
if (!StrCmpNIW(pwszSecurityURL, L"file:", 5) ||
!StrCmpNIW(pwszSecurityURL, L"about:", 6) ||
!StrCmpNIW(pwszSecurityURL, L"mk:", 3)) {
fEnforce = FALSE;
}
else {
Str_SetPtr(&_pszPicsURL, pwszSecurityURL);
}
OleFree(pwszSecurityURL);
}
}
if (fEnforce) {
TraceMsg(DM_PICS, "CDOH::CPP::_StartPicsQuery (%s) turning on wait flags", _pszPicsURL);
_fbPicsWaitFlags = PICS_WAIT_FOR_ASYNC
| PICS_WAIT_FOR_INDOC
| PICS_WAIT_FOR_END
| PICS_WAIT_FOR_ROOT
;
_fPicsAccessAllowed = 0;
HRESULT hr;
_dwPicsSerialNumber = ::_AddPicsQuery(_pdoh->_hwnd);
if (_dwPicsSerialNumber == 0)
hr = E_OUTOFMEMORY;
else
{
//
// The ratings apis are ansi.
//
CHAR szURL[MAX_URL_STRING];
SHUnicodeToAnsi(_pszPicsURL, szURL, ARRAYSIZE(szURL));
hr = RatingObtainQuery(szURL, _dwPicsSerialNumber, RatingObtainQueryCallback, &_hPicsQuery);
}
if (FAILED(hr)) {
TraceMsg(DM_PICS, "CDOH::CPP::_StartPicsQuery no async query queued");
::_RemovePicsQuery(_dwPicsSerialNumber);
_dwPicsSerialNumber = 0;
_fbPicsWaitFlags &= ~PICS_WAIT_FOR_ASYNC;
}
else {
TraceMsg(DM_PICS, "CDOH::CPP::_StartPicsQuery async query queued");
}
}
else {
// JHarding: IF we're not enforcing, we need to tell anyone who's waiting on an answer.
return S_FALSE;
}
return S_OK;
}
void CDocObjectHost::CPicsProcessor::_HandleDocumentEnd(void)
{
BYTE bFlag = (_pRootDownload != NULL) ? PICS_WAIT_FOR_ROOT : PICS_WAIT_FOR_END;
TraceMsg(DM_PICS, "CDOH::CPP::_HandleDocumentEnd -- no more PICS labels from source %x", (DWORD)bFlag);
// If we have a private window, we'll make sure the root download is gone when we
// notify the window. This simplifies the lifetime of the secondary CPicsProcessors
if (!_pPrivWindow)
{
if (_pRootDownload != NULL) {
::PostMessage(_pdoh->_hwnd, WM_PICS_ROOTDOWNLOADCOMPLETE, 0, 0);
}
else {
/* End of document; revoke the IOleCommandTarget we gave to the document,
* so it won't send us any more notifications.
*/
VARIANTARG v;
v.vt = VT_UNKNOWN;
v.punkVal = NULL;
IUnknown_Exec(_pdoh->_pole, &CGID_ShellDocView, SHDVID_CANSUPPORTPICS, 0, &v, NULL);
}
}
else
{
if (_pRootDownload == NULL)
{
_pPrivWindow->SetPICSTarget(NULL);
}
}
if (!(_fbPicsWaitFlags & bFlag)) {
TraceMsg(DM_PICS, "CDOH::CPP::_HandleDocumentEnd skipping due to waitflags %x", (DWORD)_fbPicsWaitFlags);
return;
}
_fbPicsWaitFlags &= ~PICS_WAIT_FOR_INDOC; /* we know we won't get any more indoc labels */
LPVOID pDetails = NULL;
//
// Ratings has only ansi apis!
//
CHAR szURL[MAX_URL_STRING];
SHUnicodeToAnsi(_pszPicsURL, szURL, ARRAYSIZE(szURL));
HRESULT hres = ::RatingCheckUserAccess(NULL, szURL, NULL, NULL, _dwPicsLabelSource, &pDetails);
_GotLabel(hres, pDetails, bFlag);
if (_pRootDownload == NULL) {
if (_fbPicsWaitFlags)
_StartPicsRootQuery(_pszPicsURL);
}
}
void CDocObjectHost::CPicsProcessor::_GotLabel(HRESULT hres, LPVOID pDetails, BYTE bfSource)
{
TraceMsg(DM_PICS, "CDOH::CPP::_GotLabel hres=%x, source=%x, waitflags=%x", hres, (DWORD)bfSource, (DWORD)_fbPicsWaitFlags);
/* If we've already gotten a result from this or a more significant source,
* ignore this one.
*/
if (!(_fbPicsWaitFlags & bfSource)) {
TraceMsg(DM_PICS, "CDOH::CPP::_GotLabel already got label from that source");
if (pDetails != NULL)
{
::RatingFreeDetails(pDetails);
pDetails = NULL;
}
}
else {
/* If the result is an error somehow (label doesn't apply, etc.), and
* we can expect more labels from this source, then we don't do anything
* except save the rating details if we haven't got any yet.
*/
if (FAILED(hres) && (PICS_MULTIPLE_FLAGS & bfSource)) {
TraceMsg(DM_PICS, "CDOH::CPP::_GotLabel label error and may be multiple");
if (_pRatingDetails == NULL)
{
_pRatingDetails = pDetails;
}
else
{
if ( pDetails )
{
::RatingFreeDetails(pDetails);
pDetails = NULL;
}
}
}
else {
/* Either we got a definitive answer from this rating source, or
* this is the only answer we'll get from it. We clear at least
* the flag for this source so we know we've heard from it. If
* the response was not an error, then clear flags for all less
* significant sources as well, so that we'll ignore them. On
* the other hand, if this source returned an error, it didn't
* give us anything useful, so we keep looking at other sources.
*/
if (SUCCEEDED(hres))
_fbPicsWaitFlags &= bfSource - 1;
else
_fbPicsWaitFlags &= ~bfSource;
TraceMsg(DM_PICS, "CDOH::CPP::_GotLabel, waitflags now %x", (DWORD)_fbPicsWaitFlags);
if (hres == S_OK) {
TraceMsg(DM_PICS, "CDOH::CPP::_GotLabel allowing access");
if ( pDetails )
{
::RatingFreeDetails( pDetails ); /* don't need this if access allowed */
pDetails = NULL;
}
_fPicsAccessAllowed = 1;
}
else {
/* Access denied or error. Meaningful details from this result
* can override details from an earlier, less significant
* result. Only explicitly deny access if not an error,
* though (this handles the valid root label followed by
* invalid in-document label, for example).
*/
if (pDetails != NULL) {
if (_pRatingDetails != NULL)
{
::RatingFreeDetails(_pRatingDetails);
}
_pRatingDetails = pDetails;
}
if (SUCCEEDED(hres))
_fPicsAccessAllowed = 0;
}
}
}
// If we are a secondary processor (_pPrivWindow != NULL) then
// we always want to report when the check is complete
//
if ((_pPrivWindow || _pdoh->_fPicsBlockLate) && !_fbPicsWaitFlags)
{
_HandlePicsChecksComplete();
}
}
void CDocObjectHost::CPicsProcessor::_HandleInDocumentLabel(LPCTSTR pszLabel)
{
BYTE bFlag = (_pRootDownload != NULL) ? PICS_WAIT_FOR_ROOT : PICS_WAIT_FOR_INDOC;
TraceMsg(DM_PICS, "CDOH::CPP::_HandleInDocumentLabel source %x gave label %s", (DWORD)bFlag, pszLabel);
if (!(_fbPicsWaitFlags & bFlag)) {
TraceMsg(DM_PICS, "CDOH::CPP::_HandleInDocumentLabel rejecting based on waitflags %x", (DWORD)_fbPicsWaitFlags);
return;
}
LPVOID pDetails = NULL;
//
// Ratings has only ansi apis!
//
CHAR szURL[MAX_URL_STRING];
SHUnicodeToAnsi(_pszPicsURL, szURL, ARRAYSIZE(szURL));
UINT cbMultiByte = WideCharToMultiByte(CP_ACP, 0, pszLabel,
-1, NULL, 0, NULL, NULL);
if (cbMultiByte > 0) {
char *pszLabelAnsi = new char[cbMultiByte+1];
if (pszLabelAnsi != NULL)
{
if (WideCharToMultiByte(CP_ACP, 0, pszLabel, -1, pszLabelAnsi,
cbMultiByte+1, NULL, NULL))
{
HRESULT hres = ::RatingCheckUserAccess(NULL, szURL,
pszLabelAnsi, NULL, _dwPicsLabelSource,
&pDetails);
_GotLabel(hres, pDetails, bFlag);
}
delete [] pszLabelAnsi;
}
}
}
/* This function parses the URL being downloaded and, if the URL doesn't
* already refer to the root document of the site, sets up a subordinate
* CDocObjectHost to download that root document, so we can get ratings
* out of it.
*/
void CDocObjectHost::CPicsProcessor::_StartPicsRootQuery(LPCTSTR pszURL)
{
if (_fbPicsWaitFlags & PICS_WAIT_FOR_ROOT) {
BOOL fQueued = FALSE;
TraceMsg(DM_PICS, "CDOH::CPP::_StartPicsRootQuery parsing %s", pszURL);
WCHAR wszRootURL[MAX_URL_STRING+1];
DWORD cchResult;
/* The pszURL we're passed is actually the result of calling
* CoInternetGetSecurityUrl, and so may not be the scheme that
* the caller is browsing to. To support pluggable protocols
* determining the root location themselves, we first use the
* URL reported by _GetCurrentPage, which may refer to a
* pluggable protocol; if that fails, we use the more standard
* URL.
*/
HRESULT hres = INET_E_DEFAULT_ACTION;
LPOLESTR pwszURL = NULL;
if (SUCCEEDED(_pdoh->_GetCurrentPageW(&pwszURL, TRUE)))
{
hres = CoInternetParseUrl(pwszURL, PARSE_ROOTDOCUMENT, 0, wszRootURL,
ARRAYSIZE(wszRootURL), &cchResult, 0);
OleFree(pwszURL);
}
if (pszURL != NULL && (hres == INET_E_DEFAULT_ACTION || hres == E_FAIL)) {
/* Pluggable protocol doesn't support PARSE_ROOTDOCUMENT. Use the
* more standard URL we were supplied with.
*/
hres = CoInternetParseUrl(pszURL, PARSE_ROOTDOCUMENT, 0, wszRootURL,
ARRAYSIZE(wszRootURL), &cchResult, 0);
}
if (SUCCEEDED(hres)) {
IMoniker *pmk = NULL;
hres = MonikerFromURL(wszRootURL, &pmk);
if (SUCCEEDED(hres)) {
BOOL fFrameIsSilent = FALSE;
BOOL fFrameIsOffline = FALSE;
_pdoh->_GetOfflineSilent(&fFrameIsOffline, &fFrameIsSilent);
_pRootDownload = new CPicsRootDownload(this, fFrameIsOffline, fFrameIsSilent);
if (_pRootDownload != NULL) {
TraceMsg(DM_PICS, "CDOH::CPP::_StartPicsRootQuery starting download");
hres = _pRootDownload->StartDownload(pmk);
if (SUCCEEDED(hres))
fQueued = TRUE;
}
}
if (pmk != NULL)
pmk->Release();
}
if (!fQueued) {
_fbPicsWaitFlags &= ~PICS_WAIT_FOR_ROOT;
TraceMsg(DM_PICS, "CDOH::CPP::_StartPicsRootQuery queueing failed, waitflags now %x", (DWORD)_fbPicsWaitFlags);
if (!_fbPicsWaitFlags) {
_HandlePicsChecksComplete();
}
}
}
else {
TraceMsg(DM_PICS, "CDOH::CPP::_StartPicsRootQuery no query queued, waitflags=%x", (DWORD)_fbPicsWaitFlags);
}
}
void CDocObjectHost::CPicsProcessor::_HandlePicsChecksComplete(void)
{
if (!_fPicsAccessAllowed)
{
TraceMsg(DM_PICS, "CDOH::CPP::_HandlePicsChecksComplete access denied, posting WM_PICS_DOBLOCKINGUI to hwnd %x", (DWORD_PTR)_pdoh->_hwnd);
/* Allow download of this and other frames to continue while we post
* the denial UI.
*/
if (!PostMessage(_pdoh->_hwnd, WM_PICS_DOBLOCKINGUI, 0, _GetKey())) {
TraceMsg(DM_PICS, "CDOH::CPP::_HandlePicsChecksComplete couldn't post message!");
}
}
else
{
if( _pPrivWindow )
{
TraceMsg(DM_PICS, "CDOH::CPP::_HandlePicsChecksComplete access allowed, posting WM_PICS_RESULTTOPRIVWIN");
if (!PostMessage( _pdoh->_hwnd, WM_PICS_RESULTTOPRIVWIN, IDOK, _GetKey()))
{
TraceMsg(DM_PICS, "CDOH::CPP::_HandlePicsChecksComplete couldn't post message!");
}
}
else
{
TraceMsg(DM_PICS, "CDOH::CPP::_HandlePicsChecksComplete access allowed, execing ACTIVATEMENOW");
if (!_pdoh->_fSetTarget && _pdoh->_pmsoctBrowser)
{
_pdoh->_pmsoctBrowser->Exec(&CGID_ShellDocView, SHDVID_ACTIVATEMENOW, NULL, NULL, NULL);
}
}
}
}
void CDocObjectHost::CPicsProcessor::_ReInit()
{
TraceMsg(DM_PICS, "CDOH::CPP::_ReInit");
_fPicsAccessAllowed = TRUE;
_fInDialog = FALSE;
_fTerminated = FALSE;
_fbPicsWaitFlags = 0;
_dwPicsLabelSource = 0;
if (_pPrivWindow)
{
_pPrivWindow->SetPICSTarget(NULL);
_pPrivWindow->Release();
_pPrivWindow = NULL;
}
if (_pRootDownload != NULL) {
_pRootDownload->CleanUp();
ATOMICRELEASET(_pRootDownload,CPicsRootDownload);
}
_pPicsProcNext = NULL;
if (_pRatingDetails){
::RatingFreeDetails(_pRatingDetails);
_pRatingDetails = NULL;
}
if (_dwPicsSerialNumber) {
::_RemovePicsQuery(_dwPicsSerialNumber);
_dwPicsSerialNumber = 0;
}
if (_hPicsQuery)
{
RatingObtainCancel(_hPicsQuery);
_hPicsQuery = NULL;
}
if ( _pszPicsURL )
{
delete _pszPicsURL;
_pszPicsURL = NULL;
}
}
void
CDocObjectHost::CPicsProcessor::_Terminate()
{
ASSERT( this != &(_pdoh->_PicsProcBase) );
if (!_fInDialog)
{
delete this;
}
else
{
_fTerminated = TRUE;
}
}
CDocObjectHost::CPicsProcessor *
CDocObjectHost::_GetPicsProcessorFromKey(LONG_PTR lKey)
{
CPicsProcessor * ppp = &_PicsProcBase;
while( ppp )
{
if (ppp->_GetKey() == lKey)
return ppp;
ppp = ppp->_pPicsProcNext;
}
return NULL;
}
CDocObjectHost::CPicsProcessor *
CDocObjectHost::_GetPicsProcessorFromSerialNumber(DWORD dwSerial)
{
CPicsProcessor * ppp = &_PicsProcBase;
ASSERT( dwSerial != NULL );
while( ppp )
{
if (ppp->_dwPicsSerialNumber == dwSerial)
return ppp;
ppp = ppp->_pPicsProcNext;
}
return NULL;
}
void CDocObjectHost::_StartBasePicsProcessor()
{
HRESULT hres;
BOOL bRatingsEnabled;
IRatingNotification* pRatingNotify;
hres = QueryService(SID_SRatingNotification, IID_PPV_ARG(IRatingNotification, &pRatingNotify));
if (SUCCEEDED(hres))
{
bRatingsEnabled = pRatingNotify->IsRatingsEnabled() ? FALSE : TRUE;
pRatingNotify->Release();
}
else
{
bRatingsEnabled = IS_RATINGS_ENABLED() && ::RatingEnabledQuery() == S_OK;
}
if(bRatingsEnabled)
{
TraceMsg(DM_PICS, "CDOH::_StartBasePicsProcessor entered with ratings enabled");
LPOLESTR pwszRawURL = NULL;
if (SUCCEEDED(_GetCurrentPageW(&pwszRawURL, TRUE)))
{
_PicsProcBase._StartPicsQuery(pwszRawURL);
OleFree(pwszRawURL);
}
}
}
HRESULT CDocObjectHost::_StartSecondaryPicsProcessor(IHTMLPrivateWindow * pPrivWin)
{
HRESULT hr = S_FALSE;
ASSERT( pPrivWin );
HRESULT hres;
BOOL bRatingsEnabled;
IRatingNotification* pRatingNotify;
hres = QueryService(SID_SRatingNotification, IID_PPV_ARG(IRatingNotification, &pRatingNotify));
if (SUCCEEDED(hres))
{
bRatingsEnabled = pRatingNotify->IsRatingsEnabled() ? FALSE : TRUE;
pRatingNotify->Release();
}
else
{
bRatingsEnabled = IS_RATINGS_ENABLED() && ::RatingEnabledQuery() == S_OK;
}
if (bRatingsEnabled)
{
LPOLESTR pwszURL = NULL;
// CWindow::GetPendingUrl() has LPOLESTR * parameter.
hr = pPrivWin->GetPendingUrl( &pwszURL );
if ( SUCCEEDED(hr) && pwszURL )
{
CPicsProcessor * pPicsProc = new CPicsProcessor;
if( pPicsProc )
{
pPicsProc->_pdoh = this;
pPicsProc->_pPrivWindow = pPrivWin;
pPrivWin->AddRef();
pPicsProc->_dwKey = _dwPicsKeyBase++;
hr = pPicsProc->_StartPicsQuery( pwszURL );
if( hr == S_FALSE )
{
delete pPicsProc;
}
else
{
pPicsProc->_pPicsProcNext = _PicsProcBase._pPicsProcNext;
_PicsProcBase._pPicsProcNext = pPicsProc;
pPrivWin->SetPICSTarget( SAFECAST(pPicsProc, IOleCommandTarget *) );
}
}
else
{
hr = E_OUTOFMEMORY;
}
OleFree( pwszURL );
}
}
return hr;
}
HRESULT CDocObjectHost::_RemovePicsProcessorByKey(LONG_PTR lKey)
{
ASSERT( lKey != 0 );
// The base Proc has a 0 key and should never be removed
CPicsProcessor * ppp = &_PicsProcBase;
CPicsProcessor * pppLast = NULL;
while( ppp )
{
if (ppp->_GetKey() == lKey)
{
ASSERT(pppLast);
pppLast->_pPicsProcNext = ppp->_pPicsProcNext;
ppp->_Terminate();
return S_OK;
}
pppLast = ppp;
ppp = ppp->_pPicsProcNext;
}
return S_FALSE;
}
HRESULT CDocObjectHost::_RemovePicsProcessorByPrivWindowUnk(IUnknown* pUnkFind)
{
ASSERT( pUnkFind );
// The base Proc has a 0 key and should never be removed
CPicsProcessor * ppp = &_PicsProcBase;
CPicsProcessor * pppLast = NULL;
while( ppp )
{
if (ppp->_pPrivWindow)
{
IUnknown * pUnkThisOne;
if (SUCCEEDED( ppp->_pPrivWindow->QueryInterface( IID_IUnknown, (void**)&pUnkThisOne ) ))
{
if (pUnkThisOne == pUnkFind)
{
ASSERT(pppLast);
pppLast->_pPicsProcNext = ppp->_pPicsProcNext;
ppp->_Terminate();
pUnkThisOne->Release();
return S_OK;
}
pUnkThisOne->Release();
}
}
pppLast = ppp;
ppp = ppp->_pPicsProcNext;
}
return S_FALSE;
}
HRESULT CDocObjectHost::_RemoveAllPicsProcessors()
{
CPicsProcessor * ppp = &_PicsProcBase;
CPicsProcessor * pppNext;
while( ppp )
{
pppNext = ppp->_pPicsProcNext;
if (ppp != &_PicsProcBase)
ppp->_Terminate();
else
ppp->_ReInit();
ppp = pppNext;
}
return S_OK;
}
HRESULT CDocObjectFrame::QueryService(REFGUID guidService,
REFIID riid, void **ppvObj)
{
return _pdoh->QueryService(guidService, riid, ppvObj);
}
HRESULT CDocObjectFrame::QueryInterface(REFIID riid, void ** ppvObj)
{
if (IsEqualIID(riid, IID_IOleInPlaceFrame) ||
IsEqualIID(riid, IID_IOleInPlaceUIWindow) ||
IsEqualIID(riid, IID_IOleWindow) ||
IsEqualIID(riid, IID_IUnknown))
{
*ppvObj = SAFECAST(this, IOleInPlaceFrame*);
}
else if (IsEqualIID(riid, IID_IOleCommandTarget))
{
*ppvObj = SAFECAST(this, IOleCommandTarget*);
}
else if (IsEqualIID(riid, IID_IServiceProvider))
{
*ppvObj = SAFECAST(this, IServiceProvider*);
}
else if (IsEqualIID(riid, IID_IInternetSecurityMgrSite))
{
*ppvObj = SAFECAST(this, IInternetSecurityMgrSite*);
}
else
{
*ppvObj = NULL;
return E_NOINTERFACE;
}
_pdoh->AddRef();
return NOERROR;
}
ULONG CDocObjectFrame::AddRef(void)
{
return _pdoh->AddRef();
}
ULONG CDocObjectFrame::Release(void)
{
return _pdoh->Release();
}
HRESULT CDocObjectFrame::GetWindow(HWND * lphwnd)
{
DOFMSG(TEXT("GetWindow called"));
return _pdoh->_pipu ?
_pdoh->_pipu->GetWindow(lphwnd) :
_pdoh->_psb ? _pdoh->_psb->GetWindow(lphwnd) :
_pdoh->GetWindow(lphwnd);
}
HRESULT CDocObjectFrame::ContextSensitiveHelp(BOOL fEnterMode)
{
DOFMSG(TEXT("ContextSensitiveHelp called"));
return _pdoh->ContextSensitiveHelp(fEnterMode);
}
HRESULT CDocObjectFrame::GetBorder(LPRECT lprectBorder)
{
// DOFMSG(TEXT("GetBorder called"));
return _pdoh->_pipu ?
_pdoh->_pipu->GetBorder(lprectBorder) : E_UNEXPECTED;
}
HRESULT CDocObjectFrame::RequestBorderSpace(LPCBORDERWIDTHS pborderwidths)
{
DOFMSG(TEXT("RequestBorderSpace called"));
return _pdoh->_pipu ?
_pdoh->_pipu->RequestBorderSpace(pborderwidths) : E_UNEXPECTED;
}
HRESULT CDocObjectFrame::SetBorderSpace(LPCBORDERWIDTHS pborderwidths)
{
// DOFMSG(TEXT("SetBorderSpace called"));
return _pdoh->_pipu ?
_pdoh->_pipu->SetBorderSpace(pborderwidths) : E_UNEXPECTED;
}
HRESULT CDocObjectFrame::SetActiveObject(
IOleInPlaceActiveObject *pActiveObject, LPCOLESTR pszObjName)
{
DOFMSG(TEXT("SetActiveObject called"));
// Note that we need to call both.
_pdoh->_xao.SetActiveObject(pActiveObject);
if (_pdoh->_pipu) {
//
// Note that we should pass proxy IOleActiveObject pointer instead.
//
_pdoh->_pipu->SetActiveObject(pActiveObject ? &_pdoh->_xao : NULL, pszObjName);
}
return S_OK;
}
HRESULT CDocObjectFrame::InsertMenus(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths)
{
DOFMSG2(TEXT("InsertMenus called with"), hmenuShared);
return _pdoh->_InsertMenus(hmenuShared, lpMenuWidths);
}
HRESULT CDocObjectFrame::SetMenu(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject)
{
DOFMSG2(TEXT("SetMenu called with"), hmenuShared);
return _pdoh->_SetMenu(hmenuShared, holemenu, hwndActiveObject);
}
HRESULT CDocObjectFrame::RemoveMenus(HMENU hmenuShared)
{
DOFMSG(TEXT("RemoveMenus called"));
return _pdoh->_RemoveMenus(hmenuShared);
}
HRESULT CDocObjectFrame::SetStatusText(LPCOLESTR pszStatusText)
{
DOFMSG(TEXT("SetStatusText called"));
return _pdoh->_SetStatusText(pszStatusText);
}
HRESULT CDocObjectFrame::EnableModeless(BOOL fEnable)
{
DOFMSG(TEXT("EnableModeless called"));
return _pdoh->_EnableModeless(fEnable);
}
// IOleInPlaceFrame::TranslateAccelerator
HRESULT CDocObjectFrame::TranslateAccelerator(LPMSG lpmsg, WORD wID)
{
// NOTES: This code remains as-is. If we have something special
// it should be done in CDocObjectHost::TranslateAccelerator
return _pdoh->_TranslateAccelerator(lpmsg, wID);
}
HRESULT CDocObjectFrame::QueryStatus(const GUID *pguidCmdGroup,
ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
{
DOFMSG(TEXT("QueryStatus called"));
return _pdoh->QueryStatus(pguidCmdGroup, cCmds, rgCmds, pcmdtext);
}
HRESULT CDocObjectFrame::Exec(const GUID *pguidCmdGroup,
DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
{
DOFMSG(TEXT("Exec called"));
return _pdoh->Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
}
//*** CPAO::TranslateAccelerator (IOIPAO::TranslateAccelerator)
//
HRESULT CProxyActiveObject::TranslateAccelerator(
LPMSG lpmsg)
{
HRESULT hres = E_FAIL;
// IShellBrowser is supporsed to call ISV::TranslateAcceleratorSV,
// but why not be nice?
ASSERT(!_pdoh->_fCycleFocus);
//
// Don't call DocObject's TranslateAccelarator with non-key messages.
// It's better to be IE compatible.
//
BOOL fKeybrdMsg = IsInRange(lpmsg->message, WM_KEYFIRST, WM_KEYLAST);
if (fKeybrdMsg && _piact && (hres = _piact->TranslateAccelerator(lpmsg)) == S_OK) {
if (_pdoh->_fCycleFocus) {
// we got called back by trident (IOCS::TA), but deferred it.
// time to pay the piper.
TraceMsg(DM_FOCUS, "DOH::IOIPAO::TA piao->TA==S_OK ret _fCycleFocus=FALSE hr=S_FALSE (piper)");
_pdoh->_fCycleFocus = FALSE;
return S_FALSE; // time to pay the piper
}
return S_OK;
}
if (_pdoh->_fCycleFocus) {
TraceMsg(DM_ERROR, "DOH::IOIPAO::TA _fCycleFocus && hres=%x (!=S_OK)", hres);
_pdoh->_fCycleFocus = FALSE;
return S_FALSE;
}
return _pdoh->TranslateHostAccelerators(lpmsg);
}
HRESULT CProxyActiveObject::OnFrameWindowActivate(
BOOL fActivate)
{
TraceMsg(TF_SHDUIACTIVATE, "CProxyAO::OnFrameWindowActivate called with %d (_piact=%x)",
fActivate, _piact);
if (_piact) {
return _piact->OnFrameWindowActivate(fActivate);
}
return S_OK;
}
HRESULT CProxyActiveObject::OnDocWindowActivate(
BOOL fActivate)
{
return S_OK;
}
HRESULT CProxyActiveObject::ResizeBorder(
LPCRECT prcBorder,
IOleInPlaceUIWindow *pUIWindow,
BOOL fFrameWindow)
{
if (_piact) {
//
// Note that we must pass our proxy frame instead!
//
return _piact->ResizeBorder(prcBorder, &_pdoh->_dof, TRUE);
}
return E_FAIL;
}
void CProxyActiveObject::SetActiveObject(IOleInPlaceActiveObject *piact )
{
if (_piact)
{
ATOMICRELEASE(_piact);
_hwnd = NULL;
}
if (piact) {
_piact = piact;
_piact->AddRef();
_piact->GetWindow(&_hwnd);
}
}
HRESULT CProxyActiveObject::EnableModeless(
BOOL fEnable)
{
// IShellBrowser is supporsed to call ISV::EnableModelessSV,
// but why not be nice?
HRESULT hres = S_OK;
if (_piact)
hres = _piact->EnableModeless(fEnable);
return hres;
}
HRESULT CProxyActiveObject::QueryInterface(REFIID riid, void ** ppvObj)
{
if (IsEqualIID(riid, IID_IOleInPlaceActiveObject) ||
IsEqualIID(riid, IID_IOleWindow) ||
IsEqualIID(riid, IID_IUnknown))
{
*ppvObj = SAFECAST(this, IOleInPlaceActiveObject*);
}
else
{
*ppvObj = NULL;
return E_NOINTERFACE;
}
_pdoh->AddRef();
return NOERROR;
}
ULONG CProxyActiveObject::AddRef(void)
{
return _pdoh->AddRef();
}
ULONG CProxyActiveObject::Release(void)
{
return _pdoh->Release();
}
HRESULT CProxyActiveObject::GetWindow(HWND * lphwnd)
{
return _pdoh->GetWindow(lphwnd);
}
HRESULT CProxyActiveObject::ContextSensitiveHelp(BOOL fEnterMode)
{
return _pdoh->ContextSensitiveHelp(fEnterMode);
}
#define ANIMATION_WND_WIDTH (100+3)
void CDocObjectHost::_PlaceProgressBar(BOOL fForcedLayout)
{
if (_psb) {
HWND hwndStatus = NULL;
_psb->GetControlWindow(FCW_STATUS, &hwndStatus);
if (hwndStatus) {
RECT rc;
INT_PTR fSimple = SendMessage(hwndStatus, SB_ISSIMPLE, 0, 0);
if (!fSimple || fForcedLayout) {
// While processing WM_SIZE, turn off the simple mode temporarily.
if (fSimple)
_psb->SendControlMsg(FCW_STATUS, SB_SIMPLE, 0, 0, NULL);
GetClientRect(hwndStatus, &rc);
const UINT cxZone = ZoneComputePaneSize(hwndStatus);
UINT cxProgressBar = (_fShowProgressCtl) ? 100 : 0;
INT nSBWidth = rc.right - rc.left;
INT arnRtEdge[STATUS_PANES] = {1};
INT nIconPaneWidth = GetSystemMetrics(SM_CXSMICON) +
(GetSystemMetrics(SM_CXEDGE) * 4);
INT nWidthReqd = cxZone + cxProgressBar + (nIconPaneWidth * 3);
arnRtEdge[STATUS_PANE_NAVIGATION] = max(1, nSBWidth - nWidthReqd);
nWidthReqd -= cxProgressBar;
arnRtEdge[STATUS_PANE_PROGRESS] = max(1, nSBWidth - nWidthReqd);
nWidthReqd -= (nIconPaneWidth);
arnRtEdge[STATUS_PANE_OFFLINE] = max(1, nSBWidth - nWidthReqd);
nWidthReqd -= (nIconPaneWidth);
arnRtEdge[STATUS_PANE_PRIVACY] = max(1, nSBWidth - nWidthReqd);
// nWidthReqd -= (nIconPaneWidth);
// arnRtEdge[STATUS_PANE_PRINTER] = max(1, nSBWidth - nWidthReqd);
nWidthReqd -= (nIconPaneWidth);
arnRtEdge[STATUS_PANE_SSL] = max(1, nSBWidth - nWidthReqd);
arnRtEdge[STATUS_PANE_ZONE] = -1;
LRESULT nParts = 0;
nParts = SendMessage(hwndStatus, SB_GETPARTS, 0, 0L);
if (nParts != STATUS_PANES)
{
for ( int n = 0; n < nParts; n++)
{
SendMessage(hwndStatus, SB_SETTEXT, n | SBT_NOTABPARSING, NULL);
SendMessage(hwndStatus, SB_SETICON, n, NULL);
}
SendMessage(hwndStatus, SB_SETPARTS, 0, 0L);
}
SendMessage(hwndStatus, SB_SETPARTS, STATUS_PANES, (LPARAM)arnRtEdge);
if (!_hwndProgress) {
_psb->GetControlWindow(FCW_PROGRESS, &_hwndProgress);
}
if (_hwndProgress) {
if (SendMessage(hwndStatus, SB_GETRECT, 1, (LPARAM)&rc))
{
InflateRect(&rc, -GetSystemMetrics(SM_CXEDGE), -GetSystemMetrics(SM_CYEDGE));
}
else
{
rc.left = rc.top = rc.right = rc.bottom = 0;
}
SetWindowPos(_hwndProgress, NULL,
rc.left, rc.top,
rc.right-rc.left, rc.bottom-rc.top,
SWP_NOZORDER | SWP_NOACTIVATE);
}
SendMessage(hwndStatus, SB_SETTEXT, 1 | SBT_NOTABPARSING, (LPARAM)TEXT(""));
SendMessage(hwndStatus, SB_SETMINHEIGHT, GetSystemMetrics(SM_CYSMICON) +
GetSystemMetrics(SM_CYBORDER) * 2, 0L);
// Restore
if (fSimple)
SendMessage(hwndStatus, SB_SIMPLE, TRUE, 0);
}
}
} else {
TraceMsg(TF_WARNING, "_PlaceProgressBar ASSERT(_psb) this=%x", this);
}
}
void CDocObjectHost::_ActivateOleObject(void)
{
HRESULT hres;
_pole->SetClientSite(NULL);
if (_fDontInPlaceNavigate())
{
TraceMsg(TF_SHDAPPHACK, "CDOH::_ActivateOleObject calling DoVerb because of _fDontInPlaceNavigate()");
}
_EnableModeless(FALSE);
hres = _pole->DoVerb(
_fUseOpenVerb() ? OLEIVERB_OPEN : OLEIVERB_PRIMARY,
NULL, NULL, (UINT)-1, NULL, NULL);
_EnableModeless(TRUE);
if (SUCCEEDED(hres))
{
CShdAdviseSink_Advise(_pwb, _pole);
}
#ifdef DEBUG
else if (!_fDelegatedNavigation)
{
TraceMsg(DM_ERROR, "CDOH::_ActivateOleObject DoVerb failed %x.", hres);
}
#endif
// We must release the OLE object to avoid calling Close
// from _UnBind.
_ReleaseOleObject();
_ReleasePendingObject();
}
void CDocObjectHost::ClearScriptError()
{
// clear out the script error list
_pScriptErrList->ClearErrorList();
_SetPriorityStatusText(NULL);
// reset the text and icon
_ResetStatusBar();
}
//
// The docobject's READYSTATE property may have changed
//
void CDocObjectHost::_OnReadyState(long lVal, BOOL fUpdateHistory /* = TRUE */)
{
// Forward this to the browser so we can source ReadyState events properly
// TRACE this zekel
if (_psb)
{
IDocNavigate *pdn;
if (SUCCEEDED(_psb->QueryInterface(IID_IDocNavigate, (void **)&pdn)))
{
ASSERT(_psv);
pdn->OnReadyStateChange(_psv, lVal);
pdn->Release();
}
}
// NOTE: The below code is rather wasteful. The OmWindow stuff
// should trigger off the above ReadyState code.
//
IShellHTMLWindowSupport *phtmlWS;
if (_psp && SUCCEEDED(_psp->QueryService(SID_SOmWindow, IID_IShellHTMLWindowSupport, (void**)&phtmlWS)))
{
phtmlWS->ReadyStateChangedTo(lVal, _psv);
phtmlWS->Release();
}
if ( (lVal == READYSTATE_INTERACTIVE)
&& (_pScriptErrList != NULL))
{
ClearScriptError();
}
if (lVal >= READYSTATE_INTERACTIVE)
{
// Technically we can get this value multiple times,
// so make sure we call _Navigate only once.
//
if (!_fReadystateInteractiveProcessed)
{
_fReadystateInteractiveProcessed = TRUE;
_Navigate();
}
if (lVal == READYSTATE_COMPLETE)
{
_OnSetProgressPos(0, PROGRESS_RESET);
// We aren't going to commit error pages to the history
//
if (_pwb && !_fErrorPage && fUpdateHistory)
{
WCHAR szTitle[MAX_PATH]; // titles are only stored up to this size
if (SUCCEEDED(_pwb->GetTitle(_psv, szTitle, ARRAYSIZE(szTitle))))
{
// BharatS : 01/09/97 : There is no need to tie the updating of the title in the
// history to the updating of the INTSITE database. Thus the INTSITE database
// update can be moved out of AddUrlToUrlHistoryStg() in history.cpp when time permits
// to a more logical place such as someplace in dochost.cpp
//
_UpdateHistoryAndIntSiteDB(szTitle);
}
else
{
_UpdateHistoryAndIntSiteDB(NULL);
}
}
}
}
}
HRESULT CDocObjectHost::_OnChangedReadyState()
{
IDispatch * p_idispatch;
ASSERT(_pole || _fFriendlyError);
if (!_pole)
return E_UNEXPECTED;
if (SUCCEEDED(_pole->QueryInterface( IID_IDispatch, (void **) &p_idispatch)))
{
VARIANTARG va;
EXCEPINFO exInfo;
va.vt = 0;
if (EVAL(SUCCEEDED(p_idispatch->Invoke( DISPID_READYSTATE, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, (DISPPARAMS *)&g_dispparamsNoArgs, &va, &exInfo, NULL))
&& va.vt == VT_I4))
{
_OnReadyState(va.lVal);
// If we are hosting Trident's native frames build,
// we don't want to call _RemoveTransitionCapability().
//
if (!_fDocCanNavigate && va.lVal == READYSTATE_COMPLETE)
{
_RemoveTransitionCapability();
}
}
p_idispatch->Release();
}
return( NOERROR );
}
HRESULT CDocObjectHost::OnRequestEdit(DISPID dispid)
{
return E_NOTIMPL;
}
//
// OnChanged
//
// Notification from the DocObject that one of its
// properties has changed.
//
HRESULT CDocObjectHost::OnChanged(DISPID dispid)
{
if (DISPID_READYSTATE == dispid || DISPID_UNKNOWN == dispid)
return _OnChangedReadyState();
return S_OK;
}
extern BOOL _ValidateURL(LPTSTR pszName);
void CDocObjectHost::_UpdateHistoryAndIntSiteDB(LPCWSTR pwszTitle)
{
TCHAR szUrl[MAX_URL_STRING];
if (SUCCEEDED(_GetCurrentPage(szUrl, MAX_URL_STRING, TRUE)) &&
_ValidateURL(szUrl, UQF_DEFAULT))
{
// update history and intsite if this isn't a silent browse
BOOL bSilent = FALSE;
HRESULT hr = _GetOfflineSilent(NULL, &bSilent);
if (SUCCEEDED(hr) && (!bSilent))
{
BOOL fWriteHistory,
fSelectHistory;
fWriteHistory = TRUE;
fSelectHistory = TRUE;
if (NULL != _pocthf)
{
MSOCMD rgCmd[] = { { SBCMDID_WRITEHIST, 0 }, { SBCMDID_SELECTHISTPIDL, 0 } };
_pocthf->QueryStatus(&CGID_Explorer, ARRAYSIZE(rgCmd), rgCmd, NULL);
fWriteHistory = BOOLIFY(rgCmd[0].cmdf & MSOCMDF_ENABLED);
fSelectHistory = BOOLIFY(rgCmd[1].cmdf & MSOCMDF_ENABLED);
}
AddUrlToUrlHistoryStg(szUrl,
pwszTitle,
_psb,
fWriteHistory,
fSelectHistory ? _pocthf : NULL,
get_punkSFHistory(), NULL);
//
// Satona had the redirect code ifdef'd out, but for
// netscape compatibility, we need to update the history
// for the redirected URL as well. - zekel - 22-JUL-97
//
// If this page is a redirect, update intsite for destination too
INTERNET_CACHE_ENTRY_INFO *pCacheEntry = NULL;
#ifndef UNIX
WCHAR chBuf[MAX_CACHE_ENTRY_INFO_SIZE];
// Find entry in cache using redirect map
pCacheEntry = (INTERNET_CACHE_ENTRY_INFO *)chBuf;
#else
union
{
double _alignOn8;
WCHAR _chBuf[MAX_CACHE_ENTRY_INFO_SIZE];
} chBuf;
// Find entry in cache using redirect map
pCacheEntry = (INTERNET_CACHE_ENTRY_INFO *)&chBuf;
#endif /* !UNIX */
DWORD dwSize = SIZEOF(chBuf);
BOOL fSuccess = GetUrlCacheEntryInfoEx(szUrl, pCacheEntry, &dwSize, NULL, 0, NULL, 0);
if (fSuccess)
{
// If we have a different url than we started with, update it too
if (StrCmp(szUrl, pCacheEntry->lpszSourceUrlName))
{
AddUrlToUrlHistoryStg(pCacheEntry->lpszSourceUrlName,
pwszTitle,
_psb,
fWriteHistory,
fSelectHistory ? _pocthf : NULL,
get_punkSFHistory(), NULL);
}
}
}
}
}
//
// CDocObjectHost::_SetUpTransitionCapability()
//
// Returns TRUE if all the following hold true:
// - object has readystate property
// - readystate property is currently < interactive
// - Object supports IPropertyNotifySink
// Then this object supports delayed switching when it
// it tells us that it is ready...
//
// This is how we switch pages only when the new page is ready to be
// switched to. Also, by doing this we can also make the switch smooth
// by applying graphical transitions.
//
BOOL CDocObjectHost::_SetUpTransitionCapability(BOOL fWindowOpen /* = FALSE */)
{
// By default DocObject doesn't have gray-flash communication
BOOL fSupportsReadystate = FALSE;
long lReadyState = 0; // Init to avoid a bogus C4701 warning
// Sanity Check
if (!_pole)
return(fSupportsReadystate);
// Check for proper readystate support
BOOL fReadyStateOK = FALSE;
IDispatch * p_idispatch;
if (SUCCEEDED(_pole->QueryInterface( IID_IDispatch, (void **) &p_idispatch )))
{
VARIANTARG va;
EXCEPINFO exInfo;
if (SUCCEEDED(p_idispatch->Invoke( DISPID_READYSTATE, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, (DISPPARAMS *)&g_dispparamsNoArgs, &va, &exInfo, NULL)))
{
if ((va.vt == VT_I4) && (va.lVal < READYSTATE_COMPLETE))
{
lReadyState = fWindowOpen ? READYSTATE_COMPLETE : va.lVal;
fReadyStateOK = TRUE;
}
}
p_idispatch->Release();
}
if (fReadyStateOK)
{
// Check and Set-Up IPropertyNotifySink
if (SUCCEEDED(ConnectToConnectionPoint(SAFECAST(this, IPropertyNotifySink*), IID_IPropertyNotifySink, TRUE, _pole, &_dwPropNotifyCookie, NULL)))
{
fSupportsReadystate = TRUE;
_OnReadyState(lReadyState);
}
}
// If no ReadyState, we simulate it
if (!fSupportsReadystate)
{
if (fWindowOpen)
{
// Connect the property notify sink or we
// will never activate the pending view.
//
ConnectToConnectionPoint(SAFECAST(this, IPropertyNotifySink*),
IID_IPropertyNotifySink,
TRUE,
_pole,
&_dwPropNotifyCookie,
NULL);
}
_OnReadyState(READYSTATE_COMPLETE, !fWindowOpen);
}
return(fSupportsReadystate);
}
// This removes any property notify sink we set up
//
BOOL CDocObjectHost::_RemoveTransitionCapability()
{
BOOL fRet = FALSE;
if (_dwPropNotifyCookie)
{
ConnectToConnectionPoint(NULL, IID_IPropertyNotifySink, FALSE, _pole, &_dwPropNotifyCookie, NULL);
fRet = TRUE;
}
return(fRet);
}
HRESULT _GetRequestFlagFromPIB(IBinding *pib, DWORD *pdwOptions)
{
HRESULT hres = E_FAIL;
*pdwOptions = 0;
if (pib)
{
IWinInetInfo* pwinet;
hres = pib->QueryInterface(IID_IWinInetInfo, (void **)&pwinet);
if (SUCCEEDED(hres)) {
DWORD cbSize = SIZEOF(*pdwOptions);
hres = pwinet->QueryOption(INTERNET_OPTION_REQUEST_FLAGS,
(LPVOID)pdwOptions, &cbSize);
TraceMsg(TF_SHDNAVIGATE, "DOH::BSC::_HFNS() pwinet->QueryOptions hres=%x dwOptions=%x", hres, *pdwOptions);
pwinet->Release();
}
}
return hres;
}
void CDocObjectHost::_Navigate()
{
NAVMSG3(TEXT("_Navigate calling SHDVID_ACTIVATEMENOW"), 0, NULL);
if (_pmsoctBrowser)
{
VARIANT varSynch;
V_VT(&varSynch) = VT_BOOL;
V_BOOL(&varSynch) = _fDocCanNavigate ? VARIANT_TRUE : VARIANT_FALSE;
_pmsoctBrowser->Exec(&CGID_ShellDocView,
SHDVID_ACTIVATEMENOW,
NULL,
&varSynch,
NULL);
}
}
#ifndef UNIX
void CDocObjectHost::_NavigateFolder(BSTR bstrUrl)
{
// This code accesses one of IE's default behaviors which
// allows for navigation to a web folder.
// ------------------------------------------------------
Iwfolders * pWF = NULL;
IElementBehaviorFactory * pebf = NULL;
IElementBehavior * pPeer = NULL;
HWND hwndOwner = NULL;
IServiceProvider * psp = NULL;
IUnknown * punkwb = NULL;
// Make the peer factory
if ( !_psb || (FAILED(_psb->GetWindow (&hwndOwner))) ||
(FAILED(CoCreateInstance(CLSID_PeerFactory, NULL, CLSCTX_INPROC,
IID_IElementBehaviorFactory, (void **)&pebf))) ||
(FAILED(pebf->FindBehavior(L"httpFolder", NULL, NULL, &pPeer))) ||
(FAILED(pPeer->QueryInterface(IID_Iwfolders, (void **)&pWF))) ||
(FAILED(QueryService(SID_STopLevelBrowser, IID_IServiceProvider, (void **)&psp))) ||
(FAILED(psp->QueryService(SID_SContainerDispatch, IID_IUnknown, (void **)&punkwb))) )
{
WCHAR wszMessage[MAX_PATH];
WCHAR wszTitle[MAX_PATH];
MLLoadShellLangString(IDS_ERRORINTERNAL, wszMessage, ARRAYSIZE(wszMessage));
MLLoadShellLangString(IDS_NAME, wszTitle, ARRAYSIZE(wszTitle));
ULONG_PTR uCookie = 0;
SHActivateContext(&uCookie);
MessageBox(hwndOwner, wszMessage, wszTitle, MB_OK | MB_ICONERROR);
if (uCookie)
{
SHDeactivateContext(uCookie);
}
goto done;
}
// Sundown: coercion to unsigned long is valid for HWNDs
pWF->navigateNoSite(bstrUrl, NULL, PtrToUlong(hwndOwner), punkwb);
done:
if (pebf)
pebf->Release();
if (pPeer)
pPeer->Release();
if (pWF)
pWF->Release();
if (punkwb)
punkwb->Release();
if (psp)
psp->Release();
}
#endif //UNIX
void CDocObjectHost::_CancelPendingNavigation(BOOL fAsyncDownload,
BOOL fSyncReally, /* = FALSE */
BOOL fDontShowNavCancelPage, /* = FALSE */
BOOL fForceClose /* = FALSE */)
{
ASSERT(_phf);
// TODO: We need to clean up the call to this method and eliminate need for fSyncReally
//
// Passing NULL for the 4th argument (instead of passing in a
// variant like we do in the case when fAsyncDownload is TRUE) looks plain wrong!
// See how this command+argument is interpreted in CBaseBrowser2::Exec.
// It is too close to RTM (of IE5.5) now to fix this. The net result is that
// when this function is called with fAsyncDownload=FALSE, we end up posting
// ASYNCOP_CANCELPENDINGNAVIGATION, which is just what the caller does not want.
// To workaround this, I have introduced the argument fSyncReally.
// the hlframe no longer knows if the clear was a cancel or a start of navigation
// because we don't call anything tosignal a successfull navigation
if (_pmsoctBrowser)
{
TraceMsg(DM_TRACE, "DOH::_CancelPendingNavigation calling _pmsc->Exec");
if (_pwb && fDontShowNavCancelPage)
{
_pwb->SetFlags(BSF_DONTSHOWNAVCANCELPAGE, BSF_DONTSHOWNAVCANCELPAGE);
}
if (fForceClose && _fWindowOpen) // Force the browser to close
{
HideBrowser(); // Makes the browser appear to go away faster.
_pmsoctBrowser->Exec(&CGID_Explorer, SBCMDID_CANCELANDCLOSE, 0, NULL, NULL);
}
else if (fAsyncDownload)
{
VARIANT var = {0};
var.vt = VT_I4;
ASSERT(var.lVal == FALSE); // asynd download is done.
_pmsoctBrowser->Exec(&CGID_Explorer, SBCMDID_CANCELNAVIGATION, 0, &var, NULL);
}
else if (fSyncReally)
{
VARIANT var = { 0 };
var.vt = VT_I4;
var.lVal = TRUE;
_pmsoctBrowser->Exec(&CGID_Explorer, SBCMDID_CANCELNAVIGATION, 0, &var, NULL);
}
else
{
// This actually causes an async cancel
//
_pmsoctBrowser->Exec(&CGID_Explorer, SBCMDID_CANCELNAVIGATION, 0, NULL, NULL);
}
}
// Release our navigation state.
// Doing a "fSyncReally" may cause us to lose our _phf member value. Check it again.
//
if (_phf)
{
_phf->Navigate(0, NULL, NULL, (IHlink*)-1);
}
}
void CDocObjectHost::_ResetStatusBar()
{
_SetStatusText(TEXT(""));
if (_psb)
_psb->SendControlMsg(FCW_STATUS, SB_SETICON, STATUS_PANE_NAVIGATION,
(LPARAM)g_ahiconState[IDI_STATE_NORMAL-IDI_STATE_FIRST], NULL);
return;
}
void CDocObjectHost::_DoAsyncNavigation(LPCTSTR pszURL)
{
if (_pmsoctBrowser) {
VARIANT vararg = {0};
vararg.vt = VT_BSTR;
vararg.bstrVal = SysAllocStringT(pszURL);
if (vararg.bstrVal)
{
TraceMsg(DM_TRACE, "DOH::_DoAsyncNavigation calling _pmsc->Exec");
_pmsoctBrowser->Exec(&CGID_Explorer, SBCMDID_ASYNCNAVIGATION, 0, &vararg, NULL);
VariantClear(&vararg);
}
}
}
// note: szError is never used, so don't waste time setting it
UINT SHIEErrorMsgBox(IShellBrowser* psb,
HWND hwndOwner, HRESULT hrError, LPCWSTR szError, LPCTSTR pszURLparam,
UINT idResource, UINT wFlags)
{
UINT uRet = IDCANCEL;
TCHAR szMsg[MAX_PATH];
LPCTSTR pszURL = TEXT("");
HWND hwndParent = hwndOwner;
IShellBrowser *psbParent = NULL;
// if a URL was specified, use it; otherwise use empty string
if (pszURLparam)
pszURL = pszURLparam;
//
// NOTES: This table of error code will be mapped to (IDS_ERRMSG_FIRST +
// offset) and we MLLoadString it.
//
const static c_ahres[] = {
HRESULT_FROM_WIN32(ERROR_INTERNET_INVALID_URL),
HRESULT_FROM_WIN32(ERROR_INTERNET_NAME_NOT_RESOLVED),
INET_E_UNKNOWN_PROTOCOL,
INET_E_REDIRECT_FAILED,
INET_E_DATA_NOT_AVAILABLE,
};
for (int i=0; i<ARRAYSIZE(c_ahres); i++) {
if (c_ahres[i]==hrError) {
MLLoadString(IDS_ERRMSG_FIRST+i, szMsg, ARRAYSIZE(szMsg));
break;
}
}
if (i >= ARRAYSIZE(c_ahres))
{
// Default message if FormatMessage doesn't recognize dwLastError
MLLoadString(IDS_UNDEFINEDERR, szMsg, ARRAYSIZE(szMsg));
if (hrError >= HRESULT_FROM_WIN32(INTERNET_ERROR_BASE)
&& hrError <= HRESULT_FROM_WIN32(INTERNET_ERROR_LAST))
{
HMODULE hmod = GetModuleHandle(TEXT("WININET"));
ASSERT(hmod);
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, (LPCVOID)hmod, HRESULT_CODE(hrError), 0L,
szMsg, ARRAYSIZE(szMsg), NULL);
} else {
// See if one of the system components has an error message
// for this error. If not, szMsg will retain our default
// message to handle this.
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, hrError, 0L,
szMsg, ARRAYSIZE(szMsg), NULL);
}
}
psbParent = psb;
if (psbParent)
{
psbParent->AddRef();
}
// Here we make an heroic effort to find a visible window to run the dialog against
// If we can't, then we bail, to avoid weird UI effect (particularly when the frametop
// browser is in kiosk mode
if (!IsWindowVisible(hwndParent))
{
if (NULL == psb || FAILED(psb->GetWindow(&hwndParent)) || !IsWindowVisible(hwndParent))
{
hwndParent = NULL;
ATOMICRELEASE(psbParent);
}
if (NULL == hwndParent && psb)
{
IUnknown_QueryService(psb, SID_STopFrameBrowser, IID_IShellBrowser, (void **)&psbParent);
if (NULL == psbParent || FAILED(psbParent->GetWindow(&hwndParent)) || !IsWindowVisible(hwndParent))
{
hwndParent = NULL;
}
}
}
if (hwndParent)
{
if (psbParent) {
psbParent->EnableModelessSB(FALSE);
}
uRet = MLShellMessageBox(hwndParent,
MAKEINTRESOURCE(idResource),
MAKEINTRESOURCE(IDS_TITLE),
wFlags, pszURL,szMsg);
if (psbParent) {
psbParent->EnableModelessSB(TRUE);
}
}
if (psbParent)
{
UINT cRef = psbParent->Release();
AssertMsg(cRef>0, TEXT("IE_ErrorMsgBox psb->Release returned 0."));
}
return uRet;
}
//
// See if the URL is of a type that we should
// ShellExecute()
//
HRESULT IsProtocolRegistered(LPCTSTR pcszProtocol);
BOOL ShouldShellExecURL(LPTSTR pszURL)
{
BOOL fRet = FALSE;
PARSEDURL pu = {sizeof(pu)};
HRESULT hr = ParseURL(pszURL, &pu);
if (SUCCEEDED(hr) && pu.nScheme != URL_SCHEME_SHELL)
{
TCHAR sz[MAX_PATH];
hr = StringCchCopyN(sz, ARRAYSIZE(sz), pu.pszProtocol, pu.cchProtocol);
if (SUCCEEDED(hr))
{
hr = IsProtocolRegistered(sz);
if (SUCCEEDED(hr))
{
//
// HACKHACK - telnet.exe will fault on buffer overrun
// if the url is > 230. we special case here.
//
if (lstrlen(pszURL) <= 230 ||
(StrCmpI(sz, TEXT("telnet")) &&
StrCmpI(sz, TEXT("rlogin")) &&
StrCmpI(sz, TEXT("tn3270"))
)
)
{
fRet = TRUE;
}
}
}
}
return fRet;
}
//========================================================================
// class CShdAdviseSink
//========================================================================
class CShdAdviseSink : public IAdviseSink
{
public:
// *** IUnknown methods ***
virtual STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj);
virtual STDMETHODIMP_(ULONG) AddRef(void) ;
virtual STDMETHODIMP_(ULONG) Release(void);
// *** IAdviseSink methods ***
virtual void __stdcall OnDataChange(
FORMATETC *pFormatetc,
STGMEDIUM *pStgmed);
virtual void __stdcall OnViewChange(
DWORD dwAspect,
LONG lindex);
virtual void __stdcall OnRename(
IMoniker *pmk);
virtual void __stdcall OnSave( void);
virtual void __stdcall OnClose( void);
CShdAdviseSink(IBrowserService* pwb, IOleObject* pole);
~CShdAdviseSink();
protected:
UINT _cRef;
IOleObject* _pole;
DWORD _dwConnection;
};
//
// We'd better maintain the list of those CShdAdviseSink
// per-thread so that we don't leak all those oleobjects when
// the thread is terminated before those objects are closed.
//
void CShdAdviseSink_Advise(IBrowserService* pwb, IOleObject* pole)
{
IAdviseSink* padv = new CShdAdviseSink(pwb, pole);
// If pole->Advise succeeds, it will addreff to IAdviseSink.
if (padv != NULL)
{
padv->Release();
}
}
CShdAdviseSink::CShdAdviseSink(IBrowserService* pwb, IOleObject* pole)
: _cRef(1)
{
ASSERT(pole);
TraceMsg(DM_ADVISE, "CShdAdviseSink(%x) being constructed", this);
HRESULT hres = pole->Advise(this, &_dwConnection);
if (SUCCEEDED(hres)) {
_pole = pole;
pole->AddRef();
TraceMsg(DM_ADVISE, "CShdAdviseSink(%x) called pole->Advise. new _cRef=%d (%x)", this, _cRef, _dwConnection);
}
}
CShdAdviseSink::~CShdAdviseSink()
{
TraceMsg(DM_ADVISE, "CShdAdviseSink(%x) being destroyed", this);
ATOMICRELEASE(_pole);
}
ULONG CShdAdviseSink::AddRef()
{
_cRef++;
TraceMsg(TF_SHDREF, "CShdAdviseSink(%x)::AddRef called, new _cRef=%d", this, _cRef);
return _cRef;
}
ULONG CShdAdviseSink::Release()
{
_cRef--;
TraceMsg(TF_SHDREF, "CShdAdviseSink(%x)::Release called, new _cRef=%d", this, _cRef);
if (_cRef > 0)
return _cRef;
delete this;
return 0;
}
HRESULT CShdAdviseSink::QueryInterface(REFIID riid, void ** ppvObj)
{
if (IsEqualIID(riid, IID_IAdviseSink) ||
IsEqualIID(riid, IID_IUnknown))
{
*ppvObj = (IAdviseSink*)this;
}
else
{
*ppvObj = NULL;
return E_NOINTERFACE;
}
AddRef();
return NOERROR;
}
void CShdAdviseSink::OnDataChange(
FORMATETC *pFormatetc,
STGMEDIUM *pStgmed)
{
}
void CShdAdviseSink::OnViewChange(
DWORD dwAspect,
LONG lindex)
{
}
void CShdAdviseSink::OnRename(
IMoniker *pmk)
{
}
void CShdAdviseSink::OnSave( void)
{
}
void CShdAdviseSink::OnClose( void)
{
TraceMsg(DM_ADVISE, "CShdAdviseSink(%x)::OnClose called. Calling Unadvise. _cRef=%d", this, _cRef);
HRESULT hres;
AddRef();
ASSERT(_pole);
if (_pole) // paranoia
{
hres = _pole->Unadvise(_dwConnection);
ATOMICRELEASE(_pole);
TraceMsg(DM_ADVISE, "CShdAdviseSink(%x)::OnClose. Called Unadvise(%x). new _cRef=%d", this, hres, _cRef);
}
Release();
}
/// adding property sheet pages
HRESULT CDocObjectHost::AddPages(LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam)
{
HRESULT hres = S_OK;
IShellPropSheetExt *pspse;
/*
* Create a property sheet page for required page, including imported File
* Types property sheet.
*/
// add stuff that the docobj itself has.
if (_pole)
{
if (SUCCEEDED(_pole->QueryInterface(IID_IShellPropSheetExt, (void **)&pspse)))
{
hres = pspse->AddPages(lpfnAddPage, lParam);
pspse->Release();
}
else
{
// Some docobjects don't know about IShellPropSheetExt (ie, Visual Basic),
// so do it ourselves.
if (NULL == _hinstInetCpl)
_hinstInetCpl = LoadLibrary(TEXT("inetcpl.cpl"));
if (_hinstInetCpl)
{
PFNADDINTERNETPROPERTYSHEETSEX pfnAddSheet = (PFNADDINTERNETPROPERTYSHEETSEX)GetProcAddress(_hinstInetCpl, STR_ADDINTERNETPROPSHEETSEX);
if (pfnAddSheet)
{
IEPROPPAGEINFO iepi = {0};
// we just want the security page.
iepi.cbSize = sizeof(iepi);
iepi.dwFlags = (DWORD)-1; // all pages
hres = pfnAddSheet(lpfnAddPage, lParam, 0, 0, &iepi);
}
// Don't FreeLibrary here, otherwise PropertyPage will GP-fault!
}
}
}
return hres;
}
//==========================================================================
// IDocHostUIHandler implementation
//==========================================================================
HRESULT CDocObjectHost::TranslateAccelerator(LPMSG lpMsg, const GUID *pguidCmdGroup, DWORD nCmdID)
{
if (_pWebOCUIHandler)
return _pWebOCUIHandler->TranslateAccelerator(lpMsg, pguidCmdGroup, nCmdID);
return _dhUIHandler.TranslateAccelerator(lpMsg, pguidCmdGroup, nCmdID);
}
HRESULT CDocObjectHost::GetDropTarget(IDropTarget *pDropTarget, IDropTarget **ppDropTarget)
{
// REVIEW: Does this apply anymore?
TraceMsg(DM_DOCHOSTUIHANDLER, "CDOH::GetDropTarget called");
HRESULT hres;
if (_pWebOCUIHandler)
{
hres = _pWebOCUIHandler->GetDropTarget(pDropTarget, ppDropTarget);
if (SUCCEEDED(hres))
return hres;
}
if (pDropTarget)
{
hres = S_OK;
IDropTarget *pdtFrame;
IDropTarget *pdt3;
IDropTarget *pdtBlocking;
QueryService(SID_STopFrameBrowser, IID_IDropTarget, (void **)&pdtFrame);
// hack: this is because we need to look all the way through to top parents for a containing drop target
// what we really need is a per dataobject drop target
//
// this is not required to be obtained
QueryService(SID_ITopViewHost, IID_IDropTarget, (void **)&pdt3);
if (IsSameObject(pdt3, pdtFrame)) {
ATOMICRELEASE(pdt3);
}
// allow constrained browser bands like Search to prevent drop
QueryService(SID_SDropBlocker, IID_IUnknown, (void **)&pdtBlocking);
if (pdtBlocking)
{
ATOMICRELEASE(pdt3);
pDropTarget = NULL;
}
if (pdtFrame)
{
*ppDropTarget = DropTargetWrap_CreateInstance(pDropTarget, pdtFrame, _hwnd, pdt3);
if (!*ppDropTarget)
hres = E_OUTOFMEMORY;
TraceMsg(DM_DOCHOSTUIHANDLER, "CDOH::GetDropTarget returning S_OK");
ASSERT(hres == S_OK);
pdtFrame->Release();
}
else
{
ASSERT(0);
hres = E_UNEXPECTED;
}
ATOMICRELEASE(pdtBlocking);
ATOMICRELEASE(pdt3);
}
else
{
hres = E_INVALIDARG;
}
return hres;
}
HRESULT CDocObjectHost::ShowUI(
DWORD dwID, IOleInPlaceActiveObject *pActiveObject,
IOleCommandTarget *pCommandTarget, IOleInPlaceFrame *pFrame,
IOleInPlaceUIWindow *pDoc)
{
if (_pWebOCUIHandler)
return _pWebOCUIHandler->ShowUI(dwID, pActiveObject, pCommandTarget, pFrame, pDoc);
if (_dwAppHack & BROWSERFLAG_MSHTML) // Who else will call on this interface?
{
if (_pmsoctBrowser)
{
TraceMsg(DM_PREMERGEDMENU, "DOH::ShowUI called this=%x pcmd=%x",
this,pCommandTarget);
VARIANT var = { 0 };
HRESULT hresT=_pmsoctBrowser->Exec(&CGID_Explorer, SBCMDID_SETMERGEDWEBMENU, 0, NULL, &var);
if (SUCCEEDED(hresT))
{
if (_pcmdMergedMenu)
{
// Tell Trident to stop calling us twice
TraceMsg(DM_WARNING, "DOH::ShowUI called twice! "
"this=%x pcmdCur=%x pcmdNew=%x",
this, _pcmdMergedMenu, pCommandTarget);
_pcmdMergedMenu->Release();
}
_pcmdMergedMenu = pCommandTarget;
_pcmdMergedMenu->AddRef();
ASSERT(var.vt == VT_INT_PTR);
_hmenuCur = (HMENU)var.byref;
DEBUG_CODE( _DumpMenus(TEXT("after ShowUI"), TRUE); )
return S_OK;
}
}
}
return S_FALSE;
}
HRESULT CDocObjectHost::HideUI(void)
{
if (_pWebOCUIHandler)
return _pWebOCUIHandler->HideUI();
if (_pcmdMergedMenu) {
_pcmdMergedMenu->Release();
_pcmdMergedMenu = NULL;
}
return S_FALSE;
}
HRESULT CDocObjectHost::GetHostInfo(DOCHOSTUIINFO *pInfo)
{
IServiceProvider * psp = NULL;
IWebBrowser2 * pwb = NULL;
VARIANT_BOOL b = VARIANT_FALSE;
DWORD dwFlagsWebOC = 0;
HRESULT hr;
if (_pWebOCUIHandler
&& SUCCEEDED(_pWebOCUIHandler->GetHostInfo(pInfo))
)
{
dwFlagsWebOC = pInfo->dwFlags;
}
_dhUIHandler.GetHostInfo(pInfo);
// Merge flags
//
pInfo->dwFlags |= dwFlagsWebOC;
// Add the local machine flag, if we're in iexplore.exe or explorer.exe.
if (IsInternetExplorerApp())
pInfo->dwFlags |= DOCHOSTUIFLAG_LOCAL_MACHINE_ACCESS_CHECK;
// Get the top level browser
//
hr = QueryService(SID_STopLevelBrowser, IID_PPV_ARG(IServiceProvider, &psp));
if (hr)
goto Cleanup;
// Get the IWebBrowser2 object/interface
//
hr = psp->QueryService(SID_SContainerDispatch, IID_IWebBrowser2, (void **)&pwb);
if (hr)
goto Cleanup;
// Tell the browser what our dochost flags are
IEFrameAuto *pIEFrameAuto;
if (SUCCEEDED(pwb->QueryInterface(IID_PPV_ARG(IEFrameAuto, &pIEFrameAuto))))
{
pIEFrameAuto->SetDocHostFlags(pInfo->dwFlags);
pIEFrameAuto->Release();
}
// Is the browser in Theater Mode?
//
hr = pwb->get_TheaterMode(&b);
if (hr)
goto Cleanup;
// If so, turn on flat scrollbars.
//
if (b == VARIANT_TRUE)
pInfo->dwFlags |= DOCHOSTUIFLAG_FLAT_SCROLLBAR;
Cleanup:
ATOMICRELEASE(psp);
ATOMICRELEASE(pwb);
return S_OK;
}
HRESULT CDocObjectHost::ShowContextMenu(DWORD dwID, POINT *ppt, IUnknown *pcmdtReserved, IDispatch *pdispReserved)
{
HRESULT hr;
OLECMD rgcmd = { IDM_BROWSEMODE, 0 };
// If we're in the WebOC and it has a IDocHostUIHandler, use it.
//
if (_pWebOCUIHandler)
{
hr = _pWebOCUIHandler->ShowContextMenu(dwID, ppt, pcmdtReserved, pdispReserved);
if (hr == S_OK)
goto Cleanup;
}
// Find out if the DocObject is in Edit mode
// Don't need apphack here as only Trident responds to CGID_MSHTML
//
hr = IUnknown_QueryStatus(pcmdtReserved, &CGID_MSHTML, 1, &rgcmd, NULL);
if ( hr == S_OK
&& !(rgcmd.cmdf & OLECMDF_LATCHED)) // if not LATCHED means we're in edit mode.
{
hr = S_FALSE;
}
else
{
hr = _dhUIHandler.ShowContextMenu(dwID, ppt, pcmdtReserved, pdispReserved);
}
Cleanup:
return hr;
}
HRESULT CDocObjectHost::UpdateUI(void)
{
if (_pWebOCUIHandler)
return _pWebOCUIHandler->UpdateUI();
return _dhUIHandler.UpdateUI();
}
HRESULT CDocObjectHost::EnableModeless(BOOL fEnable)
{
if (_pWebOCUIHandler)
return _pWebOCUIHandler->EnableModeless(fEnable);
return _dhUIHandler.EnableModeless(fEnable);
}
HRESULT CDocObjectHost::OnDocWindowActivate(BOOL fActivate)
{
if (_pWebOCUIHandler)
return _pWebOCUIHandler->OnDocWindowActivate(fActivate);
return _dhUIHandler.OnDocWindowActivate(fActivate);
}
HRESULT CDocObjectHost::OnFrameWindowActivate(BOOL fActivate)
{
if (_pWebOCUIHandler)
return _pWebOCUIHandler->OnFrameWindowActivate(fActivate);
return _dhUIHandler.OnFrameWindowActivate(fActivate);
}
HRESULT CDocObjectHost::ResizeBorder( LPCRECT prcBorder, IOleInPlaceUIWindow *pUIWindow, BOOL fRameWindow)
{
if (_pWebOCUIHandler)
return _pWebOCUIHandler->ResizeBorder(prcBorder, pUIWindow, fRameWindow);
return _dhUIHandler.ResizeBorder(prcBorder, pUIWindow, fRameWindow);
}
HRESULT CDocObjectHost::GetOptionKeyPath(BSTR *pbstrKey, DWORD dw)
{
if (_pWebOCUIHandler)
return _pWebOCUIHandler->GetOptionKeyPath(pbstrKey, dw);
return _dhUIHandler.GetOptionKeyPath(pbstrKey, dw);
}
HRESULT CDocObjectHost::GetExternal(IDispatch **ppDisp)
{
if (_pWebOCUIHandler)
return _pWebOCUIHandler->GetExternal(ppDisp);
return _dhUIHandler.GetExternal(ppDisp);
}
HRESULT CDocObjectHost::TranslateUrl(DWORD dwTranslate, OLECHAR *pchURLIn, OLECHAR **ppchURLOut)
{
if (_pWebOCUIHandler)
return _pWebOCUIHandler->TranslateUrl(dwTranslate, pchURLIn, ppchURLOut);
return _dhUIHandler.TranslateUrl(dwTranslate, pchURLIn, ppchURLOut);
}
HRESULT CDocObjectHost::FilterDataObject(IDataObject *pDO, IDataObject **ppDORet)
{
if (_pWebOCUIHandler)
return _pWebOCUIHandler->FilterDataObject(pDO, ppDORet);
return _dhUIHandler.FilterDataObject(pDO, ppDORet);
}
HRESULT CDocObjectHost::GetOverrideKeyPath(LPOLESTR *pchKey, DWORD dw)
{
if (_pWebOCUIHandler2)
return _pWebOCUIHandler2->GetOverrideKeyPath(pchKey, dw);
return _dhUIHandler.GetOverrideKeyPath(pchKey, dw);
}
HRESULT CDocObjectHost::ShowMessage(HWND hwnd, LPOLESTR lpstrText, LPOLESTR lpstrCaption,
DWORD dwType, LPOLESTR lpstrHelpFile, DWORD dwHelpContext, LRESULT __RPC_FAR *plResult)
{
if (_pWebOCShowUI)
{
return _pWebOCShowUI->ShowMessage(hwnd, lpstrText, lpstrCaption, dwType,
lpstrHelpFile, dwHelpContext, plResult);
}
return E_FAIL;
}
HRESULT CDocObjectHost::ShowHelp(HWND hwnd, LPOLESTR pszHelpFile, UINT uCommand, DWORD dwData,
POINT ptMouse, IDispatch __RPC_FAR *pDispatchObjectHit)
{
if (_pWebOCShowUI)
{
return _pWebOCShowUI->ShowHelp(hwnd, pszHelpFile, uCommand, dwData, ptMouse,
pDispatchObjectHit);
}
return E_FAIL;
}
void
CDocObjectHost::HideBrowser() const
{
if (_psp && _fWindowOpen && !(_dwAppHack & BROWSERFLAG_DONTAUTOCLOSE))
{
IWebBrowser2 * pWebBrowser;
HRESULT hres = _psp->QueryService(SID_SWebBrowserApp,
IID_PPV_ARG(IWebBrowser2, &pWebBrowser));
if (SUCCEEDED(hres))
{
pWebBrowser->put_Visible(VARIANT_FALSE);
pWebBrowser->Release();
}
}
}
//
// support for script error caching and status bar notification
//
void
CDocObjectHost::_ScriptErr_Dlg(BOOL fOverridePerErrorMode)
{
// we can get reentered through the message pump ShowHTMLDialog runs
// therefore we might already have a dialog open when a second dialog
// is requested
if (_fScriptErrDlgOpen)
{
// a dialog is already open lower in the callstack
// request an up-to-date dialog be shown
// we have to do this because otherwise we might
// be in per-error-mode and miss some errors which
// arrived while the dialog lower in the callstack
// was open. note that we only do this if we're set
// to show notifications for every error.
_fShowScriptErrDlgAgain = SHRegGetBoolUSValue(szRegKey_SMIEM,
szRegVal_ErrDlgPerErr,
FALSE,
TRUE);
}
else
{
_fScriptErrDlgOpen = TRUE;
// keep showing dialogs as long as someone farther up the
// call stack keeps requesting them
do
{
BOOL fShowDlg;
_fShowScriptErrDlgAgain = FALSE;
// if the user double clicked on the status bar, then we
// show the dialog regardless of per-error-mode settings
if (fOverridePerErrorMode)
{
fShowDlg = TRUE;
// because of other script errors hitting the
// _fScriptErrDlgOpen code above, we can arrive
// here multiple times. The first time we show a
// dialog can be because the user requested it,
// but all subsequent times must be because we're
// in "show every error" mode.
fOverridePerErrorMode = FALSE;
}
else
{
fShowDlg = SHRegGetBoolUSValue(szRegKey_SMIEM,
szRegVal_ErrDlgPerErr,
FALSE,
TRUE);
}
if (fShowDlg)
{
HRESULT hr;
TCHAR szResURL[MAX_URL_STRING];
hr = MLBuildResURLWrap(TEXT("shdoclc.dll"),
HINST_THISDLL,
ML_CROSSCODEPAGE,
TEXT("ieerror.dlg"),
szResURL,
ARRAYSIZE(szResURL),
TEXT("shdocvw.dll"));
if (SUCCEEDED(hr))
{
IMoniker * pmk;
HWND hwnd;
hr = CreateURLMoniker(NULL, szResURL, &pmk);
if (SUCCEEDED(hr))
{
VARIANT varErrorCache;
V_VT(&varErrorCache) = VT_DISPATCH;
V_DISPATCH(&varErrorCache) = _pScriptErrList;
GetWindow(&hwnd);
ShowHTMLDialog(hwnd, pmk, &varErrorCache, L"help:no", NULL);
ATOMICRELEASE(pmk);
}
}
}
} while (_fShowScriptErrDlgAgain);
_fScriptErrDlgOpen = FALSE;
}
}
HRESULT
CDocObjectHost::_ScriptErr_CacheInfo(VARIANTARG *pvarIn)
{
IHTMLDocument2 * pOmDoc;
IHTMLWindow2 * pOmWindow;
IHTMLEventObj * pEventObj;
HRESULT hr;
TCHAR * apchNames[] =
{ TEXT("errorLine"),
TEXT("errorCharacter"),
TEXT("errorCode"),
TEXT("errorMessage"),
TEXT("errorUrl")
};
DISPID aDispid[ARRAYSIZE(apchNames)];
VARIANT varOut[ARRAYSIZE(apchNames)];
int i;
pOmDoc = NULL;
pOmWindow = NULL;
pEventObj = NULL;
// load the script error object
hr = V_UNKNOWN(pvarIn)->QueryInterface(IID_IHTMLDocument2, (void **) &pOmDoc);
if (FAILED(hr))
{
return hr;
}
hr = pOmDoc->get_parentWindow(&pOmWindow);
ATOMICRELEASE(pOmDoc);
if (FAILED(hr))
{
return hr;
}
hr = pOmWindow->get_event(&pEventObj);
ATOMICRELEASE(pOmWindow);
if (FAILED(hr))
{
return hr;
}
//
// copy the interesting data out of the event object
//
for (i = 0; i < ARRAYSIZE(apchNames); i++)
{
DISPPARAMS params;
// get the property's dispid
hr = pEventObj->GetIDsOfNames(IID_NULL, &apchNames[i], 1, LOCALE_SYSTEM_DEFAULT, &aDispid[i]);
if (hr != S_OK)
{
ATOMICRELEASE(pEventObj);
return hr;
}
params.cArgs = 0;
params.cNamedArgs = 0;
hr = pEventObj->Invoke(aDispid[i], IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &params, &varOut[i], NULL, NULL);
if (hr != S_OK)
{
ATOMICRELEASE(pEventObj);
return hr;
}
}
ATOMICRELEASE(pEventObj);
ASSERT(V_VT(&varOut[0]) == VT_I4);
ASSERT(V_VT(&varOut[1]) == VT_I4);
ASSERT(V_VT(&varOut[2]) == VT_I4);
ASSERT(V_VT(&varOut[3]) == VT_BSTR);
ASSERT(V_VT(&varOut[4]) == VT_BSTR);
ASSERT(ARRAYSIZE(apchNames) == 5);
hr = _pScriptErrList->AddNewErrorInfo(V_I4(&varOut[0]), // line
V_I4(&varOut[1]), // char
V_I4(&varOut[2]), // code
V_BSTR(&varOut[3]), // message
V_BSTR(&varOut[4])); // url
return hr;
}
//
// CScriptErrorList manages an array of _CScriptErrInfo objects
// the script error handler dialogs access this information
// when requested by the user
//
CScriptErrorList::CScriptErrorList() :
CImpIDispatch(LIBID_SHDocVw, 1, 1, IID_IScriptErrorList)
{
ASSERT(_lDispIndex == 0);
_ulRefCount = 1;
_hdpa = DPA_Create(4);
}
CScriptErrorList::~CScriptErrorList()
{
if (_hdpa != NULL)
{
ClearErrorList();
DPA_Destroy(_hdpa);
_hdpa = NULL;
}
}
HRESULT
CScriptErrorList::AddNewErrorInfo(LONG lLine,
LONG lChar,
LONG lCode,
BSTR strMsg,
BSTR strUrl)
{
HRESULT hr;
_CScriptErrInfo * pNewData;
if (strMsg == NULL || strUrl == NULL)
{
return E_INVALIDARG;
}
pNewData = new _CScriptErrInfo;
if (pNewData != NULL)
{
hr = pNewData->Init(lLine, lChar, lCode, strMsg, strUrl);
if (SUCCEEDED(hr))
{
if (_hdpa != NULL)
{
DPA_AppendPtr(_hdpa, (LPVOID)pNewData);
_lDispIndex = DPA_GetPtrCount(_hdpa)-1;
}
else
{
delete pNewData;
hr = E_FAIL;
}
}
else
{
delete pNewData;
}
}
else
{
hr = E_OUTOFMEMORY;
}
return hr;
}
void
CScriptErrorList::ClearErrorList()
{
if (_hdpa != NULL)
{
int iDel;
int cPtr;
cPtr = DPA_GetPtrCount(_hdpa);
// delete from end to beginning to avoid unnecessary packing
for (iDel = cPtr-1; iDel >= 0; iDel--)
{
delete ((_CScriptErrInfo *)DPA_GetPtr(_hdpa, iDel));
DPA_DeletePtr(_hdpa, iDel);
}
_lDispIndex = 0;
}
}
STDMETHODIMP
CScriptErrorList::QueryInterface(REFIID iid, void ** ppObj)
{
ASSERT(ppObj != NULL);
if (IsEqualIID(iid, IID_IUnknown) ||
IsEqualIID(iid, IID_IDispatch) ||
IsEqualIID(iid, IID_IScriptErrorList))
{
*ppObj = (IScriptErrorList *)this;
}
else
{
*ppObj = NULL;
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
STDMETHODIMP_(ULONG)
CScriptErrorList::AddRef()
{
_ulRefCount++;
return _ulRefCount;
}
STDMETHODIMP_(ULONG)
CScriptErrorList::Release()
{
_ulRefCount--;
if (_ulRefCount > 0)
{
return _ulRefCount;
}
delete this;
return 0;
}
STDMETHODIMP
CScriptErrorList::advanceError()
{
HRESULT hr;
hr = E_FAIL;
if (_hdpa != NULL)
{
int cPtr;
cPtr = DPA_GetPtrCount(_hdpa);
if (_lDispIndex < cPtr-1)
{
_lDispIndex++;
hr = S_OK;
}
}
return hr;
}
STDMETHODIMP
CScriptErrorList::retreatError()
{
if (_lDispIndex < 1)
{
return E_FAIL;
}
_lDispIndex--;
return S_OK;
}
STDMETHODIMP
CScriptErrorList::canAdvanceError(BOOL * pfCanAdvance)
{
HRESULT hr;
ASSERT(pfCanAdvance != NULL);
hr = E_FAIL;
if (_hdpa != NULL)
{
int cPtr;
cPtr = DPA_GetPtrCount(_hdpa);
*pfCanAdvance = _lDispIndex < cPtr-1;
hr = S_OK;
}
return hr;
}
STDMETHODIMP
CScriptErrorList::canRetreatError(BOOL * pfCanRetreat)
{
ASSERT(pfCanRetreat != NULL);
*pfCanRetreat = _lDispIndex > 0;
return S_OK;
}
STDMETHODIMP
CScriptErrorList::getErrorLine(LONG * plLine)
{
HRESULT hr;
ASSERT(plLine != NULL);
ASSERT(_lDispIndex >= 0);
hr = E_FAIL;
if (_hdpa != NULL)
{
int cPtr;
cPtr = DPA_GetPtrCount(_hdpa);
ASSERT(_lDispIndex < cPtr || _lDispIndex == 0);
if (cPtr > 0)
{
_CScriptErrInfo * pInfo;
pInfo = (_CScriptErrInfo *)DPA_GetPtr(_hdpa, _lDispIndex);
*plLine = pInfo->_lLine;
hr = S_OK;
}
}
return hr;
}
STDMETHODIMP
CScriptErrorList::getErrorChar(LONG * plChar)
{
HRESULT hr;
ASSERT(plChar != NULL);
ASSERT(_lDispIndex >= 0);
hr = E_FAIL;
if (_hdpa != NULL)
{
int cPtr;
cPtr = DPA_GetPtrCount(_hdpa);
ASSERT(_lDispIndex < cPtr || _lDispIndex == 0);
if (cPtr > 0)
{
_CScriptErrInfo * pInfo;
pInfo = (_CScriptErrInfo *)DPA_GetPtr(_hdpa, _lDispIndex);
*plChar = pInfo->_lChar;
hr = S_OK;
}
}
return hr;
}
STDMETHODIMP
CScriptErrorList::getErrorCode(LONG * plCode)
{
HRESULT hr;
ASSERT(plCode != NULL);
ASSERT(_lDispIndex >= 0);
hr = E_FAIL;
if (_hdpa != NULL)
{
int cPtr;
cPtr = DPA_GetPtrCount(_hdpa);
ASSERT(_lDispIndex < cPtr || _lDispIndex == 0);
if (cPtr > 0)
{
_CScriptErrInfo * pInfo;
pInfo = (_CScriptErrInfo *)DPA_GetPtr(_hdpa, _lDispIndex);
*plCode = pInfo->_lCode;
hr = S_OK;
}
}
return hr;
}
STDMETHODIMP
CScriptErrorList::getErrorMsg(BSTR * pstrMsg)
{
HRESULT hr;
ASSERT(pstrMsg != NULL);
ASSERT(_lDispIndex >= 0);
hr = E_FAIL;
if (_hdpa != NULL)
{
int cPtr;
cPtr = DPA_GetPtrCount(_hdpa);
ASSERT(_lDispIndex < cPtr || _lDispIndex == 0);
if (cPtr > 0)
{
_CScriptErrInfo * pInfo;
pInfo = (_CScriptErrInfo *)DPA_GetPtr(_hdpa, _lDispIndex);
*pstrMsg = SysAllocString(pInfo->_strMsg);
if (*pstrMsg != NULL)
{
hr = S_OK;
}
else
{
hr = E_OUTOFMEMORY;
}
}
}
return hr;
}
STDMETHODIMP
CScriptErrorList::getErrorUrl(BSTR * pstrUrl)
{
HRESULT hr;
ASSERT(pstrUrl != NULL);
ASSERT(_lDispIndex >= 0);
hr = E_FAIL;
if (_hdpa != NULL)
{
int cPtr;
cPtr = DPA_GetPtrCount(_hdpa);
ASSERT(_lDispIndex < cPtr || _lDispIndex == 0);
if (cPtr > 0)
{
_CScriptErrInfo * pInfo;
pInfo = (_CScriptErrInfo *)DPA_GetPtr(_hdpa, _lDispIndex);
*pstrUrl = SysAllocString(pInfo->_strUrl);
if (*pstrUrl != NULL)
{
hr = S_OK;
}
else
{
hr = E_OUTOFMEMORY;
}
}
}
return hr;
}
STDMETHODIMP
CScriptErrorList::getAlwaysShowLockState(BOOL * pfAlwaysShowLocked)
{
*pfAlwaysShowLocked = IsInetcplRestricted(TEXT("Advanced"));
return S_OK;
}
STDMETHODIMP
CScriptErrorList::getDetailsPaneOpen(BOOL * pfDetailsPaneOpen)
{
*pfDetailsPaneOpen =
SHRegGetBoolUSValue(szRegKey_SMIEM,
szRegVal_ErrDlgDetailsOpen,
FALSE,
FALSE);
return S_OK;
}
STDMETHODIMP
CScriptErrorList::setDetailsPaneOpen(BOOL fDetailsPaneOpen)
{
TCHAR szYes[] = TEXT("yes");
TCHAR szNo[] = TEXT("no");
LPTSTR pszVal;
int cbSize;
if (fDetailsPaneOpen)
{
pszVal = szYes;
cbSize = sizeof(szYes);
}
else
{
pszVal = szNo;
cbSize = sizeof(szNo);
}
SHRegSetUSValue(szRegKey_SMIEM,
szRegVal_ErrDlgDetailsOpen,
REG_SZ,
pszVal,
cbSize,
SHREGSET_HKCU | SHREGSET_FORCE_HKCU);
// even if it failed, we can't do anything about it...
return S_OK;
}
STDMETHODIMP
CScriptErrorList::getPerErrorDisplay(BOOL * pfPerErrorDisplay)
{
*pfPerErrorDisplay =
SHRegGetBoolUSValue(szRegKey_SMIEM,
szRegVal_ErrDlgPerErr,
FALSE,
FALSE);
return S_OK;
}
STDMETHODIMP
CScriptErrorList::setPerErrorDisplay(BOOL fPerErrorDisplay)
{
TCHAR szYes[] = TEXT("yes");
TCHAR szNo[] = TEXT("no");
LPTSTR pszVal;
int cbSize;
if (fPerErrorDisplay)
{
pszVal = szYes;
cbSize = sizeof(szYes);
}
else
{
pszVal = szNo;
cbSize = sizeof(szNo);
}
SHRegSetUSValue(szRegKey_SMIEM,
szRegVal_ErrDlgPerErr,
REG_SZ,
pszVal,
cbSize,
SHREGSET_HKCU | SHREGSET_FORCE_HKCU);
// even if it failed, we can't do anything about it...
return S_OK;
}
HRESULT
CScriptErrorList::_CScriptErrInfo::Init(LONG lLine,
LONG lChar,
LONG lCode,
BSTR strMsg,
BSTR strUrl)
{
ASSERT(_strMsg == NULL);
ASSERT(_strUrl == NULL);
_strMsg = SysAllocString(strMsg);
if (_strMsg == NULL)
{
return E_OUTOFMEMORY;
}
_strUrl = SysAllocString(strUrl);
if (_strUrl == NULL)
{
SysFreeString(_strMsg);
return E_OUTOFMEMORY;
}
_lLine = lLine;
_lChar = lChar;
_lCode = lCode;
return S_OK;
}
CScriptErrorList::_CScriptErrInfo::~_CScriptErrInfo()
{
if (_strMsg != NULL)
{
SysFreeString(_strMsg);
}
if (_strUrl != NULL)
{
SysFreeString(_strUrl);
}
}