#include "priv.h" #include "hlframe.h" #include "dochost.h" #include "bindcb.h" #include "iface.h" #include "resource.h" #include "idhidden.h" #include "shdocfl.h" const ITEMIDLIST s_idNull = { {0} }; extern HRESULT VariantClearLazy(VARIANTARG *pvarg); LPWSTR URLFindExtensionW(LPCWSTR pszURL, int * piLen); #define DM_CACHETRACE 0 #define DM_ZONECROSSING 0 #define NAVMSG3(psz, x, y) TraceMsg(0, "NAV::%s %x %x", psz, x, y) #define PAINTMSG(psz,x) TraceMsg(0, "TraceMsgPAINT::%s %x", psz, x) #define JMPMSG(psz, psz2) TraceMsg(0, "TraceMsgCDOV::%s %s", psz, psz2) #define JMPMSG2(psz, x) TraceMsg(0, "TraceMsgCDOV::%s %x", psz, x) #define DOFMSG(psz) TraceMsg(0, "TraceMsgDOF::%s", psz) #define DOFMSG2(psz, x) TraceMsg(0, "TraceMsgDOF::%s %x", psz, x) #define URLMSG(psz) TraceMsg(0, "TraceMsgDOF::%s", psz) #define URLMSG2(psz, x) TraceMsg(0, "TraceMsgDOF::%s %x", psz, x) #define URLMSG3(psz, x, y) TraceMsg(0, "TraceMsgDOF::%s %x %x", psz, x, y) #define BSCMSG(psz, i, j) TraceMsg(0, "TraceMsgBSC::%s %x %x", psz, i, j) #define BSCMSG3(psz, i, j, k) TraceMsg(0, "TraceMsgBSC::%s %x %x %x", psz, i, j, k) #define BSCMSG4(psz, i, j, k, l) TraceMsg(0, "TraceMsgBSC::%s %x %x %x %x", psz, i, j, k, l) #define BSCMSGS(psz, sz) TraceMsg(0, "TraceMsgBSC::%s %s", psz, sz) #define OIPSMSG(psz) TraceMsg(0, "TraceMsgOIPS::%s", psz) #define OIPSMSG3(psz, sz, p) TraceMsg(0, "TraceMsgOIPS::%s %s,%x", psz, sz,p) #define VIEWMSG(psz) TraceMsg(0, "CDOV::%s", psz) #define VIEWMSG2(psz,xx) TraceMsg(0, "CDOV::%s %x", psz,xx) #define CACHEMSG(psz, d) TraceMsg(0, "CDocObjectCtx::%s %d", psz, d) #define OPENMSG(psz) TraceMsg(0, "OPENING %s", psz) #define OPENMSG2(psz, x) TraceMsg(0, "OPENING %s %x", psz, x) #define HFRMMSG(psz) TraceMsg(0, "HFRM::%s", psz) #define HFRMMSG2(psz, x, y) TraceMsg(0, "HFRM::%s %x %x", psz, x, y) #define MNKMSG(psz, psz2) TraceMsg(0, "MNK::%s (%s)", psz, psz2) #define CHAINMSG(psz, x) TraceMsg(0, "CHAIN::%s %x", psz, x) #define SHVMSG(psz, x, y) TraceMsg(0, "SHV::%s %x %x", psz, x, y) #define HOMEMSG(psz, psz2, x) TraceMsg(0, "HOME::%s %s %x", psz, psz2, x) #define SAVEMSG(psz, x) TraceMsg(0, "SAVE::%s %x", psz, x) #define PERFMSG(psz, x) TraceMsg(TF_SHDPERF, "PERF::%s %d msec", psz, x) // this saves the view information for this shell view class typedef struct { UINT cbSize; BOOL fCoolbar:1; BOOL fToolbar:1; BOOL fStatusbar:1; } IEVIEWINFO; class CDocObjectView : /* Group 1 */ public IShellView2, public IDropTarget , public IViewObject, public IAdviseSink , public IOleCommandTarget , public IDocViewSite , public IPrivateOleObject , public IPersistFolder , public IServiceProvider { protected: CDocObjectHost* _pdoh; IDocHostObject* _pdho; UINT _cRef; IShellFolder *_psf; IShellBrowser* _psb; IOleCommandTarget* _pctShellBrowser; FOLDERSETTINGS _fs; LPITEMIDLIST _pidl; LPTSTR _pszLocation; UINT _uiCP; IShellView * _psvPrev; // Advisory connection IAdviseSink *_padvise; DWORD _advise_aspect; DWORD _advise_advf; BOOL _fInHistory : 1; BOOL _fSaveViewState : 1; BOOL _fIsGet : 1; BOOL _fCanCache : 1; BOOL _fCanCacheFetched : 1; BOOL _fPrevViewIsDocView : 1; BOOL _fSelfDragging : 1; // DocObject is the drag source SYSTEMTIME _stLastRefresh; HWND _hwndParent; UINT _uState; // DragContext DWORD _dwDragEffect; ~CDocObjectView(); void _RestoreViewSettings(); void _SaveViewState(); void _GetViewSettings(IEVIEWINFO* pievi); int _ShowControl(UINT idControl, int idCmd); void _CreateDocObjHost(IShellView * psvPrev); void _CompleteDocHostPassing(IShellView *psvPrev, HRESULT hres); BOOL _CanUseCache(); void _SetLastRefreshTime() { GetSystemTime(&_stLastRefresh); }; void _ConnectHostSink(); void _DisconnectHostSink(); public: CDocObjectView(LPCITEMIDLIST pidl, IShellFolder *psf); // *** IUnknown methods *** STDMETHODIMP QueryInterface(REFIID riid, void ** ppvObj); STDMETHODIMP_(ULONG) AddRef(void) ; STDMETHODIMP_(ULONG) Release(void); // *** IOleWindow methods *** STDMETHODIMP GetWindow(HWND * lphwnd); STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode); // *** IShellView methods *** STDMETHODIMP TranslateAccelerator(LPMSG lpmsg); STDMETHODIMP EnableModelessSV(BOOL fEnable); STDMETHODIMP UIActivate(UINT uState); STDMETHODIMP Refresh(); STDMETHODIMP CreateViewWindow(IShellView *lpPrevView, LPCFOLDERSETTINGS lpfs, IShellBrowser * psb, RECT * prcView, HWND *phWnd); STDMETHODIMP DestroyViewWindow(); STDMETHODIMP GetCurrentInfo(LPFOLDERSETTINGS lpfs); STDMETHODIMP AddPropertySheetPages(DWORD dwReserved, LPFNADDPROPSHEETPAGE lpfn, LPARAM lparam); STDMETHODIMP SaveViewState(); STDMETHODIMP SelectItem(LPCITEMIDLIST pidlItem, UINT uFlags); STDMETHODIMP GetItemObject(UINT uItem, REFIID riid, void **ppv); STDMETHODIMP GetView(SHELLVIEWID* pvid, ULONG uView) ; STDMETHODIMP CreateViewWindow2(LPSV2CVW2_PARAMS lpParams) ; STDMETHODIMP HandleRename(LPCITEMIDLIST pidl) ; STDMETHODIMP SelectAndPositionItem(LPCITEMIDLIST pidlItem, UINT uFlags, POINT *ppt) { return E_NOTIMPL; } // IViewObject STDMETHODIMP Draw(DWORD, LONG, void *, DVTARGETDEVICE *, HDC, HDC, const RECTL *, const RECTL *, BOOL (*)(ULONG_PTR), ULONG_PTR); STDMETHODIMP GetColorSet(DWORD, LONG, void *, DVTARGETDEVICE *, HDC, LOGPALETTE **); STDMETHODIMP Freeze(DWORD, LONG, void *, DWORD *); STDMETHODIMP Unfreeze(DWORD); STDMETHODIMP SetAdvise(DWORD, DWORD, IAdviseSink *); STDMETHODIMP GetAdvise(DWORD *, DWORD *, IAdviseSink **); // IAdviseSink STDMETHODIMP_(void) OnDataChange(FORMATETC *, STGMEDIUM *); STDMETHODIMP_(void) OnViewChange(DWORD dwAspect, LONG lindex); STDMETHODIMP_(void) OnRename(IMoniker *); STDMETHODIMP_(void) OnSave(); STDMETHODIMP_(void) OnClose(); // IOleCommandTarget STDMETHODIMP QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext); STDMETHODIMP Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut); // IDropTarget STDMETHODIMP DragEnter( IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); STDMETHODIMP DragOver( DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); STDMETHODIMP DragLeave(void); STDMETHODIMP Drop( IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); // IDocViewSite STDMETHODIMP OnSetTitle(VARIANTARG *pvTitle); // IPrivateOleObject STDMETHODIMP SetExtent( DWORD dwDrawAspect, SIZEL *psizel); STDMETHODIMP GetExtent( DWORD dwDrawAspect, SIZEL *psizel); // IPersist methods STDMETHODIMP GetClassID(CLSID *pclsid); // IPersistFolder methods STDMETHODIMP Initialize(LPCITEMIDLIST pidl); // IServiceProvider methods STDMETHODIMP QueryService(REFGUID guidService, REFIID riid, void ** ppvObj); }; //-------------------------------------------------------------------------- // Detecting a memory leak //-------------------------------------------------------------------------- CDocObjectView::~CDocObjectView() { // just in case DestroyViewWindow(); if (_pidl) { ILFree(_pidl); _pidl = NULL; } ATOMICRELEASE(_psf); if (_pszLocation) { LocalFree(_pszLocation); _pszLocation = NULL; } ATOMICRELEASE(_padvise); ATOMICRELEASE(_psvPrev); TraceMsg(TF_SHDLIFE, "dtor CDocObjectView(%x) being destructed", this); } CDocObjectView::CDocObjectView(LPCITEMIDLIST pidl, IShellFolder* psf) : _psf(psf), _cRef(1) { TraceMsg(TF_SHDLIFE, "ctor CDocObjectView(%x) being constructed", this); _dwDragEffect = DROPEFFECT_NONE; if (pidl) { _pidl = ILClone(pidl); if (_pidl) { #ifndef UNIX WCHAR wszPath[MAX_URL_STRING]; #else WCHAR wszPath[MAX_URL_STRING] = TEXT(""); #endif if(IEILGetFragment(_pidl, wszPath, SIZECHARS(wszPath))) { _pszLocation = StrDup(wszPath); } _uiCP = IEILGetCP(_pidl); } } ASSERT(psf); if (_psf) { _psf->AddRef(); } } HRESULT CDocObjectView_Create(IShellView** ppvOut, IShellFolder * psf, LPCITEMIDLIST pidl) { *ppvOut = new CDocObjectView(pidl, psf); return (*ppvOut) ? S_OK : E_OUTOFMEMORY; } HRESULT CDocObjectView::GetWindow(HWND * lphwnd) { *lphwnd = NULL; if (_pdoh) return _pdoh->GetWindow(lphwnd); return S_OK; } HRESULT CDocObjectView::ContextSensitiveHelp(BOOL fEnterMode) { // NOTES: This is optional return E_NOTIMPL; // As specified in Kraig's document (optional) } // IShellView::TranslateAccelerator // From Browser -> DocView -> DocObject HRESULT CDocObjectView::TranslateAccelerator(LPMSG lpmsg) { HRESULT hres = S_FALSE; if (_pdoh) hres = _pdoh->_xao.TranslateAccelerator(lpmsg); if (hres == S_FALSE && lpmsg->message == WM_KEYDOWN) { HWND hwndFocus = GetFocus(); HWND hwndView = NULL; if(_pdoh) //WARNING ZEKEL i found this NULL _pdoh->GetWindow(&hwndView); if (hwndView && IsChildOrSelf(hwndView, hwndFocus) == S_OK) { switch (lpmsg->wParam) { case VK_BACK: TranslateMessage(lpmsg); DispatchMessage(lpmsg); hres = NOERROR; break; } } } return hres; } // IShellView::EnableModelessSV // From Browser -> DocView -> DocObject HRESULT CDocObjectView::EnableModelessSV(BOOL fEnable) { HRESULT hres = S_OK; // We have no modeless window to be enabled/disabled TraceMsg(0, "sdv TR - ::EnableModelessSV(%d) called", fEnable); if (_pdoh) { hres = _pdoh->_xao.EnableModeless(fEnable); TraceMsg(0, "sdv TR - _piact->EnableModeless returned %x", hres); } return hres; } HRESULT CDocObjectView::UIActivate(UINT uState) { HRESULT hres = E_FAIL; if (_pdoh) { hres = _pdoh->UIActivate(uState, _fPrevViewIsDocView); } _uState = uState; return hres; } HRESULT CDocObjectView::Refresh() { if (_pdoh) { VARIANT v = {0}; v.vt = VT_I4; v.lVal = OLECMDIDF_REFRESH_NO_CACHE; // send this to exec so that it will update last refresh time. // all refresh to dochost should go through our own exec return Exec(NULL, OLECMDID_REFRESH, OLECMDEXECOPT_PROMPTUSER, &v, NULL); } return S_OK; } // Return: // S_OK: It is a folder shortcut and ppidlTarget is filled out if provided. // S_FALSE: It is not a folder shortcut and ppidlTarget is filled with NULL if provided. // FAILED: An error occured trying to detect. // // Don't use this function if there is a better way. Look into using // IBrowserFrameOptions. -BryanSt HRESULT IsFolderShortcutPidl(IN LPCITEMIDLIST pidl) { IShellFolder * psf = NULL; HRESULT hr = IEBindToObject(pidl, &psf); if (SUCCEEDED(hr)) { IShellLinkA * psl; hr = psf->QueryInterface(IID_PPV_ARG(IShellLinkA, &psl)); if (SUCCEEDED(hr)) { hr = S_OK; psl->Release(); } else hr = S_FALSE; // It's not a folder shortcut. psf->Release(); } return hr; } // WARNING: This function explicitely creates URLMON monikers // because that's what the caller needs. Some NSEs will // support IShellFolder::BindToObject(IMoniker) // but this function doesn't use that logic intentionally. // STDAPI _URLMONMonikerFromPidl(LPCITEMIDLIST pidl, IMoniker** ppmk, BOOL* pfFileProtocol) { TCHAR szPath[MAX_URL_STRING]; HRESULT hres = E_UNEXPECTED; *ppmk = NULL; *pfFileProtocol = FALSE; MNKMSG(TEXT("_URLMONMonikerFromPidl"), TEXT("called")); AssertMsg((S_OK != IsFolderShortcutPidl(pidl)), TEXT("We shouldn't get Folder Shortcuts here because we don't deref them to get the target. And we should never need to.")); // Is this a child of the "Internet Explorer" name space? if (!IsURLChild(pidl, TRUE)) { // No, so we want to get the display name to use to // create the moniker. We will try to turn it into // an URL if it isn't already an URL. // NOTE: We don't try IEBindToObject(pidl, IID_IMoniker) // because the caller requires that this // IMoniker come from URLMON. HRESULT hrTemp = SHGetPathFromIDList(pidl, szPath); AssertMsg(SUCCEEDED(hrTemp), TEXT("_URLMONMonikerFromPidl() failed SHGetPathFromIDList() which is really bad because we probably won't be able to create a moniker from it. We will try to create a URLMON moniker below.")); if (SUCCEEDED(hrTemp)) { // this should never be a fully qualified URL DWORD cchPath = ARRAYSIZE(szPath); ASSERT(URL_SCHEME_INVALID == GetUrlScheme(szPath)); if(SUCCEEDED(hres = UrlCreateFromPath(szPath, szPath, &cchPath, 0))) { MNKMSG(TEXT("_URLMONMonikerFromPidl Creating File Moniker"), szPath); } *pfFileProtocol = TRUE; } } else { // Yes so we are guaranteed this is from the "Internet Explorer" // name space so remove Fragment hidden itemID. // We do this because we navigate to the fragment later, // after the page is downloaded. This is also needed // for delegates of the IE name space. (FTP is one example of // a delegate). ASSERT(pidl); ASSERT(!ILIsEmpty(_ILNext(pidl))); // Make sure it's not the start page URL. LPITEMIDLIST pidlClone = ILClone(pidl); if (pidlClone) { // we dont want to pass the fragment into URLMON // so we pull it off before calling into GDN. // Note that pidlClone needs to be the pidlTarget // of folder shortcuts or the dislpay name will // be the file system path of the folder shortcut // if at the top level. ILRemoveHiddenID(pidlClone, IDLHID_URLFRAGMENT); IEGetDisplayName(pidlClone, szPath, SHGDN_FORPARSING); hres = S_OK; ILFree(pidlClone); } } if (SUCCEEDED(hres)) { if (szPath[0]) { hres = MonikerFromString(szPath, ppmk); } else { ASSERT(FALSE); hres = E_UNEXPECTED; } } return hres; } HRESULT CDocObjectView::HandleRename(LPCITEMIDLIST pidl) { return E_NOTIMPL; } HRESULT CDocObjectView::CreateViewWindow(IShellView *psvPrev, LPCFOLDERSETTINGS lpfs, IShellBrowser * psb, RECT * prcView, HWND *phWnd) { SV2CVW2_PARAMS cParams; cParams.cbSize = SIZEOF(SV2CVW2_PARAMS); cParams.psvPrev = psvPrev; cParams.pfs = lpfs; cParams.psbOwner = psb; cParams.prcView = prcView; cParams.pvid = NULL; HRESULT hres = CreateViewWindow2(&cParams); *phWnd = cParams.hwndView; IOleWindow *pOleWindow; // need top level frame available for D&D HRESULT hr = IUnknown_QueryService(_psb, SID_STopLevelBrowser, IID_PPV_ARG(IOleWindow, &pOleWindow)); if(SUCCEEDED(hr)) { ASSERT(pOleWindow); pOleWindow->GetWindow(&_hwndParent); pOleWindow->Release(); } return(hres); } void CDocObjectView::_CompleteDocHostPassing(IShellView * psvPrev, HRESULT hresBinding) { BOOL fPassedSV = FALSE; // If there was a previous shell view, see if it was our class. // if (psvPrev) { CDocObjectView * pdovPrev; HRESULT hres = psvPrev->QueryInterface(CLSID_CDocObjectView, (void **)&pdovPrev); if (SUCCEEDED(hres)) { // Previous shell view is also an instance of CDocObjectView, // Remember that for optimization later. // _fPrevViewIsDocView = TRUE; // it was, and we have the same doc host as they do. // we've succeeded in taking over, so null them out if we // succeeeded in our bind. null ourselves out if we failed // if (pdovPrev->_pdoh == _pdoh) { if (SUCCEEDED(hresBinding)) { pdovPrev->_DisconnectHostSink(); // just in case _ConnectHostSink(); // just in case ATOMICRELEASET(pdovPrev->_pdoh, CDocObjectHost); } else { _DisconnectHostSink(); // unhook pdovPrev->_ConnectHostSink(); // kick other guy ATOMICRELEASET(_pdoh, CDocObjectHost); } fPassedSV = TRUE; } pdovPrev->Release(); } } if (!fPassedSV) { if (FAILED(hresBinding)) { DestroyViewWindow(); } } } BOOL CDocObjectView::_CanUseCache() { // NOTE: this function is more like _DontHaveToHitTheNet() // the name is legacy from the objectcache. if (!_fCanCacheFetched) { _fCanCache = TRUE; _fCanCacheFetched = TRUE; _fIsGet = TRUE; IServiceProvider *psp; _psb->QueryInterface(IID_PPV_ARG(IServiceProvider, &psp)); if (psp) { IBindStatusCallback *pbsc; if (SUCCEEDED(GetTopLevelBindStatusCallback(psp, &pbsc))) { BINDINFO binfo; ZeroMemory(&binfo, sizeof(BINDINFO)); binfo.cbSize = sizeof(BINDINFO); DWORD grfBINDF = BINDF_ASYNCHRONOUS; HRESULT hr = pbsc->GetBindInfo(&grfBINDF, &binfo); if (SUCCEEDED(hr)) { if (grfBINDF & (BINDF_GETNEWESTVERSION | BINDF_RESYNCHRONIZE)) { _fCanCache = FALSE; } _fIsGet = (binfo.dwBindVerb == BINDVERB_GET); ReleaseBindInfo(&binfo); } pbsc->Release(); } // I believe that failing to get the Bindstatuscallback should // not happen since we no longer use the object cache. // and if it does, then we no that we didnt have to repost // or something similar psp->Release(); } } return _fCanCache; } void CDocObjectView::_ConnectHostSink() { if (_pdoh) { IAdviseSink *pSink; if (FAILED(_pdoh->GetAdvise(NULL, NULL, &pSink))) pSink = NULL; if (pSink != (IAdviseSink *)this) _pdoh->SetAdvise(DVASPECT_CONTENT, ADVF_PRIMEFIRST, this); if (pSink) pSink->Release(); } } void CDocObjectView::_DisconnectHostSink() { IAdviseSink *pSink; // paranoia: only blow away the advise sink if it is still us if (_pdoh && SUCCEEDED(_pdoh->GetAdvise(NULL, NULL, &pSink)) && pSink) { if (pSink == (IAdviseSink *)this) _pdoh->SetAdvise(0, 0, NULL); pSink->Release(); } OnViewChange(DVASPECT_CONTENT, -1); } // // This function either (1) Create a new CDocObjectHost or (2) reuse it from // the previous view. If also returns the DisplayName of previous moniker. // void CDocObjectView::_CreateDocObjHost(IShellView * psvPrev) { BOOL fWindowOpen = FALSE; // if there was a previous shell view, see if it was our class. if (psvPrev) { CDocObjectView * pdovPrev = NULL; HRESULT hres = psvPrev->QueryInterface(CLSID_CDocObjectView, (void **)&pdovPrev); if (SUCCEEDED(hres)) { CDocObjectHost * pPrevDOH = pdovPrev->_pdoh; ASSERT(_psb); ASSERT(_psb == pdovPrev->_psb); // find out if we should be saving the view state when we close // if the one we're coming from says yes, then we'll take over that // job instead of them. // _fSaveViewState = pdovPrev->_fSaveViewState; pdovPrev->_fSaveViewState = FALSE; // // if this is a local anchor navigation, // we treat it substantially differently. // we can reuse the DOH and the OLE Object that // it holds. - zekel - 31-JUL-97 // // WARNING: we should not reuse these objects for any other // reason than a local anchor (fragment) navigation. // we used to be a lot more lenient about reusing the DOH // but i think this was mostly because of the old object cache. // // we check for equal pidls so that we know we are on // the same URL. we require that _pszLocation be set. // this is for Netscape compat. this means that anytime // we get a navigate to ourself it will refresh completely // if there was no fragment. we also check to make sure that // the binding does not require us to hit the net for // this request. // // 08-11-1999 (scotrobe): We now reuse the DOH if the // hosted document has indicated that it knows how to // navigate on its own. // if (_pidl && pdovPrev->_pidl && pPrevDOH && (pPrevDOH->_fDocCanNavigate || ( _pszLocation && IEILIsEqual(_pidl, pdovPrev->_pidl, TRUE) && _CanUseCache()))) { IBrowserService *pbs; if (SUCCEEDED(_psb->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs)))) { DWORD dwFlags = 0; // If the document doesn't know how to navigate, then this may // mean that this navigation was delegated from the document. // In that case, if we have gotten this far, that means that // _pszLocation is set. If it is and this is a delegated navigation // (i.e., the navigation wasn't due to a link in a non-html document) // we must create a new document. // if (!pPrevDOH->_fDocCanNavigate) { pbs->GetFlags(&dwFlags); } if (!(dwFlags & BSF_DELEGATEDNAVIGATION)) { // // if SetHistoryObject() fails, then that means there is already // an object there for us to use. this means that we should // not treat this as a local anchor navigation. and // we shouldnt reuse the DOH even though the pidl is identical // // TRUE is passed to SetHistoryObject even if this is not a // local anchor. In the case that this is not a local anchor, // the document (Trident) handles the navigation. Therefore, // the document will take care of updating the travel log and // the fIsLocalAnchor flag is ignored. // if (SUCCEEDED(pbs->SetHistoryObject(pPrevDOH->_pole, !pPrevDOH->_fDocCanNavigate ? TRUE : FALSE))) { TraceMsg(TF_TRAVELLOG, "CDOV::CreateDocObjHost reusing current DOH on local anchor navigate"); // // we cant update in ActivateNow because at that point // we have already switched the dochost over to the new // (reused) view. so we need to update the entry now // // The fact that the hosted document can navigate on its own, // implies that it will take care of updating the travel log. // if (!pPrevDOH->_fDocCanNavigate) { ITravelLog * ptl; pbs->GetTravelLog(&ptl); if (ptl) { ptl->UpdateEntry(pbs, TRUE); ptl->Release(); } } pdovPrev->_DisconnectHostSink(); // we will connect below // same target! pass the docobj host _pdoh = pPrevDOH; _pdoh->AddRef(); if (_pdoh->_fDocCanNavigate) { _pdoh->OnInitialUpdate(); } if ((_pdoh->_dwAppHack & BROWSERFLAG_SUPPORTTOP) && !_pszLocation) { // if there's no location for us to go to, and // we're traversing in the same document, // assume top. // _pszLocation = StrDup(TEXT("#top")); _uiCP = CP_ACP; } } pbs->Release(); } } } // In the case where we are opening a non-html mime type // in a new window, we need to pass the _fWindowOpen // flag from the previous docobject host to the new // docobject host. This is needed so that if we are opening // a file outside of IE in a new window, we'll know to close // the newly created IE afterwards. // The problem is that there isn't really a good place to // clear this flag since it has to remain set from one // instance of the docobject host to the next. This causes // us to get into the situation where we'll close the browser // window if we click on a link to a file that opens outside // of IE after opening a new window for an html file. // The bottom line is that we only need to pass this flag to // the new docobject host if we opening an non-html mime type // in a new window. // if (!_pdoh && pPrevDOH && pPrevDOH->_fDelegatedNavigation) { fWindowOpen = pPrevDOH->_fWindowOpen; } // // FEATURE: We should take care of _pibscNC as well // to 'chain' the IBindStatusCallback. // pdovPrev->Release(); } } // if we didn't pass the docobj host, create a new one and // pass the doc context if (!_pdoh) { ASSERT(_psb); _pdoh = new CDocObjectHost(fWindowOpen); // Reset host navigation flag in the browser // IUnknown_Exec(_psb, &CGID_DocHostCmdPriv, DOCHOST_DOCCANNAVIGATE, 0, NULL, NULL); } if (_pdoh) { _ConnectHostSink(); } } extern HRESULT TargetQueryService(IUnknown *punk, REFIID riid, void **ppvObj); HRESULT CDocObjectView::CreateViewWindow2(LPSV2CVW2_PARAMS lpParams) { HRESULT hres = S_OK; IShellView * psvPrev = lpParams->psvPrev; LPCFOLDERSETTINGS lpfs = lpParams->pfs; IShellBrowser * psb = lpParams->psbOwner; RECT * prcView = lpParams->prcView; HWND UNALIGNED * phWnd = &lpParams->hwndView; if (_pdoh || !_pidl) { *phWnd = NULL; ASSERT(0); return E_UNEXPECTED; } _fs = *lpfs; ASSERT(_psb==NULL); _psb = psb; psb->AddRef(); ASSERT(_pctShellBrowser==NULL); _psb->QueryInterface(IID_IOleCommandTarget, (void **)&_pctShellBrowser); //if somebody that is not a ShellBrowser (like the FileOpenBrowser) // tries to use us, we want to block them. we will fault later on // if we dont have the right stuff. if (!_pctShellBrowser) return E_UNEXPECTED; // prime the cache bit. this needs to be done while we're *the* guy navigating. // otherwise, if we ask later when there's a different pending navigation, // we'll get his info _CanUseCache(); _SetLastRefreshTime(); // Either create a new CDocObjectHost or reuse it from the previous view // and set it in _pdoh. _CreateDocObjHost(psvPrev); if (!_pdoh || !_pdoh->InitHostWindow(this, psb, prcView)) { ATOMICRELEASE(_psb); _pctShellBrowser->Release(); _pctShellBrowser = NULL; return E_OUTOFMEMORY; } _pdoh->GetWindow(phWnd); ASSERT(NULL == _pdho); _pdoh->QueryInterface(IID_IDocHostObject, (void **)&_pdho); IMoniker* pmk = NULL; BOOL fFileProtocol; hres = ::_URLMONMonikerFromPidl(_pidl, &pmk, &fFileProtocol); if (SUCCEEDED(hres) && EVAL(pmk)) { hres = _pdoh->SetTarget(pmk, _uiCP, _pszLocation, _pidl, psvPrev, fFileProtocol); _psvPrev = psvPrev; if (_psvPrev) _psvPrev->AddRef(); #ifdef NON_NATIVE_FRAMES _CompleteDocHostPassing(psvPrev, hres); #endif pmk->Release(); } return hres; } void CDocObjectView::_GetViewSettings(IEVIEWINFO* pievi) { DWORD dwType, dwSize; // REVIEW: Currently, we have on setting for all docobj class views // (everything hosted by shdocvw). we may want to subclassify them by clsid // of the docobj or maybe special case mshtml... dwSize = sizeof(IEVIEWINFO); if (SHGetValueGoodBoot(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Internet Explorer\\Main"), TEXT("ViewSettings"), &dwType, (PBYTE)pievi, &dwSize) == ERROR_SUCCESS) { if (pievi->cbSize != sizeof(IEVIEWINFO)) { goto DefaultInfo; } } else { DefaultInfo: // can't count on 0 init because registry could have read stuff, but // of the wrong size (corruption) pievi->fToolbar = FALSE; pievi->fCoolbar = TRUE; pievi->fStatusbar = TRUE; } } void CDocObjectView::_SaveViewState() { IEVIEWINFO ievi; int id; // First ask up if it is ok for us to save view state. If we get the return value of // S_FALSE bail as we were told no. if (_pctShellBrowser && (_pctShellBrowser->Exec(&CGID_Explorer, SBCMDID_MAYSAVEVIEWSTATE, 0, NULL, NULL) == S_FALSE)) return; // first load to preserve things we're not going to set _GetViewSettings(&ievi); ievi.cbSize = sizeof(ievi); id = _ShowControl(FCW_STATUS, SBSC_QUERY); // bail if it's not supported if (id == -1) return; ievi.fStatusbar = (id == SBSC_SHOW); id = _ShowControl(FCW_TOOLBAR, SBSC_QUERY); if (id != -1) { // this is allowed to fail if toolbar isn't supported (ie30 case) ievi.fToolbar = (id == SBSC_SHOW); } id = _ShowControl(FCW_INTERNETBAR, SBSC_QUERY); if (id != -1) { // this is allowed to fail if coolbar isn't supported ievi.fCoolbar = (id == SBSC_SHOW); } SHSetValue(HKEY_CURRENT_USER, TEXT("Software\\Microsoft\\Internet Explorer\\Main"), TEXT("ViewSettings"), REG_BINARY, (const BYTE *)&ievi, sizeof(ievi)); } int CDocObjectView::_ShowControl(UINT idControl, int idCmd) { VARIANTARG var; VariantInit(&var); var.vt = VT_I4; var.lVal = MAKELONG(idControl, idCmd); if (_pctShellBrowser && SUCCEEDED(_pctShellBrowser->Exec(&CGID_Explorer, SBCMDID_SHOWCONTROL, OLECMDEXECOPT_DODEFAULT, &var, &var))) return var.lVal; return -1; } HRESULT CDocObjectView::DestroyViewWindow() { ATOMICRELEASE(_pdho); if (_pdoh) { BOOL fDestroyHost = TRUE; if (_psb && _pdoh->_pwb) { DWORD dwFlags; _pdoh->_pwb->GetFlags(&dwFlags); if (dwFlags & BSF_HTMLNAVCANCELED) { IShellView * psvCur; HRESULT hr = _psb->QueryActiveShellView(&psvCur); if (S_OK == hr) { CDocObjectView * pdovCur; hr = psvCur->QueryInterface(CLSID_CDocObjectView, (void**)&pdovCur); if (S_OK == hr) { ASSERT(this != pdovCur); if (_pdoh == pdovCur->_pdoh) { fDestroyHost = FALSE; } pdovCur->Release(); } psvCur->Release(); } } } if (fDestroyHost) { TraceMsg(DM_WARNING, "CDocObjectView::DestroyViewWindow(): Destroying Host Window"); _DisconnectHostSink(); if (_fSaveViewState) _SaveViewState(); _pdoh->DestroyHostWindow(); } ATOMICRELEASET(_pdoh, CDocObjectHost); } ATOMICRELEASE(_pctShellBrowser); // Note that we should release _psb at the very end. ATOMICRELEASE(_psb); return S_OK; } HRESULT CDocObjectView::GetCurrentInfo(LPFOLDERSETTINGS lpfs) { *lpfs = _fs; return S_OK; } HRESULT CDocObjectView::AddPropertySheetPages(DWORD dwReserved, LPFNADDPROPSHEETPAGE lpfn, LPARAM lParam) { if (_pdoh) return _pdoh->AddPages(lpfn, lParam); return E_FAIL; } HRESULT CDocObjectView::SaveViewState() { // No viewsate to be saved return S_OK; } HRESULT CDocObjectView::SelectItem(LPCITEMIDLIST pidlItem, UINT uFlags) { // No item return E_FAIL; } // // IShellView::GetItemObject // // For this IShellView object, the only valid uItem is SVGIO_BACKGROUND, // which allows the browser to access an interface pointer to the // currently active document object. // // Notes: // The browser should be aware that IShellView::CreateViewWindow might be // asynchronous. This method will fail with E_FAIL if the document is not // instanciated yet. // HRESULT CDocObjectView::GetItemObject(UINT uItem, REFIID riid, void **ppv) { HRESULT hres = E_INVALIDARG; *ppv = NULL; // assumes error switch(uItem) { case SVGIO_BACKGROUND: if (_pdoh) { if (_pdoh->_pole) { hres = _pdoh->_pole->QueryInterface(riid, ppv); break; } else if (_pdoh->_punkPending) { hres = _pdoh->_punkPending->QueryInterface(riid, ppv); break; } } // fall through on the else's default: hres = E_FAIL; break; } return hres; } HRESULT CDocObjectView::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect) { _fSelfDragging = FALSE; // // Check if this is a self-dragging or not. // if (_pdoh && _pdoh->_pmsot) { VARIANT var = { 0 }; HRESULT hresT = _pdoh->_pmsot->Exec( &CGID_ShellDocView, SHDVID_ISDRAGSOURCE, 0, NULL, &var); if (SUCCEEDED(hresT) && var.vt==VT_I4 && var.lVal) { _fSelfDragging = TRUE; } VariantClearLazy(&var); } ASSERT(pdtobj); _DragEnter(_hwndParent, ptl, pdtobj); _dwDragEffect = CommonDragEnter(pdtobj, grfKeyState, ptl); return DragOver(grfKeyState, ptl, pdwEffect); } HRESULT CDocObjectView::DragOver(DWORD grfKeyState, POINTL ptl, DWORD *pdwEffect) { *pdwEffect &= _dwDragEffect; _DragMove(_hwndParent, ptl); if (_fSelfDragging && _pdoh && _pdoh->_hwnd) { RECT rc; GetClientRect(_pdoh->_hwnd, &rc); POINT ptMap = { ptl.x, ptl.y }; MapWindowPoints(HWND_DESKTOP, _pdoh->_hwnd, &ptMap, 1); if (PtInRect(&rc, ptMap)) { *pdwEffect = DROPEFFECT_NONE; } } return S_OK; } HRESULT CDocObjectView::DragLeave(void) { DAD_DragLeave(); return S_OK; } HRESULT CDocObjectView::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect) { LPITEMIDLIST pidlTarget; HRESULT hr = SHPidlFromDataObject(pdtobj, &pidlTarget, NULL, 0); if (SUCCEEDED(hr)) { ASSERT(pidlTarget); if((!ILIsWeb(pidlTarget) && _pdoh && SHIsRestricted2W(_pdoh->_hwnd, REST_NOFILEURL, NULL, 0)) || (_pdoh && !IEIsLinkSafe(_pdoh->_hwnd, pidlTarget, ILS_NAVIGATE))) { ILFree(pidlTarget); hr = E_ACCESSDENIED; } else { DWORD flags = GetKeyState(VK_CONTROL) < 0 ? (SBSP_NEWBROWSER | SBSP_ABSOLUTE) : (SBSP_SAMEBROWSER | SBSP_ABSOLUTE); hr = _psb->BrowseObject(pidlTarget, flags); HFRMMSG2(TEXT("::Drop _psb->BrowseObject returned"), hr, 0); ILFree(pidlTarget); } } if (SUCCEEDED(hr)) { *pdwEffect &= _dwDragEffect; } DAD_DragLeave(); return hr; } ULONG CDocObjectView::AddRef() { _cRef++; TraceMsg(TF_SHDREF, "CDocObjectView(%x)::AddRef called new _cRef=%d", this, _cRef); return _cRef; } ULONG CDocObjectView::Release() { _cRef--; TraceMsg(TF_SHDREF, "CDocObjectView(%x)::Release called new _cRef=%d", this, _cRef); if (_cRef > 0) return _cRef; delete this; return 0; } HRESULT CDocObjectView::GetView(SHELLVIEWID* pvid, ULONG uView) { return E_NOTIMPL; } #ifdef DEBUG #define _AddRef(psz) { ++_cRef; TraceMsg(TF_SHDREF, "CDocObjectView(%x)::QI(%s) is AddRefing _cRef=%d", this, psz, _cRef); } #else #define _AddRef(psz) ++_cRef #endif HRESULT CDocObjectView::QueryInterface(REFIID riid, void ** ppvObj) { HRESULT hres; static const QITAB qit[] = { QITABENT(CDocObjectView, IShellView2), QITABENTMULTI(CDocObjectView, IShellView, IShellView2), QITABENTMULTI(CDocObjectView, IOleWindow, IShellView2), QITABENT(CDocObjectView, IDropTarget), QITABENT(CDocObjectView, IViewObject), QITABENT(CDocObjectView, IAdviseSink), QITABENT(CDocObjectView, IOleCommandTarget), QITABENT(CDocObjectView, IDocViewSite), QITABENT(CDocObjectView, IPrivateOleObject ), QITABENT(CDocObjectView, IPersistFolder), QITABENTMULTI(CDocObjectView, IPersist, IPersistFolder), QITABENT(CDocObjectView, IServiceProvider), { 0 }, }; hres = QISearch(this, qit, riid, ppvObj); if (S_OK != hres) { if (IsEqualIID(riid, CLSID_CDocObjectView)) { *ppvObj = (void*)this; _AddRef(TEXT("CLSID_CDocObjectView")); return S_OK; } } return hres; } /// ***** IViewObject ****** HRESULT CDocObjectView::GetColorSet(DWORD dwAspect, LONG lindex, void *pvAspect, DVTARGETDEVICE *ptd, HDC hicTargetDev, LOGPALETTE **ppColorSet) { if (_pdoh) { return _pdoh->GetColorSet(dwAspect, lindex, pvAspect, ptd, hicTargetDev, ppColorSet); } if (ppColorSet) *ppColorSet = NULL; return S_FALSE; } HRESULT CDocObjectView::Freeze(DWORD, LONG, void *, DWORD *pdwFreeze) { return E_NOTIMPL; } HRESULT CDocObjectView::Unfreeze(DWORD) { return E_NOTIMPL; } HRESULT CDocObjectView::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(dwAspect, -1); } else _advise_aspect = _advise_advf = 0; return S_OK; } HRESULT CDocObjectView::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; } HRESULT CDocObjectView::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 (_pdoh) { return _pdoh->Draw(dwDrawAspect, lindex, pvAspect, ptd, hicTargetDev, hdcDraw, lprcBounds, lprcWBounds, pfnContinue, dwContinue); } return OLE_E_BLANK; } // IAdviseSink void CDocObjectView::OnDataChange(FORMATETC *, STGMEDIUM *) { } void CDocObjectView::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 CDocObjectView::OnRename(IMoniker *) { } void CDocObjectView::OnSave() { } void CDocObjectView::OnClose() { // // the doc object host went away so tell anybody above what happened // OnViewChange(_advise_aspect, -1); } HRESULT CDocObjectView::QueryStatus(const GUID *pguidCmdGroup, ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext) { HRESULT hres = OLECMDERR_E_UNKNOWNGROUP; if (_pdho && _pdoh) hres = _pdho->QueryStatusDown(pguidCmdGroup, cCmds, rgCmds, pcmdtext); return hres; } HRESULT CDocObjectView::Exec(const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut) { HRESULT hres = OLECMDERR_E_UNKNOWNGROUP; if (!pguidCmdGroup) { switch (nCmdID) { case OLECMDID_REFRESH: _SetLastRefreshTime(); break; default: break; } } else if (IsEqualGUID(CGID_ShellDocView, *pguidCmdGroup)) { switch(nCmdID) { case SHDVID_UPDATEDOCHOSTSTATE: if (_pdoh) { DOCHOSTUPDATEDATA * pdhud; ASSERT(pvarargIn && pvarargIn->vt == VT_PTR); pdhud = (DOCHOSTUPDATEDATA *) V_BYREF(pvarargIn); return _pdoh->_UpdateState(pdhud->_pidl, pdhud->_fIsErrorUrl); } return S_OK; case SHDVID_COMPLETEDOCHOSTPASSING: _CompleteDocHostPassing(_psvPrev, S_OK); ATOMICRELEASE(_psvPrev); return S_OK; case SHDVID_NAVSTART: if (_pdoh) { _pdoh->_Init(); } return S_OK; default: break; } } // only forward on if we aren't 'stolen'. // FEATURE ie4: clean this up to steal _pdho along w/ _pdoh. if (_pdho && _pdoh) hres = _pdho->ExecDown(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut); // REVIEW: if _pdoh->ExecDown fails && pguidCmdGroup==NULL && nCmdID is // OLECMDID_STOP or OLECMDID_REFRESH, then we are lying // by returning a failure error code. return hres; } HRESULT CDocObjectView::OnSetTitle(VARIANTARG *pvTitle) { return E_NOTIMPL; } HRESULT CDocObjectView::SetExtent( DWORD dwDrawAspect, SIZEL *psizel) { if ( _pdoh && _pdoh->GetOleObject() ) { return _pdoh->GetOleObject()->SetExtent( dwDrawAspect, psizel ); } return E_NOTIMPL; } HRESULT CDocObjectView::GetExtent( DWORD dwDrawAspect, SIZEL *psizel) { if ( _pdoh && _pdoh->GetOleObject() ) { return _pdoh->GetOleObject()->GetExtent( dwDrawAspect, psizel ); } return E_NOTIMPL; } HRESULT CDocObjectView::GetClassID(CLSID *pclsid) { if (pclsid) { *pclsid = CLSID_CDocObjectView; return S_OK; } return E_INVALIDARG; } HRESULT CDocObjectView::Initialize(LPCITEMIDLIST pidl) { HRESULT hres = E_OUTOFMEMORY; LPITEMIDLIST pidlClone = ILClone(pidl); if (pidlClone) { IShellFolder* psf; if (SUCCEEDED(IEBindToObject(_pidl, &psf))) { ILFree(_pidl); ATOMICRELEASE(_psf); _pidl = pidlClone; _psf = psf; hres = S_OK; } else { ILFree(pidlClone); } } return hres; } HRESULT CDocObjectView::QueryService(REFGUID guidService, REFIID riid, void **ppvObj) { if( _pdoh && IsEqualGUID(guidService, IID_IElementNamespaceTable) ) { return _pdoh->QueryService( guidService, riid, ppvObj); } else return E_FAIL; }