Windows2000/private/shell/shdocvw/hlframe.cpp
2020-09-30 17:12:32 +02:00

1334 lines
44 KiB
C++

#include "priv.h"
#include "resource.h"
#include "hlframe.h"
#include "bindcb.h"
#include "winlist.h"
#include "iface.h"
#include "shdocfl.h"
#include <mluisupp.h>
#define DM_SHELLEXECOBJECT 0x80000000
// flags for SHDVID_DOCFAMILYCHARSET communication
#define DFC_URLCHARSET 1
#ifdef FEATURE_PICS
#include "dochost.h" /* for IID_IsPicsBrowser */
#endif
#ifdef DEBUG
extern DWORD g_dwPerf;
#endif
#define DM_HLINKTRACE DM_TRACE
#define DM_WEBCHECKDRT 0
#define JMPMSG(psz, psz2) TraceMsg(0, "shlf TR-CDOV::%s %s", psz, psz2)
#define JMPMSG2(psz, x) TraceMsg(0, "shlf TR-CDOV::%s %x", psz, x)
#define DOFMSG(psz) TraceMsg(0, "shlf TR-DOF::%s", psz)
#define DOFMSG2(psz, x) TraceMsg(0, "shlf TR-DOF::%s %x", psz, x)
#define URLMSG(psz) TraceMsg(0, "shlf TR-DOF::%s", psz)
#define URLMSG2(psz, x) TraceMsg(0, "shlf TR-DOF::%s %x", psz, x)
#define URLMSG3(psz, x, y) TraceMsg(0, "shlf TR-DOF::%s %x %x", psz, x, y)
#define BSCMSG(psz, i, j) TraceMsg(0, "shlf TR-BSC::%s %x %x", psz, i, j)
#define BSCMSG3(psz, i, j, k) TraceMsg(0, "shlf TR-BSC::%s %x %x %x", psz, i, j, k)
#define BSCMSGS(psz, sz) TraceMsg(0, "shlf TR-BSC::%s %s", psz, sz)
#define OIPSMSG(psz) TraceMsg(0, "shlf TR-OIPS::%s", psz)
#define OIPSMSG3(psz, sz, p) TraceMsg(0, "shlf TR-OIPS::%s %s,%x", psz, sz,p)
#define REFMSG0(psz) TraceMsg(0, "shlf TR-CDOV::%s", psz)
#define REFMSG(psz, cRef) TraceMsg(0, "shlf TR-CDOV::%s new _cRef==%d", psz, cRef)
#define REFMSG2(psz, if, cRef) TraceMsg(0, "shlf TR-CDOV::%s(%s) new _cRef==%d", psz, if, cRef)
#define VIEWMSG(psz) TraceMsg(0, "shlf TR CDOV::%s", psz)
#define VIEWMSG2(psz,xx) TraceMsg(0, "shlf TR CDOV::%s %x", psz,xx)
#define CACHEMSG(psz, d) TraceMsg(0, "shlf TR CDocObjectCtx::%s %d", psz, d)
#define HFRMMSG(psz) TraceMsg(TF_SHDNAVIGATE, "shlf HFRM::%s", psz)
#define HFRMMSG2(psz, x, y) TraceMsg(TF_SHDNAVIGATE, "shlf HFRM::%s %x %x", psz, x, y)
#define MNKMSG(psz, psz2) TraceMsg(0, "shlf MNK::%s (%s)", psz, psz2)
#define SERVMSG(psz, x, y) TraceMsg(0, "shlf SERV::%s %x %x", psz, x, y)
BOOL g_fHlinkDLLLoaded = FALSE; // must be per-process
#ifdef UNIX
extern "C"
#endif
STDAPI HlinkFrameNavigate(DWORD grfHLNF, LPBC pbc, IBindStatusCallback *pibsc, IHlink* pihlNavigate, IHlinkBrowseContext *pihlbc);
#ifdef UNIX
extern "C"
#endif
STDAPI HlinkFrameNavigateNHL(DWORD grfHLNF, LPBC pbc, IBindStatusCallback *pibsc, LPCWSTR pszTargetFrame, LPCWSTR pszUrl, LPCWSTR pszLocation);
// IHlinkFrame members
HRESULT CIEFrameAuto::SetBrowseContext(IHlinkBrowseContext *pihlbc)
{
if (pihlbc)
pihlbc->AddRef();
if (_phlbc) {
if (_dwRegHLBC) {
_phlbc->Revoke(_dwRegHLBC);
_dwRegHLBC = 0;
}
_phlbc->Release();
}
_phlbc = pihlbc;
return NOERROR;
}
HRESULT CIEFrameAuto::GetBrowseContext(IHlinkBrowseContext **ppihlbc)
{
TraceMsg(0, "shlf TR ::GetBrowseContext called");
*ppihlbc = _phlbc;
if (_phlbc) {
_phlbc->AddRef();
return S_OK;
}
return E_FAIL;
}
void CIEFrameAuto::_SetPendingNavigateContext(LPBC pbc, IBindStatusCallback *pibsc)
{
if (_pbscPending) {
_pbscPending->Release();
_pbscPending = NULL;
}
if (_pbcPending) {
_pbcPending->Release();
_pbcPending = NULL;
}
if (pibsc) {
_pbscPending = pibsc;
_pbscPending->AddRef();
}
if (pbc) {
_pbcPending = pbc;
_pbcPending->AddRef();
}
}
// NavigateContext is a set of parameters passed from one CIEFrameAuto to another.
void CIEFrameAuto::_ActivatePendingNavigateContext()
{
if (_pbsc) {
_pbsc->Release();
_pbsc = NULL;
}
if (_pbc) {
_pbc->Release();
_pbc = NULL;
}
if(_pwszShortcutPath)
{
LocalFree(_pwszShortcutPath);
_pwszShortcutPath = NULL;
}
if (_pbscPending) {
_pbsc = _pbscPending;
_pbscPending = NULL;
}
if (_pbcPending) {
_pbc = _pbcPending;
_pbcPending = NULL;
}
if(_pwszShortcutPathPending) {
_pwszShortcutPath = _pwszShortcutPathPending;
_pwszShortcutPathPending = NULL;
}
}
// Called to guarantee a newly created HLinkFrame's window is
// visible after the navigate.
HRESULT ShowHlinkFrameWindow(LPUNKNOWN pUnkTargetHlinkFrame)
{
IWebBrowserApp* pdie;
HRESULT hres;
hres = pUnkTargetHlinkFrame->QueryInterface(IID_IWebBrowserApp, (void **)&pdie);
if (SUCCEEDED(hres)) {
pdie->put_Visible(TRUE);
pdie->Release();
}
return hres;
}
HRESULT CIEFrameAuto::_NavigateMagnum(DWORD grfHLNF, LPBC pbc, IBindStatusCallback *pibsc, LPCWSTR pszTargetName, LPCWSTR pszUrl, LPCWSTR pszLocation, IHlink *pihlNavigate, IMoniker *pmkTarget)
{
HRESULT hres = NOERROR;
HFRMMSG2("Navigate called", grfHLNF, pihlNavigate);
BOOL fNavigateForReal = pszUrl || (pihlNavigate && (pihlNavigate != (IHlink*)-1));
_fSuppressHistory = _psbProxy != _psb; // no history for search band, etc
_fSuppressSelect = _psbProxy != _psb; // no need to record select pidl
if (grfHLNF != (DWORD)-1)
{
if (SHHLNF_WRITENOHISTORY & grfHLNF)
{
_fSuppressHistory = TRUE;
}
if (SHHLNF_NOAUTOSELECT & grfHLNF)
{
_fSuppressSelect = TRUE;
}
}
if (pbc == NULL && pibsc == NULL && pihlNavigate == NULL && pszUrl == NULL) {
// This is a private interface so that mshtml can do navigation
// if it is hosted by the shell. When IHlinkBrowseContext is implemented
// in the shell this special code can be removed and the associated
// code in mshtml that calls Navigate with these special parameters
// can be removed so that it just goes through the
// IHlinkBrowseContext->SetCurrentHlink interface.
// We also use this private mechanism to release the navigation
// context with grfHLNF==0.
switch (grfHLNF&~(SHHLNF_WRITENOHISTORY|SHHLNF_NOAUTOSELECT)) {
case HLNF_NAVIGATINGBACK:
hres = _BrowseObject(PIDL_LOCALHISTORY, SBSP_SAMEBROWSER|SBSP_NAVIGATEBACK);
break;
case HLNF_NAVIGATINGFORWARD:
hres = _BrowseObject(PIDL_LOCALHISTORY, SBSP_SAMEBROWSER|SBSP_NAVIGATEFORWARD);
break;
case 0:
_ActivatePendingNavigateContext();
break;
default:
hres = E_INVALIDARG;
break;
}
return hres;
}
#ifdef FEATURE_PICS
/* As part of checking ratings, the PICS code will silently download the
* root document of a site to look for rating labels in it. If that's a
* frameset page, Trident will create OCXs for the subframes and try to
* navigate them, which will invoke ratings checks for them and cause
* infinite recursion. So here we check to see if our top-level browser
* is really this PICS download, and if it is, we don't do any navigation.
*/
IUnknown *punkPics;
if (SUCCEEDED(QueryService(SID_STopLevelBrowser, IID_IsPicsBrowser, (void **)&punkPics))) {
punkPics->Release();
return S_OK;
}
#endif
// If we've got this call while we are busy (EnableModeless is FALSE),
// we should just bail here (instead of doing somthing and let _JumpTo
// call fail.
// This can happen if someone has a window.location="foobar.htm" in their unload
// event handler.
if (fNavigateForReal && !(grfHLNF & HLNF_OPENINNEWWINDOW))
{
// If _pbs is NULL, it is bad news; we can't navigate.
// An allowable reason for this condition is that someone has called CIEFrameAuto::Quit()
// and we are in the process of shutting down.
if (_pbs == NULL)
{
if (_fQuitInProgress)
{
TraceMsg(TF_WARNING, "CIEFrameAuto::_NavigateMagnum quitting due to browser closing.");
return S_OK;
}
TraceMsg(TF_WARNING, "CIEFrameAuto::_NavigateMagnum _pbs is NULL, but we are not shutting down.");
return E_FAIL;
}
// If we have a _pbs but the browser says that it can't navigate now, then return S_FALSE.
else if (_pbs->CanNavigateNow() != S_OK)
{
TraceMsg(TF_WARNING, "CIEFrameAuto::Navigate CanNavigateNow returns non S_OK, bail out.");
return S_FALSE;
}
}
// This Navigate method is not re-entrant (because of _SetPendingNavigateContext)
if (_fBusy) {
TraceMsg(DM_WARNING, "CIEA::Navigate re-entered. Returning E_FAIL");
return E_FAIL;
}
_fBusy = TRUE;
// HACK: To let Webcheck DRT go.
if (fNavigateForReal && !(grfHLNF & HLNF_OPENINNEWWINDOW)) {
TraceMsg(DM_WEBCHECKDRT, "CIFA::Navigate calling _CancelPendingNavigation");
VARIANT var = { 0 };
var.vt = VT_I4;
var.lVal = TRUE; // synchronous
_CancelPendingNavigation(&var);
}
if (pszUrl && SHRestricted2(REST_NOFILEURL, NULL, 0) && IsFileUrlW(pszUrl))
{
TCHAR szPath[MAX_URL_STRING];
SHUnicodeToTChar(pszUrl, szPath, ARRAYSIZE(szPath));
MLShellMessageBox(NULL, MAKEINTRESOURCE(IDS_SHURL_ERR_PARSE_NOTALLOWED), szPath, MB_OK | MB_ICONERROR, szPath);
_fBusy = FALSE;
return E_ACCESSDENIED;
}
_SetPendingNavigateContext(pbc, pibsc);
#ifdef DEBUG
g_dwPerf = GetCurrentTime();
#endif
if (pihlNavigate == (IHlink*)-1) {
// HACK: -1 means "release the navigation state".
// CDocObjectHost::_CancelPendingNavigation is the only caller.
// It Exec's SBCMDID_CANCELNAVIGATION which will asynchronously
// cancel the pending navigation. Therefore, we no longer need
// to call _CancelPendingNavigation here. (SatoNa)
// _CancelPendingNavigation();
} else if (pihlNavigate || pszUrl) {
hres = S_OK;
if (SUCCEEDED(hres))
{
if ((grfHLNF & HLNF_EXTERNALNAVIGATE) && (grfHLNF & HLNF_NAVIGATINGBACK))
GoBack();
else if ((grfHLNF & HLNF_EXTERNALNAVIGATE) && (grfHLNF & HLNF_NAVIGATINGFORWARD))
GoForward();
else {
hres = _JumpTo(pbc,(LPWSTR) pszLocation, grfHLNF, pibsc, pihlNavigate, pszTargetName, pszUrl);
if (FAILED(hres)) {
TraceMsg(DM_ERROR, "IEAuto::Navigate _JumpTo failed %x", hres);
}
}
if (pihlNavigate)
{
// BUGBUG: Hopefully, we'll come up with a clean solution to
// solve this problem nicely. I made a proposal to NatBro/SriniK
// that CreateHlink will CoCreateInstance IHlink so that OLE
// LoadLibrary it and maintains it as an InProc server. (SatoNa)
// HACK: If we AddRef to IHlink, we need to make it sure that
// HLINK.DLL is stay loaded even though the DocObject InProc
// server (that implicitly links to HLINK.DLL) is unloaded.
if (!g_fHlinkDLLLoaded) {
LoadLibrary(TEXT("hlink.dll"));
g_fHlinkDLLLoaded = TRUE;
}
}
} else {
TraceMsg(DM_ERROR, "CIEFA::Nav phl->GetMonRef failed %x", hres);
}
}
_fBusy = FALSE;
HFRMMSG2("Navigate returning", hres, 0);
if (SUCCEEDED(hres) && (pihlNavigate != (IHlink*)-1)) {
if (grfHLNF & HLNF_EXTERNALNAVIGATE) {
HWND hwndFrame;
_psb->GetWindow(&hwndFrame);
if (_phlbc) {
// if we have a browse context, then we're navigating from it and
// we should size our window to match it.
HLBWINFO hlbwi;
hlbwi.cbSize = SIZEOF(hlbwi);
if (SUCCEEDED(_phlbc->GetBrowseWindowInfo(&hlbwi)) && (hlbwi.grfHLBWIF & HLBWIF_HASFRAMEWNDINFO)) {
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(hwndFrame, &wp);
wp.rcNormalPosition = hlbwi.rcFramePos;
wp.showCmd = (hlbwi.grfHLBWIF & HLBWIF_FRAMEWNDMAXIMIZED) ? SW_SHOWMAXIMIZED : SW_SHOWNORMAL;
// This is not broken in AOL because this
// is an external navigate (word has cocreateinstance()d
// Internet.Explorer and navigated it.
SetWindowPlacement(hwndFrame, &wp);
}
// Register the hlinkframe interface with the browse context, if it has not already
// been registered
if (_dwRegHLBC == 0)
_phlbc->Register(0, (IHlinkFrame *) this, pmkTarget, &_dwRegHLBC);
// add the link to browse context and
// REVIEW: need to pass the proper friendly name
_phlbc->OnNavigateHlink(grfHLNF, pmkTarget, pszLocation, NULL, NULL);
}
put_Visible(TRUE);
SetForegroundWindow(hwndFrame);
}
// According to SriniK, we need to call IHlinkSite::OnNavigationComplete
// before returning from IHlinkFrame::Navigate with S_OK. (SatoNa)
if (pihlNavigate) {
BOOL fExternal = FALSE;
if (_phlbc && _pbs) {
ITravelLog* ptl;
if (SUCCEEDED(_pbs->GetTravelLog(&ptl))) {
if (FAILED(ptl->GetTravelEntry(_pbs, 0, NULL))) {
TraceMsg(DM_HLINKTRACE, "CIEFA::_NavMag this is external nav. Don't call OnNavigationComplete");
fExternal = TRUE;
} else if (SUCCEEDED(ptl->GetTravelEntry(_pbs, TLOG_BACKEXTERNAL, NULL))) {
TraceMsg(DM_HLINKTRACE, "CIEFA::_NavMag this is external for. Don't call OnNavigationComplete");
fExternal = TRUE;
}
ptl->Release();
}
}
// Don't call OnNavigationComplete if this is an external navigation.
if (!fExternal) {
IHlinkSite* pihlSite = NULL;
DWORD dwSiteData;
HRESULT hresT=pihlNavigate->GetHlinkSite(&pihlSite, &dwSiteData);
if (SUCCEEDED(hresT) && pihlSite) {
TraceMsg(DM_HLINKTRACE, "CIEFA::_NavMag calling OnNavigationComplete");
hresT = pihlSite->OnNavigationComplete(dwSiteData, 0, S_OK, L"");
if (FAILED(hresT)) {
TraceMsg(DM_ERROR, "CIEFA::Navigat OnNavComplete failed %x", hresT);
}
pihlSite->Release();
}
}
}
}
return hres;
}
// BUGBUGHACK - what we really want is a good private marshalled interface - zekel 8-AUG-97
// to the Browser. but for now we will overload the NavigateHack method,
// because it is simple and quick for ship.
#define HLNF_REFERRERHACK 0x40000000
HRESULT CIEFrameAuto::_ReferrerHack(LPCWSTR pszUrl)
{
if(_pbs == NULL) //Make sure we have a IBrowserService.
return(S_FALSE);
LPITEMIDLIST pidl;
if(SUCCEEDED(_pbs->IEParseDisplayName(CP_ACP, pszUrl, &pidl)))
{
ASSERT(pidl);
_pbs->SetReferrer(pidl);
ILFree(pidl);
}
return S_OK;
}
HRESULT CIEFrameAuto::NavigateHack(DWORD grfHLNF, LPBC pbc, IBindStatusCallback *pibsc, LPCWSTR pszTargetName, LPCWSTR pszUrl, LPCWSTR pszLocation)
{
if (IsFlagSet(grfHLNF, HLNF_REFERRERHACK))
return _ReferrerHack(pszUrl);
else
return _NavigateMagnum(grfHLNF, pbc, pibsc, pszTargetName, pszUrl, pszLocation, NULL, NULL);
}
// passing NULL pibsc and pbc will make be like "ReleaseNavigationState"
// passing -1 for pihlNavigate will cancel pending navigation
HRESULT CIEFrameAuto::Navigate(DWORD grfHLNF, LPBC pbc, IBindStatusCallback *pibsc, IHlink *pihlNavigate)
{
IMoniker* pmkTarget = NULL;
LPOLESTR pwszDisplayName = NULL;
LPOLESTR pwszLocation = NULL;
LPOLESTR pwszFrameName = NULL;
HRESULT hres = S_OK;
if (pihlNavigate && ((IHlink *)-1) != pihlNavigate)
{
pihlNavigate->GetTargetFrameName(&pwszFrameName);
// BUGBUG: Note that we are discarding "relative" portion.
hres = pihlNavigate->GetMonikerReference(HLINKGETREF_ABSOLUTE, &pmkTarget, &pwszLocation);
HFRMMSG2("Navigate pihl->GetMonRef returned", hres, pmkTarget);
if (SUCCEEDED(hres))
{
IBindCtx* pbcLocal;
if (pbc) {
pbcLocal = pbc;
pbcLocal->AddRef();
} else {
hres = CreateBindCtx(0, &pbcLocal);
}
if (SUCCEEDED(hres))
{
hres = pmkTarget->GetDisplayName(pbcLocal, NULL, &pwszDisplayName);
pbcLocal->Release();
}
}
}
if (SUCCEEDED(hres))
{
hres = _NavigateMagnum(grfHLNF, pbc, pibsc, pwszFrameName, pwszDisplayName, pwszLocation, pihlNavigate, pmkTarget);
}
if (pwszFrameName)
{
OleFree(pwszFrameName);
}
if (pwszDisplayName)
{
OleFree(pwszDisplayName);
}
if (pwszLocation)
{
OleFree(pwszLocation);
}
if (pmkTarget)
{
pmkTarget->Release();
}
return hres;
}
HRESULT CIEFrameAuto::OnNavigate(DWORD grfHLNF,
/* [unique][in] */ IMoniker *pimkTarget,
/* [unique][in] */ LPCWSTR pwzLocation,
/* [unique][in] */ LPCWSTR pwzFriendlyName,
/* [in] */ DWORD dwreserved)
{
// BUGBUG: Implement it later
TraceMsg(0, "shlf TR ::OnNavigate called");
return S_OK;
}
void CIEFrameAuto::_CancelPendingNavigation(VARIANTARG* pvar)
{
TraceMsg(0, "shd TR _CancelPendingNavigation called");
if (_pmsc) {
TraceMsg(0, "shd TR _CancelPendingNavigation calling _pmsc->Exec");
_pmsc->Exec(&CGID_Explorer, SBCMDID_CANCELNAVIGATION, 0, pvar, NULL);
}
}
// *** ITargetNotify ***
HRESULT CIEFrameAuto::OnCreate(LPUNKNOWN pUnkDestination, ULONG cbCookie)
{
HRESULT hres = S_OK;
if (cbCookie == (ULONG)_cbCookie && _ptgnNotify)
{
hres = _ptgnNotify->OnCreate(pUnkDestination, cbCookie);
SAFERELEASE(_ptgnNotify);
}
return hres;
}
HRESULT CIEFrameAuto::OnReuse(LPUNKNOWN pUnkDestination)
{
return S_OK;
}
#define NOTIFY_WAIT_TIMEOUT (60000)
// BUGBUG chrisfra 10/10/96: do we need EnableModeless(FALSE)/(TRUE) around
// our little loop, or is the busy flag (which is set) sufficient?
HRESULT CIEFrameAuto::_WaitForNotify()
{
if (_ptgnNotify)
{
DWORD dwObject, msWait, msStart = GetTickCount();
goto DOPEEK;
while (_ptgnNotify)
{
// NB We need to let the run dialog become active so we have to half handle sent
// messages but we don't want to handle any input events or we'll swallow the
// type-ahead.
msWait = GetTickCount();
if (msWait - msStart > NOTIFY_WAIT_TIMEOUT)
break;
msWait = NOTIFY_WAIT_TIMEOUT - (msWait - msStart);
dwObject = MsgWaitForMultipleObjects(0, NULL, FALSE, msWait, QS_ALLINPUT);
// Are we done waiting?
switch (dwObject)
{
case WAIT_FAILED:
break;
case WAIT_OBJECT_0:
DOPEEK:
// got a message, dispatch it and wait again
MSG msg;
while (PeekMessage(&msg, NULL,0, 0, PM_REMOVE))
{
DispatchMessage(&msg);
if (_ptgnNotify == NULL || ((GetTickCount() - msStart) > NOTIFY_WAIT_TIMEOUT))
break;
}
break;
}
}
}
return S_OK;
}
HRESULT CIEFrameAuto::_RegisterCallback(TCHAR *szFrameName, LPTARGETNOTIFY ptgnNotify)
{
HRESULT hr = S_OK;
SAFERELEASE(_ptgnNotify);
_fRegistered = 0;
if (ptgnNotify)
{
IDispatch *pid;
hr = QueryInterface(IID_IDispatch, (void **) &pid);
if (SUCCEEDED(hr))
{
hr = E_FAIL;
IShellWindows *psw = WinList_GetShellWindows(TRUE);
if (psw != NULL)
{
long cbCookie;
hr = psw->Register(pid, NULL, SWC_CALLBACK, &cbCookie);
if (SUCCEEDED(hr))
{
TCHAR szCookie[25]; // big enough for "_[cbCookie]"
int slenCookie;
int slenName;
int slenMin;
_cbCookie = cbCookie;
_fRegistered = 1;
_ptgnNotify = ptgnNotify;
_ptgnNotify->AddRef();
// prepend unique id to target -- tells created WebBrowserOC to
// register the remainder (if any) as frame name and to perform
// callbacks on all registered callbacks
wnsprintf(szCookie, ARRAYSIZE(szCookie), TEXT("_[%ld]"), cbCookie);
slenCookie = lstrlen(szCookie);
slenName = lstrlen(szFrameName);
slenMin = min((int)MAX_URL_STRING-slenCookie,slenName);
MoveMemory(&szFrameName[slenCookie], szFrameName, CbFromCch(slenMin));
szFrameName[slenCookie+slenMin] = 0;
CopyMemory(szFrameName, szCookie, CbFromCch(slenCookie));
}
psw->Release();
}
pid->Release();
}
}
return hr;
}
HRESULT CIEFrameAuto::_RevokeCallback()
{
IShellWindows* psw = NULL;
HRESULT hr = S_OK;
if (_fRegistered)
{
psw = WinList_GetShellWindows(TRUE);
if (psw != NULL)
{
hr = psw->Revoke(_cbCookie);
psw->Release();
}
}
SAFERELEASE(_ptgnNotify);
_fRegistered = 0;
return hr;
}
// BUGBUGHACK - what we really want is a good private marshalled interface - zekel 8-AUG-97
// to the Browser. but for now we will overload the NavigateHack method,
// because it is simple and quick for ship.
void CIEFrameAuto::_SetReferrer(LPTARGETFRAMEPRIV ptgfp)
{
LPITEMIDLIST pidl;
WCHAR szUrl[MAX_URL_STRING];
ASSERT(ptgfp);
//Make sure we have a IBrowserService.
if(_psb && SUCCEEDED(_pbs->GetPidl(&pidl)))
{
if (SUCCEEDED(_pbs->IEGetDisplayName(pidl, szUrl, SHGDN_FORPARSING)))
ptgfp->NavigateHack(HLNF_REFERRERHACK, NULL, NULL, NULL, szUrl, NULL);
ILFree(pidl);
}
}
HRESULT CIEFrameAuto::_JumpTo(LPBC pbc, LPWSTR pszLocation, DWORD grfHLNF, IBindStatusCallback *pibsc, IHlink *pihlNavigate, LPCWSTR pszFrameName, LPCWSTR pszUrl)
{
LPITEMIDLIST pidl = NULL;
HRESULT hres;
LPTARGETNOTIFY ptgnNotify = NULL;
IUnknown *punkNotify = NULL;
IUnknown *punkThis = NULL;
UINT uiCP = CP_ACP;
// Get the current document codepage from Trident and use it for url string conversion if necessary.
if (!(grfHLNF & HLNF_ALLOW_AUTONAVIGATE) && _pmsc)
{
VARIANT varOut = { 0 };
VARIANT varIn = { 0 };
varIn.vt = VT_I4;
varIn.lVal = DFC_URLCHARSET; // we want the doc's url charset
if (SUCCEEDED(_pmsc->Exec(&CGID_ShellDocView, SHDVID_DOCFAMILYCHARSET, 0, &varIn, &varOut)))
uiCP = (UINT)varOut.lVal;
}
// Note that we are simply passing the pidl to ISB::BrowseObject,
// assuming that new shell32.dll allows us to bind to DocObject
// documents.
DWORD flags = (grfHLNF & HLNF_OPENINNEWWINDOW) ?
(SBSP_NEWBROWSER | SBSP_ABSOLUTE | SBSP_INITIATEDBYHLINKFRAME) :
(SBSP_SAMEBROWSER | SBSP_ABSOLUTE | SBSP_INITIATEDBYHLINKFRAME);
flags |= ((grfHLNF & HLNF_ALLOW_AUTONAVIGATE) ? (SBSP_ALLOW_AUTONAVIGATE) : 0);
flags |= ((grfHLNF & SHHLNF_WRITENOHISTORY) ? (SBSP_WRITENOHISTORY) : 0);
flags |= ((grfHLNF & SHHLNF_NOAUTOSELECT) ? (SBSP_NOAUTOSELECT) : 0);
if (pbc && SUCCEEDED(pbc->GetObjectParam(TARGET_NOTIFY_OBJECT_NAME, &punkNotify)))
{
if (FAILED(punkNotify->QueryInterface(IID_ITargetNotify, (void **) &ptgnNotify)))
ptgnNotify = NULL;
punkNotify->Release();
QueryInterface(IID_IUnknown, (void **) &punkThis);
}
if (grfHLNF & HLNF_CREATENOHISTORY)
flags |= SBSP_REDIRECT;
if (flags & SBSP_NEWBROWSER)
{
TCHAR *pszHeaders = NULL;
BYTE *pPostData = NULL;
DWORD cbPostData = 0;
TCHAR szFrameName[MAX_URL_STRING+1];
STGMEDIUM stgPostData = { TYMED_NULL, NULL, NULL };
//Qfe:1478 If restricted to open in new window, return failure.
if((grfHLNF & HLNF_OPENINNEWWINDOW) && SHIsRestricted2W(_hwnd, REST_NoOpeninNewWnd, NULL, 0))
{
SAFERELEASE(punkThis);
return E_ACCESSDENIED;
}
szFrameName[0] = 0;
// Here is where if we are doing a new window we must
// extract frame, post etc and append to pidl. These must
// be done in the following order (to match extraction code):
// URLID_FRAMENAME,URLID_POSTDATA,URLID_HEADERS
if (pszFrameName)
{
OleStrToStrN(szFrameName, ARRAYSIZE(szFrameName), pszFrameName, (UINT)-1);
}
if (pibsc)
{
GetHeadersAndPostData(pibsc,&pszHeaders,&stgPostData,&cbPostData, NULL);
if (stgPostData.tymed == TYMED_HGLOBAL) {
pPostData = (LPBYTE) stgPostData.hGlobal;
}
}
hres = _PidlFromUrlEtc(uiCP, pszUrl, pszLocation, &pidl);
HFRMMSG2("_JumpTo _PidlFromUrlEtc returned", hres, pidl);
if (SUCCEEDED(hres))
{
IUnknown* punkNewWindow = NULL;
BOOL fCancel = FALSE;
// The NewWindow2 event may return the window for us.
FireEvent_NewWindow2(_GetOuter(), &punkNewWindow, &fCancel);
if (!fCancel)
{
BOOL fProcessed = FALSE;
// We might need the old NewWindow event...
if (!punkNewWindow)
{
_RegisterCallback(szFrameName, ptgnNotify);
// fire an event to indicate a new window needs to be created
// to allow a container to handle it itself if it wants
// since we may be aggregated, QI our parent
// Yet another Compuserve workaround (IE 60688):
// If the target frame name is "_blank", Compuserve will pass that name
// in to the Navigate call of the new window. We would then create a new window
// (which would fire this event) causing a loop. Break the recursion by sending
// an empty string for the frame name.
FireEvent_NewWindow(_GetOuter(), _GetHWND(), pidl,pszLocation,0,
StrCmpI(szFrameName, TEXT("_blank")) ? szFrameName : TEXT(""), // Target frame name
pPostData,cbPostData,pszHeaders,&fProcessed);
}
if (!fProcessed)
{
if (!punkNewWindow)
{
#ifdef INCLUDE_BUSTED_OC_QI
IUnknown* pdvb = NULL;
#endif
_RevokeCallback();
#ifdef INCLUDE_BUSTED_OC_QI
// For some unidentifiable reason the old code did NOT
// create a new window if we were hosted in the WebBrowserOC.
// mikesh/cheechew/jeremys/chrisfra don't know why this happens.
// Who knows what app will break if we change this...
// (Note: IDefViewBrowser is a CWebBrowseSB only interface)
// NOTE: chrisfra 3/11/97, this code breaks open a
// new window for a non-existent target, when in
// desktop component or browser band
fCancel = !(_psbTop && FAILED(_psbTop->QueryInterface(IID_IDefViewBrowser, (void **)&pdvb)));
if (pdvb)
pdvb->Release();
#endif
}
// what we really want to do is just hand this off to
// _psbTop->BrowseObject and let it (CWebBrowserSB or CShellBrowser)
// decide whether to use HlinkFrameNavigate or not, but if we
// do that, then we lose the grfHLNF and pihlNavigate.
// So put that logic here...
if (!fCancel)
{
hres = CreateTargetFrame(pszFrameName, &punkNewWindow);
if (SUCCEEDED(hres))
{
LPHLINKFRAME phf;
LPTARGETFRAMEPRIV ptgfp = NULL; // init to suppress bogus C4701 warning
// Notify ptgnNotify, then release and remove from bindctx
if (ptgnNotify)
{
ptgnNotify->OnCreate(punkNewWindow, GetTickCount());
ptgnNotify->Release();
ptgnNotify = NULL;
pbc->RevokeObjectParam(TARGET_NOTIFY_OBJECT_NAME);
}
hres = punkNewWindow->QueryInterface(IID_IHlinkFrame, (void **) &phf);
if (SUCCEEDED(hres))
{
if(NULL == pihlNavigate)
{
hres = punkNewWindow->QueryInterface(IID_ITargetFramePriv, (void **) &ptgfp);
}
if (SUCCEEDED(hres))
{
if (pihlNavigate)
{
hres = phf->Navigate(grfHLNF & ~HLNF_OPENINNEWWINDOW, pbc, pibsc, pihlNavigate);
}
else
{
// BUGBUGHACK - see this methods comments
_SetReferrer(ptgfp);
hres = ptgfp->NavigateHack(grfHLNF & ~HLNF_OPENINNEWWINDOW, pbc, pibsc, NULL, pszUrl, pszLocation);
}
if (FAILED(hres)) {
TraceMsg(DM_ERROR, "CIEFA::_JumpTo marshalled IHlinkFrame::Navigate failed %x", hres);
}
ShowHlinkFrameWindow(punkNewWindow);
if(NULL == pihlNavigate)
{
ptgfp->Release();
}
}
phf->Release();
}
}
} else {
// If NEWBROWSER is specified when there is no top level
// browser, we should ask IE/Shell to do browsing.
// We don't pass HLNF_OPENINNEWWINDOW in this case.
// Notify object doing navigation that we are the object implementing IWebBrowserApp
if (ptgnNotify) ptgnNotify->OnReuse(punkThis);
if (pihlNavigate)
{
hres = HlinkFrameNavigate(grfHLNF & ~HLNF_OPENINNEWWINDOW, NULL, NULL, pihlNavigate, NULL);
}
else
{
hres = HlinkFrameNavigateNHL(grfHLNF & ~HLNF_OPENINNEWWINDOW, NULL, NULL, NULL, pszUrl, pszLocation);
}
}
}
else
{
// Oldstyle AOL or other 3rd Party, wait for registration of
// WebBrowserOC, which calls us back on _ptgnNotify
_WaitForNotify();
// We timed out the window create, notify caller
if (_ptgnNotify)
_ptgnNotify->OnCreate(NULL, 0);
_RevokeCallback();
}
}
if (punkNewWindow)
punkNewWindow->Release();
} else {
TraceMsg(DM_ERROR, "IEAuto::_JumpTo _PidlFromUrlEtc (1) failed %x", hres);
}
if (pszHeaders) {
LocalFree(pszHeaders);
}
if (stgPostData.tymed != TYMED_NULL ) {
ReleaseStgMedium(&stgPostData);
}
}
else
{
// Notify object doing navigation that we are the object implementing IWebBrowserApp
if (ptgnNotify) ptgnNotify->OnReuse(punkThis);
hres = _PidlFromUrlEtc(uiCP, pszUrl, pszLocation, &pidl);
if (SUCCEEDED(hres)) {
hres = _psb->BrowseObject(pidl, flags);
} else {
TraceMsg(DM_ERROR, "IEAuto::_JumpTo _PidlFromUrlEtc (2) failed %x", hres);
}
}
if (pidl)
{
HFRMMSG2("_JumpTo _psb->BrowseObject returned", hres, 0);
ILFree(pidl);
}
if (ptgnNotify)
{
ptgnNotify->Release();
pbc->RevokeObjectParam(TARGET_NOTIFY_OBJECT_NAME);
}
SAFERELEASE(punkThis);
return hres;
}
HRESULT CIEFrameAuto::QueryService(REFGUID guidService, REFIID riid, void ** ppvObj)
{
*ppvObj = NULL;
// WARNING: Note that we are not following the strict semantics of
// ISP::QueryService. It is, however, OK because this (the fact that
// IHlinkFrame support IServiceProvider) is not public.
if (IsEqualIID(guidService, SID_SOmWindow))
{
return _omwin.QueryInterface(riid, ppvObj);
}
else if (IsEqualIID(guidService, IID_IHlinkFrame))
{
SERVMSG("QueryService called", _pbc, _pbsc);
if (IsEqualIID(riid, IID_IBindCtx) && _pbc)
{
*ppvObj = _pbc;
_pbc->AddRef();
}
else if (IsEqualIID(riid, IID_IBindStatusCallback) && _pbsc)
{
*ppvObj = _pbsc;
_pbsc->AddRef();
}
else
{
return QueryInterface(riid, ppvObj);
}
}
else if (IsEqualIID(guidService, SID_PendingBindStatusCallback))
{
if (IsEqualIID(riid, IID_IBindStatusCallback) && _pbscPending)
{
*ppvObj = _pbscPending;
_pbscPending->AddRef();
}
}
else if (_psp)
{
return _psp->QueryService(guidService, riid, ppvObj);
}
return *ppvObj ? S_OK : E_FAIL;
}
HRESULT CIEFrameAuto::Exec(
/* [unique][in] */ const GUID *pguidCmdGroup,
/* [in] */ DWORD nCmdID,
/* [in] */ DWORD nCmdexecopt,
/* [unique][in] */ VARIANTARG *pvarargIn,
/* [unique][out][in] */ VARIANTARG *pvarargOut)
{
HRESULT hres = S_OK;
if (pguidCmdGroup)
{
if (IsEqualGUID(CGID_Explorer, *pguidCmdGroup))
{
switch(nCmdID)
{
case SBCMDID_CANCELNAVIGATION:
_CancelPendingNavigation(NULL);
break;
case SBCMDID_SELECTHISTPIDL:
case SBCMDID_HISTSFOLDER:
if (_poctFrameTop)
hres = _poctFrameTop->Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
else
hres = S_OK;
break;
case SBCMDID_IESHORTCUT:
#ifdef BROWSENEWPROCESS_STRICT // "Nav in new process" has become "Launch in new process", so this is no longer needed
// If this is an IE shortcut and browse in a new process is turned on
// and we are explorer.exe - we should pass on the request to navigate to
// this shortcut. The caller is expected to create a new window/process to
// launch this shortcut
if (IsBrowseNewProcessAndExplorer())
hres = E_FAIL;
else
#endif
hres = _NavIEShortcut(pvarargIn,pvarargOut);
break;
case SBCMDID_GETSHORTCUTPATH:
if (_pwszShortcutPath && pvarargOut)
{
pvarargOut->bstrVal = SysAllocString(_pwszShortcutPath);
if (pvarargOut->bstrVal)
pvarargOut->vt = VT_BSTR; //no need to set hres=S_OK since it is inited already
else
hres = E_OUTOFMEMORY;
}
else
{
hres = E_FAIL;
pvarargOut->vt = VT_EMPTY;
}
break;
default:
hres = OLECMDERR_E_NOTSUPPORTED;
}
}
else if (IsEqualGUID(CGID_ShortCut, *pguidCmdGroup))
{
if (_poctFrameTop) // we must check!
hres = _poctFrameTop->Exec(&CGID_ShortCut, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
else
hres = OLECMDERR_E_NOTSUPPORTED;
}
else if (IsEqualGUID(CGID_ShellDocView, *pguidCmdGroup))
{
switch (nCmdID)
{
case SHDVID_DELEGATEWINDOWOM:
_omwin.SetDelegationPolicy(V_BOOL(pvarargIn));
break;
default:
hres = OLECMDERR_E_NOTSUPPORTED;
}
}
else if (IsEqualGUID(CGID_InternetExplorer, *pguidCmdGroup))
{
switch (nCmdID)
{
case IECMDID_CLEAR_AUTOCOMPLETE_FOR_FORMS:
{
if (pvarargIn->vt == VT_I4)
{
hres = ClearAutoSuggestForForms(V_I4(pvarargIn));
}
else
hres = E_INVALIDARG;
}
break;
default:
hres = OLECMDERR_E_NOTSUPPORTED;
}
}
else
{
hres = OLECMDERR_E_UNKNOWNGROUP;
}
}
else
{
hres = OLECMDERR_E_UNKNOWNGROUP;
}
return hres;
}
BOOL CIEFrameAuto::_fNavigationPending()
{
// unfortunately, the hyperlink frame doesn't REALLY know when there's
// a navigation pending or not because people might not call OnReleaseNavigation.
// only the real browser knows.
if (_pmsc) {
MSOCMD rgCmd;
rgCmd.cmdID = SBCMDID_CANCELNAVIGATION;
rgCmd.cmdf = 0;
_pmsc->QueryStatus(&CGID_Explorer, 1, &rgCmd, NULL);
return (rgCmd.cmdf & MSOCMDF_ENABLED);
} else {
// ASSERT(0);
}
return FALSE;
}
HRESULT CIEFrameAuto::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
{
if (pguidCmdGroup && IsEqualGUID(CGID_Explorer, *pguidCmdGroup))
{
for (ULONG i = 0; i < cCmds; i++)
{
switch (rgCmds[i].cmdID)
{
case SBCMDID_CANCELNAVIGATION:
rgCmds[i].cmdf = _fNavigationPending() ? MSOCMDF_ENABLED : 0;
break;
case SBCMDID_WRITEHIST:
rgCmds[i].cmdf = _fSuppressHistory ? 0:MSOCMDF_ENABLED;
break;
case SBCMDID_SELECTHISTPIDL:
rgCmds[i].cmdf = _fSuppressSelect || !_poctFrameTop ? 0:MSOCMDF_ENABLED;
break;
default:
rgCmds[i].cmdf = 0;
break;
}
}
}
else
{
return OLECMDERR_E_UNKNOWNGROUP;
}
if (pcmdtext)
{
pcmdtext->cmdtextf = MSOCMDTEXTF_NONE;
pcmdtext->cwActual = 0;
}
return NOERROR;
}
HRESULT CIEFrameAuto::_PidlFromUrlEtc(UINT uiCP, LPCWSTR pszUrl, LPWSTR pszLocation, LPITEMIDLIST* ppidl)
{
*ppidl = NULL; // assumes error
// ALGORITHM:
// - First, we call IEParseDisplayName to generate the pidl
// to the specified URL or file name.
// - if we have fragment (pszLocation) specified,
// we call IEILAppendFragment() to add the hidden fragment id
if(_pbs == NULL) //Make sure we have a IBrowserService.
return (S_FALSE);
HRESULT hr = _pbs->IEParseDisplayName(uiCP, pszUrl, ppidl);
// This is ugly, if it's a file path that failed to parse because
// it doesn't exist, we want to create a SimpleIDList so we display
// a res: navigation failed IFrame instead of the err dlg displayed
// in DisplayParseError() below.
if (FAILED(hr))
{
TCHAR szPath[MAX_PATH];
DWORD cchBuf = ARRAYSIZE(szPath);
// If it's a FILE URL, convert it to a path.
if (IsFileUrlW(pszUrl) && SUCCEEDED(PathCreateFromUrl(pszUrl, szPath, &cchBuf, 0)))
{
// That worked, we are done because our buffer is now full.
}
else
{
// We now need to copy to the buffer and we assume it's a path.
StrCpyN(szPath, pszUrl, ARRAYSIZE(szPath));
}
*ppidl = SHSimpleIDListFromPath(szPath);
if (*ppidl)
hr = S_OK;
}
if (SUCCEEDED(hr))
{
if (pszLocation && *pszLocation)
{
*ppidl = IEILAppendFragment(*ppidl, pszLocation);
hr = *ppidl ? S_OK : E_OUTOFMEMORY;
}
}
else
{
// NOTES: This behavior is new in IE4.0. We are adding
// this message box based on the request (bug-report)
// from Office guys. (SatoNa)
hr = _pbs->DisplayParseError(hr, pszUrl);
}
return hr;
}
HRESULT CIEFrameAuto::_NavIEShortcut(VARIANT *pvarIn, VARIANT *pvarargOut)
{
// need to validate verb and clsid
HRESULT hr = E_ACCESSDENIED;
READYSTATE ready;
BOOL fForceNavigate = pvarargOut ? ((VT_BOOL == pvarargOut->vt ) && (pvarargOut->boolVal)) : FALSE;
get_ReadyState(&ready);
ASSERT(pvarIn);
ASSERT(pvarIn->vt == VT_BSTR);
// we dont want to allow the exec to go through if this window
// is busy with something else. we should probably allow
// READYSTATE_COMPLETE and READYSTATE_UNINITIALIZED.
// if we use READYSTATE_UNINITIALIZED, we need to init the browser
// and make it visible and stuff like that. something to the
// check that IPersisteHistory->LoadHistory() does in shvocx.cpp.
// right now we will only allow COMPLETE.
TraceMsgW(DM_SHELLEXECOBJECT, "[%X] IEAuto_NavIEShortcut entered '%s' ready = %d", this, pvarIn->bstrVal, ready);
if (((ready == READYSTATE_COMPLETE || ready == READYSTATE_UNINITIALIZED) || (fForceNavigate))
&& S_OK == IUnknown_Exec(_psbTop, &CGID_Explorer, SBCMDID_ISIEMODEBROWSER, 0, NULL, NULL))
{
IPersistFile *ppf;
if (SUCCEEDED(CoCreateInstance(CLSID_InternetShortcut, NULL, CLSCTX_ALL, IID_IPersistFile, (void **)&ppf)))
{
if (SUCCEEDED(ppf->Load(pvarIn->bstrVal, STGM_READ)))
{
LPWSTR pszUrl = NULL;
TraceMsg(DM_SHELLEXECOBJECT, "[%X] IEAuto_NavIEShortcut shortcut inited with file", this);
IUniformResourceLocatorW *purl;
if (SUCCEEDED(ppf->QueryInterface(IID_IUniformResourceLocatorW, (void **)&purl)))
{
purl->GetURL(&pszUrl);
purl->Release();
}
if (pszUrl)
{
TraceMsgW(DM_SHELLEXECOBJECT, "[%X] IEAuto_NavIEShortcut found %s", this, pszUrl);
LPITEMIDLIST pidl;
IEParseDisplayNameW(CP_ACP, pszUrl, &pidl);
if (pidl)
{
ASSERT(NULL == _pwszShortcutPathPending);
if (_pwszShortcutPathPending)
LocalFree(_pwszShortcutPathPending);
_pwszShortcutPathPending = StrDupW(pvarIn->bstrVal);
hr = _BrowseObject(pidl, 0);
if (SUCCEEDED(hr))
{
if (ready == READYSTATE_UNINITIALIZED)
put_Visible(VARIANT_TRUE);
HWND hwnd = _GetHWND();
if (hwnd)
{
if (IsIconic(hwnd))
ShowWindow(hwnd, SW_RESTORE);
else
SetForegroundWindow(hwnd);
}
}
ILFree(pidl);
}
SHFree(pszUrl);
}
}
ppf->Release();
}
}
TraceMsg(DM_SHELLEXECOBJECT, "IEAuto_NavIEShortcut returns 0x%X", hr);
return hr;
}