#include "priv.h" #include "dspsprt.h" #include #include "iface.h" #include "resource.h" #include #include "shdocfl.h" class CTravelLog; class CEnumEntry : public IEnumTravelLogEntry { public: // *** IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); // *** IEnumTravelLogEntry specific methods STDMETHODIMP Next(ULONG cElt, ITravelLogEntry **rgElt, ULONG *pcEltFetched); STDMETHODIMP Skip(ULONG cElt); STDMETHODIMP Reset(); STDMETHODIMP Clone(IEnumTravelLogEntry **ppEnum); CEnumEntry(); void Init(CTravelLog *ptl, IUnknown *punk, DWORD dwOffset, DWORD dwFlags); void SetBase(); protected: ~CEnumEntry(); LONG _cRef; DWORD _dwFlags; DWORD _dwOffset; LONG _lStart; CTravelLog *_ptl; IUnknown *_punk; }; class CTravelEntry : public ITravelEntry, public ITravelLogEntry, public IPropertyBag { public: CTravelEntry(BOOL fIsLocalAnchor); // *** IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); // *** ITravelEntry specific methods STDMETHODIMP Update(IUnknown *punk, BOOL fIsLocalAnchor); STDMETHODIMP Invoke(IUnknown *punk); STDMETHODIMP GetPidl(LPITEMIDLIST *ppidl); // *** ITravelLogEntry specific methods STDMETHODIMP GetTitle(LPOLESTR *ppszTitle); STDMETHODIMP GetURL(LPOLESTR *ppszURL); // *** IPropertyBag specific methods STDMETHODIMP Read(LPCOLESTR pszPropName, VARIANT *pVar, IErrorLog *pErrorLog); STDMETHODIMP Write(LPCOLESTR pszPropName, VARIANT *pVar); static HRESULT CreateTravelEntry(IBrowserService *pbs, BOOL fIsLocalAnchor, CTravelEntry **ppte); void SetPrev(CTravelEntry *ptePrev); void SetNext(CTravelEntry *pteNext); CTravelEntry *GetPrev() {return _ptePrev;} CTravelEntry *GetNext() {return _pteNext;} void RemoveSelf(); BOOL CanInvoke(IUnknown *punk, BOOL fAllowLocalAnchor); HRESULT GetIndexBrowser(IUnknown *punkIn, IUnknown ** ppsbOut) const; DWORD Size(); DWORD ListSize(); HRESULT Clone(CTravelEntry **ppte); HRESULT UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext); HRESULT UpdateSelf(IUnknown *punk) {return Update(punk, (_type == TET_LOCALANCHOR));} BOOL IsExternal(void) { return (_type==TET_EXTERNALNAV); } HRESULT GetDisplayName(LPTSTR psz, DWORD cch, DWORD dwFlags); BOOL IsEqual(LPCITEMIDLIST pidl) {return ILIsEqual(pidl, _pidl);} BOOL IsLocalAnchor(void) { return (_type==TET_LOCALANCHOR);} protected: CTravelEntry(void); HRESULT _InvokeExternal(IUnknown *punk); HRESULT _UpdateTravelLog(IUnknown *punk, BOOL fIsLocalAnchor); HRESULT _UpdateFromTLClient(IUnknown * punk, IStream ** ppStream); LONG _cRef; ~CTravelEntry(); void _Reset(void); enum { TET_EMPTY = 0, TET_DEFAULT = 1, TET_LOCALANCHOR, TET_EXTERNALNAV }; DWORD _type; // flags for our own sake... LPITEMIDLIST _pidl; // pidl of the entry HGLOBAL _hGlobalData; // the stream data saved by the entry DWORD _bid; // the BrowserIndex for frame specific navigation DWORD _dwCookie; // if _hGlobalData is NULL the cookie should be set WCHAR * _pwzTitle; WCHAR * _pwzUrlLocation; IHlink *_phl; IHlinkBrowseContext *_phlbc; IPropertyBag *_ppb; CTravelEntry *_ptePrev; CTravelEntry *_pteNext; }; CTravelEntry::CTravelEntry(BOOL fIsLocalAnchor) : _cRef(1) { //these should always be allocated // thus they will always start 0 if (fIsLocalAnchor) _type = TET_LOCALANCHOR; else ASSERT(!_type); ASSERT(!_pwzTitle); ASSERT(!_pwzUrlLocation); ASSERT(!_pidl); ASSERT(!_hGlobalData); ASSERT(!_bid); ASSERT(!_dwCookie); ASSERT(!_ptePrev); ASSERT(!_pteNext); ASSERT(!_phl); ASSERT(!_ppb); ASSERT(!_phlbc); TraceMsg(TF_TRAVELLOG, "TE[%X] created _type = %x", this, _type); } CTravelEntry::CTravelEntry(void) : _cRef(1) { ASSERT(!_type); ASSERT(!_pwzTitle); ASSERT(!_pwzUrlLocation); ASSERT(!_pidl); ASSERT(!_hGlobalData); ASSERT(!_bid); ASSERT(!_dwCookie); ASSERT(!_ptePrev); ASSERT(!_pteNext); ASSERT(!_phl); ASSERT(!_ppb); ASSERT(!_phlbc); TraceMsg(TF_TRAVELLOG, "TE[%X] created", this, _type); } HGLOBAL CloneHGlobal(HGLOBAL hGlobalIn) { DWORD dwSize = (DWORD)GlobalSize(hGlobalIn); HGLOBAL hGlobalOut = GlobalAlloc(GlobalFlags(hGlobalIn), dwSize); HGLOBAL hGlobalResult = NULL; if (NULL != hGlobalOut) { LPVOID pIn= GlobalLock(hGlobalIn); if (NULL != pIn) { LPVOID pOut= GlobalLock(hGlobalOut); if (NULL != pOut) { memcpy(pOut, pIn, dwSize); GlobalUnlock(hGlobalOut); hGlobalResult = hGlobalOut; } GlobalUnlock(hGlobalIn); } if (!hGlobalResult) { GlobalFree(hGlobalOut); hGlobalOut = NULL; } } return hGlobalResult; } HRESULT CTravelEntry::Clone(CTravelEntry **ppte) { // dont ever clone an external entry if (_type == TET_EXTERNALNAV) return E_FAIL; HRESULT hr = S_OK; CTravelEntry *pte = new CTravelEntry(); if (pte) { pte->_type = _type; pte->_bid = _bid; pte->_dwCookie = _dwCookie; if (_pwzTitle) { pte->_pwzTitle = StrDup(_pwzTitle); if (!pte->_pwzTitle) { hr = E_OUTOFMEMORY; } } if (_pwzUrlLocation) { pte->_pwzUrlLocation = StrDup(_pwzUrlLocation); if (!pte->_pwzUrlLocation) { hr = E_OUTOFMEMORY; } } if (_pidl) { pte->_pidl = ILClone(_pidl); if (!pte->_pidl) hr = E_OUTOFMEMORY; } else pte->_pidl = NULL; if (_hGlobalData) { pte->_hGlobalData = CloneHGlobal(_hGlobalData); if (NULL == pte->_hGlobalData) { hr = E_OUTOFMEMORY; } } else { ASSERT(NULL == pte->_hGlobalData); } } else hr = E_OUTOFMEMORY; if (FAILED(hr) && pte) { pte->Release(); *ppte = NULL; } else *ppte = pte; TraceMsg(TF_TRAVELLOG, "TE[%X] Clone hr = %x", this, hr); return hr; } CTravelEntry::~CTravelEntry() { ILFree(_pidl); if (_hGlobalData) { GlobalFree(_hGlobalData); _hGlobalData = NULL; } if (_pwzTitle) { LocalFree(_pwzTitle); _pwzTitle = NULL; } if (_pwzUrlLocation) { LocalFree(_pwzUrlLocation); _pwzUrlLocation = NULL; } if (_pteNext) { _pteNext->Release(); } // Don't need to release _ptePrev because TravelEntry only addref's pteNext ATOMICRELEASE(_ppb); ATOMICRELEASE(_phl); ATOMICRELEASE(_phlbc); TraceMsg(TF_TRAVELLOG, "TE[%X] destroyed ", this); } HRESULT CTravelEntry::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CTravelEntry, ITravelEntry), // IID_ITravelEntry QITABENT(CTravelEntry, ITravelLogEntry), QITABENT(CTravelEntry, IPropertyBag), { 0 }, }; return QISearch(this, qit, riid, ppvObj); } ULONG CTravelEntry::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CTravelEntry::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if (0 == cRef) { delete this; } return cRef; } //+------------------------------------------------------------------------- // // Method : CTravelEntry::GetIndexBrowser // // Synopsis : This method finds and returns the IUnknown of the browser // with the index in _bid. This method first checks to see // if the passed in punk supports ITravelLogClient. If it // doesn't, it checks for IBrowserService. // //-------------------------------------------------------------------------- HRESULT CTravelEntry::GetIndexBrowser(IUnknown * punk, IUnknown ** ppunkBrowser) const { HRESULT hr = E_FAIL; ASSERT(ppunkBrowser); ITravelLogClient * ptlcTop; hr = punk->QueryInterface(IID_PPV_ARG(ITravelLogClient, &ptlcTop)); if (SUCCEEDED(hr)) { hr = ptlcTop->FindWindowByIndex(_bid, ppunkBrowser); ptlcTop->Release(); } TraceMsg(TF_TRAVELLOG, "TE[%X]::GetIndexBrowser _bid = %X, hr = %X", this, _bid, hr); return hr; } //+------------------------------------------------------------------------- // // Method : CTravelEntry::CanInvoke // // Synopsis : This method determines if the current travel entry can // be invoked. There are two criteria that determine if // this entry can be invoked. // 1) If the entry is a local anchor, fAllowLocalAnchor must // be TRUE. // 2) A browser with the index in _bid must exist. // //-------------------------------------------------------------------------- BOOL CTravelEntry::CanInvoke(IUnknown *punk, BOOL fAllowLocalAnchor) { IUnknown * punkBrowser = NULL; BOOL fRet = IsLocalAnchor() ? fAllowLocalAnchor : TRUE; fRet = fRet && SUCCEEDED(GetIndexBrowser(punk, &punkBrowser)); SAFERELEASE(punkBrowser); return fRet; } DWORD CTravelEntry::Size() { DWORD cbSize = SIZEOF(*this); if (_pidl) cbSize += ILGetSize(_pidl); if (_hGlobalData) { cbSize += (DWORD)GlobalSize(_hGlobalData); } if (_pwzTitle) { cbSize += (DWORD)LocalSize(_pwzTitle); } if (_pwzUrlLocation) { cbSize += (DWORD)LocalSize(_pwzUrlLocation); } return cbSize; } DWORD CTravelEntry::ListSize() { CTravelEntry *pte = GetNext(); DWORD cb = Size(); while (pte) { cb += pte->Size(); pte = pte->GetNext(); } return cb; } void CTravelEntry::_Reset() { Pidl_Set(&_pidl, NULL); if (NULL != _hGlobalData) { GlobalFree(_hGlobalData); _hGlobalData = NULL; } ATOMICRELEASE(_phl); ATOMICRELEASE(_phlbc); _bid = 0; _type = TET_EMPTY; _dwCookie = 0; if (_pwzTitle) { LocalFree(_pwzTitle); _pwzTitle = NULL; } if (_pwzUrlLocation) { LocalFree(_pwzUrlLocation); _pwzUrlLocation = NULL; } TraceMsg(TF_TRAVELLOG, "TE[%X]::_Reset", this); } HRESULT CTravelEntry::_UpdateTravelLog(IUnknown *punk, BOOL fIsLocalAnchor) { IBrowserService *pbs; HRESULT hr = E_FAIL; // we need to update here if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs)))) { ITravelLog *ptl; if (SUCCEEDED(pbs->GetTravelLog(&ptl))) { hr = ptl->UpdateEntry(punk, fIsLocalAnchor); ptl->Release(); } pbs->Release(); } return hr; } HRESULT CTravelEntry::_InvokeExternal(IUnknown *punk) { HRESULT hr = E_FAIL; ASSERT(_phl); ASSERT(_phlbc); TraceMsg(TF_TRAVELLOG, "TE[%X]::InvokeExternal entered on _bid = %X, _phl = %X, _phlbc = %X", this, _bid, _phl, _phlbc); HWND hwnd = NULL; IOleWindow *pow; if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IOleWindow, &pow)))) { pow->GetWindow(&hwnd); pow->Release(); } // set the size and position of the browser frame window, so that the // external target can sync up its frame window to those coordinates HLBWINFO hlbwi = {0}; hlbwi.cbSize = sizeof(hlbwi); hlbwi.grfHLBWIF = 0; if (hwnd) { WINDOWPLACEMENT wp = {0}; wp.length = sizeof(WINDOWPLACEMENT); GetWindowPlacement(hwnd, &wp); hlbwi.grfHLBWIF = HLBWIF_HASFRAMEWNDINFO; hlbwi.rcFramePos = wp.rcNormalPosition; if (wp.showCmd == SW_SHOWMAXIMIZED) hlbwi.grfHLBWIF |= HLBWIF_FRAMEWNDMAXIMIZED; } _phlbc->SetBrowseWindowInfo(&hlbwi); // // right now we always now we are going back, but later on // maybe we should ask the browser whether this is back or forward // hr = _phl->Navigate(HLNF_NAVIGATINGBACK, NULL, NULL, _phlbc); IWebBrowser2 *pwb; if (SUCCEEDED(IUnknown_QueryService(punk, SID_SWebBrowserApp, IID_PPV_ARG(IWebBrowser2, &pwb)))) { ASSERT(pwb); pwb->put_Visible(FALSE); pwb->Release(); } _UpdateTravelLog(punk, FALSE); TraceMsg(TF_TRAVELLOG, "TE[%X]::InvokeExternal exited hr = %X", this, hr); return hr; } HRESULT CTravelEntry::Invoke(IUnknown *punk) { IPersistHistory *pph = NULL; HRESULT hr = E_FAIL; IUnknown * punkBrowser = NULL; IHTMLWindow2 * pWindow = NULL; TraceMsg(TF_TRAVELLOG, "TE[%X]::Invoke entered on _bid = %X", this, _bid); TraceMsgW(TF_TRAVELLOG, "TE[%X]::Invoke title '%s'", this, _pwzTitle); if (_type == TET_EXTERNALNAV) { hr = _InvokeExternal(punk); goto Quit; } // Get the window/browser with the index. If that // fails, punk may be a IHTMLWindow2. If so, // get its IPersistHistory so the travel entry // can be loaded directly. (This is needed by Trident // in order to navigate in frames when traveling // backwards or forwards. // hr = GetIndexBrowser(punk, &punkBrowser); if (SUCCEEDED(hr)) { hr = punkBrowser->QueryInterface(IID_PPV_ARG(IPersistHistory, &pph)); } else { hr = punk->QueryInterface(IID_PPV_ARG(IHTMLWindow2, &pWindow)); if (SUCCEEDED(hr)) { hr = pWindow->QueryInterface(IID_PPV_ARG(IPersistHistory, &pph)); } } if (SUCCEEDED(hr)) { ASSERT(pph); if (_type == TET_LOCALANCHOR) { ITravelLogClient * pTLClient; hr = pph->QueryInterface(IID_PPV_ARG(ITravelLogClient, &pTLClient)); if (SUCCEEDED(hr)) { hr = pTLClient->LoadHistoryPosition(_pwzUrlLocation, _dwCookie); pTLClient->Release(); } else { hr = pph->SetPositionCookie(_dwCookie); } } else { // we need to clone it ASSERT(_hGlobalData); HGLOBAL hGlobal = CloneHGlobal(_hGlobalData); if (NULL != hGlobal) { IStream *pstm; hr = CreateStreamOnHGlobal(hGlobal, TRUE, &pstm); if (SUCCEEDED(hr)) { hr = pph->LoadHistory(pstm, NULL); pstm->Release(); } else { GlobalFree(hGlobal); } } else { hr = E_OUTOFMEMORY; } } pph->Release(); } Quit: SAFERELEASE(punkBrowser); SAFERELEASE(pWindow); TraceMsg(TF_TRAVELLOG, "TE[%X]::Invoke exited on _bid = %X, hr = %X", this, _bid, hr); return hr; } HRESULT CTravelEntry::UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext) { TraceMsg(TF_TRAVELLOG, "TE[%X]::UpdateExternal entered on punk = %X, punkhlbc = %X", this, punk, punkHLBrowseContext); _Reset(); ASSERT(punkHLBrowseContext); punkHLBrowseContext->QueryInterface(IID_PPV_ARG(IHlinkBrowseContext, &_phlbc)); ASSERT(_phlbc); _type = TET_EXTERNALNAV; HRESULT hr = E_FAIL; // // right now we only support externals being previous. we never actually navigate // to another app. we handle everything in pane ourselves. // so theoretically we never need to worry about HLID_NEXT _phlbc->GetHlink((ULONG) HLID_PREVIOUS, &_phl); IBrowserService *pbs; punk->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs)); if (pbs && _phl) { _bid = pbs->GetBrowserIndex(); WCHAR *pwszTarget; hr = _phl->GetStringReference(HLINKGETREF_ABSOLUTE, &pwszTarget, NULL); if (SUCCEEDED(hr)) { // create pidl hr = IECreateFromPath(pwszTarget, &_pidl); OleFree(pwszTarget); } } ATOMICRELEASE(pbs); TraceMsg(TF_TRAVELLOG, "TE[%X]::UpdateExternal exited _bid = %X, hr = %X", this, _bid, hr); return hr; } HRESULT CTravelEntry::Update(IUnknown *punk, BOOL fIsLocalAnchor) { ASSERT(punk); // this means that we went back to an external app, // and now we are going forward again. we dont persist // any state info about them that would be different. if (_type == TET_EXTERNALNAV) { TraceMsg(TF_TRAVELLOG, "TE[%X]::Update NOOP on external entry", this); return S_OK; } _Reset(); // Try ITravelLogClient first. If that fails, revert to IBrowserService. // IStream *pstm = NULL; IPersistHistory *pph = NULL; HRESULT hr = _UpdateFromTLClient(punk, &pstm); if (S_OK != hr) goto Cleanup; hr = punk->QueryInterface(IID_PPV_ARG(IPersistHistory, &pph)); ASSERT(SUCCEEDED(hr)); if (S_OK != hr) goto Cleanup; if (fIsLocalAnchor) { // persist a cookie // _type = TET_LOCALANCHOR; hr = pph->GetPositionCookie(&_dwCookie); } else { _type = TET_DEFAULT; // persist a stream // ASSERT(!_hGlobalData); if (!pstm) { hr = CreateStreamOnHGlobal(NULL, FALSE, &pstm); if (hr != S_OK) goto Cleanup; pph->SaveHistory(pstm); } STATSTG stg; HRESULT hrStat = pstm->Stat(&stg, STATFLAG_NONAME); hr = GetHGlobalFromStream(pstm, &_hGlobalData); // This little exercise here is to shrink the memory block we get from // the OLE API which allocates blocks in chunks of 8KB. Typical stream // sizes are only a few hundred bytes. if (S_OK != hrStat) goto Cleanup; HGLOBAL hGlobalTemp = GlobalReAlloc(_hGlobalData, stg.cbSize.LowPart, GMEM_MOVEABLE); if (NULL != hGlobalTemp) { _hGlobalData = hGlobalTemp; } } Cleanup: if (FAILED(hr)) _Reset(); SAFERELEASE(pstm); SAFERELEASE(pph); TraceMsg(TF_TRAVELLOG, "TE[%X]::Update exited on _bid = %X, hr = %X", this, _bid, hr); return hr; } //+----------------------------------------------------------------------------- // // Method : CTravelEntry::_UpdateFromTLClient // // Synopsis : Updates the travel entry using the ITravelLogClient interface // //------------------------------------------------------------------------------ HRESULT CTravelEntry::_UpdateFromTLClient(IUnknown * punk, IStream ** ppStream) { HRESULT hr; WINDOWDATA windata = {0}; ITravelLogClient * ptlc = NULL; hr = punk->QueryInterface(IID_PPV_ARG(ITravelLogClient, &ptlc)); if (S_OK != hr) goto Cleanup; hr = ptlc->GetWindowData(&windata); if (S_OK != hr) goto Cleanup; _bid = windata.dwWindowID; ILFree(_pidl); if (windata.pidl) { _pidl = ILClone(windata.pidl); } else { hr = IEParseDisplayNameWithBCW(windata.uiCP, windata.lpszUrl, NULL, &_pidl); if (S_OK != hr) goto Cleanup; } ASSERT(_pidl); // If there is an url location, append it to the end of the url // if (_pwzUrlLocation) { LocalFree(_pwzUrlLocation); _pwzUrlLocation = NULL; } if (windata.lpszUrlLocation && *windata.lpszUrlLocation) { _pwzUrlLocation = StrDup(windata.lpszUrlLocation); } // Pick up the title as a display name for menus and such. // if (_pwzTitle) { LocalFree(_pwzTitle); _pwzTitle = NULL; } if (windata.lpszTitle) _pwzTitle = StrDup(windata.lpszTitle); *ppStream = windata.pStream; TraceMsgW(TF_TRAVELLOG, "TE[%X]::_UpdateFromTLClient - ptlc:[0x%X] _bid:[%ld] Url:[%ws] Title:[%ws] UrlLocation:[%ws] ppStream:[0x%X]", this, ptlc, _bid, windata.lpszUrl, _pwzTitle, _pwzUrlLocation, *ppStream); Cleanup: ILFree(windata.pidl); CoTaskMemFree(windata.lpszUrl); CoTaskMemFree(windata.lpszUrlLocation); CoTaskMemFree(windata.lpszTitle); SAFERELEASE(ptlc); // Don't release windata.pStream. It will // be released when ppStream is released. return hr; } HRESULT CTravelEntry::GetPidl(LPITEMIDLIST * ppidl) { HRESULT hr = E_FAIL; if (EVAL(ppidl)) { hr = SHILClone(_pidl, ppidl); } return hr; } void CTravelEntry::SetNext(CTravelEntry *pteNext) { if (_pteNext) _pteNext->Release(); _pteNext = pteNext; if (_pteNext) { _pteNext->_ptePrev = this; } } void CTravelEntry::SetPrev(CTravelEntry *ptePrev) { _ptePrev = ptePrev; if (_ptePrev) _ptePrev->SetNext(this); } // // this is for removing from the middle of the list... // void CTravelEntry::RemoveSelf() { if (_pteNext) _pteNext->_ptePrev = _ptePrev; // remove yourself from the list if (_ptePrev) { // after this point, we may be destroyed so can't touch any more member vars _ptePrev->_pteNext = _pteNext; } _ptePrev = NULL; _pteNext = NULL; // we lose a reference now because we're gone from _ptePrev's _pteNext // (or if we were the top of the list, we're also nuked) Release(); } HRESULT GetUnescapedUrlIfAppropriate(LPCITEMIDLIST pidl, LPTSTR pszUrl, DWORD cch) { TCHAR szUrl[MAX_URL_STRING]; // The SHGDN_NORMAL display name will be the pretty name (Web Page title) unless // it's an FTP URL or the web page didn't set a title. if (SUCCEEDED(IEGetDisplayName(pidl, szUrl, SHGDN_NORMAL)) && UrlIs(szUrl, URLIS_URL)) { // NT #279192, If an URL is escaped, it normally contains three types of // escaped chars. // 1) Seperating type chars ('#' for frag, '?' for params, etc.) // 2) DBCS chars, // 3) Data (a bitmap in the url by escaping the binary bytes) // Since #2 is very common, we want to try to unescape it so it has meaning // to the user. UnEscaping isn't safe if the user can copy or modify the data // because they could loose data when it's reparsed. One thing we need to // do for #2 to work is for it to be in ANSI when unescaped. This is needed // or the DBCS lead and trail bytes will be in unicode as [0x 0x00] // [0x 0x00]. Being in ANSI could cause a problem if the the string normally // crosses code pages, but that is uncommon or non-existent in the IsURLChild() // case. CHAR szUrlAnsi[MAX_URL_STRING]; SHTCharToAnsi(szUrl, szUrlAnsi, ARRAYSIZE(szUrlAnsi)); UrlUnescapeA(szUrlAnsi, NULL, NULL, URL_UNESCAPE_INPLACE|URL_UNESCAPE_HIGH_ANSI_ONLY); SHAnsiToTChar(szUrlAnsi, pszUrl, cch); } else { StrCpyN(pszUrl, szUrl, cch); // Truncate if needed } return S_OK; } #define TEGDN_FORSYSTEM 0x00000001 HRESULT CTravelEntry::GetDisplayName(LPTSTR psz, DWORD cch, DWORD dwFlags) { if (!psz || !cch) return E_INVALIDARG; psz[0] = 0; if ((NULL != _pwzTitle) && (*_pwzTitle != 0)) { StrCpyNW(psz, _pwzTitle, cch); } else if (_pidl) { GetUnescapedUrlIfAppropriate(_pidl, psz, cch); } if (dwFlags & TEGDN_FORSYSTEM) { if (!SHIsDisplayable(psz, g_fRunOnFE, g_bRunOnNT5)) { // Display name isn't system-displayable. Just use the path/url instead. SHTitleFromPidl(_pidl, psz, cch, FALSE); } } SHCleanupUrlForDisplay(psz); return psz[0] ? S_OK : E_FAIL; } HRESULT CTravelEntry::GetTitle(LPOLESTR *ppszTitle) { HRESULT hres = S_OK; TCHAR szTitle[MAX_BROWSER_WINDOW_TITLE]; ASSERT(IS_VALID_WRITE_PTR(ppszTitle, LPOLESTR)); hres = GetDisplayName(szTitle, ARRAYSIZE(szTitle), TEGDN_FORSYSTEM); if (SUCCEEDED(hres)) { ASSERT(*szTitle); hres = SHStrDup(szTitle, ppszTitle); } return hres; } HRESULT CTravelEntry::GetURL(LPOLESTR *ppszUrl) { HRESULT hres = E_FAIL; LPITEMIDLIST pidl = NULL; WCHAR wszURL[MAX_URL_STRING]; if (_pidl) hres = ::IEGetDisplayName(_pidl, wszURL, SHGDN_FORPARSING); if (SUCCEEDED(hres)) hres = SHStrDup(wszURL, ppszUrl); return hres; } HRESULT CTravelEntry::Read(LPCOLESTR pszPropName, VARIANT *pVar, IErrorLog *pErrorLog) { if (!_ppb) { return E_INVALIDARG; } return _ppb->Read(pszPropName, pVar, pErrorLog); } HRESULT CTravelEntry::Write(LPCOLESTR pszPropName, VARIANT *pVar) { HRESULT hres = S_OK; if (!_ppb) { hres = SHCreatePropertyBagOnMemory(STGM_READWRITE, IID_PPV_ARG(IPropertyBag, &_ppb)); } if (SUCCEEDED(hres)) { ASSERT(_ppb); hres = _ppb->Write(pszPropName, pVar); } return hres; } class CTravelLog : public ITravelLog, public ITravelLogEx { public: // *** IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef() ; STDMETHODIMP_(ULONG) Release(); // *** ITravelLog specific methods STDMETHODIMP AddEntry(IUnknown *punk, BOOL fIsLocalAnchor); STDMETHODIMP UpdateEntry(IUnknown *punk, BOOL fIsLocalAnchor); STDMETHODIMP UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext); STDMETHODIMP Travel(IUnknown *punk, int iOffset); STDMETHODIMP GetTravelEntry(IUnknown *punk, int iOffset, ITravelEntry **ppte); STDMETHODIMP FindTravelEntry(IUnknown *punk, LPCITEMIDLIST pidl, ITravelEntry **ppte); STDMETHODIMP GetToolTipText(IUnknown *punk, int iOffset, int idsTemplate, LPWSTR pwzText, DWORD cchText); STDMETHODIMP InsertMenuEntries(IUnknown *punk, HMENU hmenu, int nPos, int idFirst, int idLast, DWORD dwFlags); STDMETHODIMP Clone(ITravelLog **pptl); STDMETHODIMP_(DWORD) CountEntries(IUnknown *punk); STDMETHODIMP Revert(void); // *** ITravelLogEx specific methods STDMETHODIMP FindTravelEntryWithUrl(IUnknown * punk, UINT uiCP, LPOLESTR lpszUrl, ITravelEntry ** ppte); STDMETHODIMP TravelToUrl(IUnknown * punk, UINT uiCP, LPOLESTR lpszUrl); STDMETHOD(DeleteIndexEntry)(IUnknown *punk, int index); STDMETHOD(DeleteUrlEntry)(IUnknown *punk, UINT uiCP, LPOLESTR pszUrl); STDMETHOD(CountEntryNodes)(IUnknown *punk, DWORD dwFlags, DWORD *pdwCount); STDMETHOD(CreateEnumEntry)(IUnknown *punk, IEnumTravelLogEntry **ppEnum, DWORD dwFlags); STDMETHOD(DeleteEntry)(IUnknown *punk, ITravelLogEntry *pte); STDMETHOD(InsertEntry)(IUnknown *punkBrowser, ITravelLogEntry *pteRelativeTo, BOOL fPrepend, IUnknown* punkTLClient, ITravelLogEntry **ppEntry); STDMETHOD(TravelToEntry)(IUnknown *punkBrowser, ITravelLogEntry *pteDestination); CTravelLog(); protected: ~CTravelLog(); HRESULT _FindEntryByOffset(IUnknown *punk, int iOffset, CTravelEntry **ppte); HRESULT _FindEntryByPidl(IUnknown * punk, LPCITEMIDLIST pidl, CTravelEntry ** ppte); HRESULT _FindEntryByPunk(IUnknown * punk, ITravelLogEntry *pteSearch, CTravelEntry ** ppte); void _DeleteFrameSetEntry(IUnknown *punk, CTravelEntry *pte); void _Prune(void); LONG _cRef; DWORD _cbMaxSize; DWORD _cbTotalSize; CTravelEntry *_pteCurrent; //pteCurrent CTravelEntry *_pteUpdate; CTravelEntry *_pteRoot; }; CTravelLog::CTravelLog() : _cRef(1) { ASSERT(!_pteCurrent); ASSERT(!_pteUpdate); ASSERT(!_pteRoot); DWORD dwType, dwSize = SIZEOF(_cbMaxSize), dwDefault = 1024 * 1024; SHRegGetUSValue(TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\TravelLog"), TEXT("MaxSize"), &dwType, (LPVOID)&_cbMaxSize, &dwSize, FALSE, (void *)&dwDefault, SIZEOF(dwDefault)); TraceMsg(TF_TRAVELLOG, "TL[%X] created", this); } CTravelLog::~CTravelLog() { //DestroyList by releasing the root SAFERELEASE(_pteRoot); TraceMsg(TF_TRAVELLOG, "TL[%X] destroyed ", this); } HRESULT CTravelLog::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CTravelLog, ITravelLog), // IID_ITravelLog QITABENT(CTravelLog, ITravelLogEx), // IID_ITravelLogEx { 0 }, }; return QISearch(this, qit, riid, ppvObj); } ULONG CTravelLog::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CTravelLog::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if (0 == cRef) { delete this; } return cRef; } HRESULT CTravelLog::AddEntry(IUnknown *punk, BOOL fIsLocalAnchor) { ASSERT(punk); if (SHRestricted2W(REST_NoNavButtons, NULL, 0)) { return S_FALSE; } TraceMsg(TF_TRAVELLOG, "TL[%X]::AddEntry punk = %X, IsLocal = %s", this, punk, fIsLocalAnchor ? "TRUE" : "FALSE"); CTravelEntry *pte = new CTravelEntry(fIsLocalAnchor); if (pte) { //replace the current with the new if (_pteCurrent) { CTravelEntry *pteNext = _pteCurrent->GetNext(); if (pteNext) { _cbTotalSize -= pteNext->ListSize(); } // the list keeps its own ref count, and only needs // to be modified when passed outside of the list // setnext will release the current next if necessary // this will also set pte->prev = pteCurrent _pteCurrent->SetNext(pte); } else _pteRoot = pte; _cbTotalSize += pte->Size(); _pteCurrent = pte; ASSERT(_cbTotalSize == _pteRoot->ListSize()); } TraceMsg(TF_TRAVELLOG, "TL[%X]::AddEntry punk = %X, IsLocal = %d, pte = %X", this, punk, fIsLocalAnchor, pte); return pte ? S_OK : E_OUTOFMEMORY; } void CTravelLog::_Prune(void) { // FEATURE: need an increment or something ASSERT(_cbTotalSize == _pteRoot->ListSize()); while (_cbTotalSize > _cbMaxSize && _pteRoot != _pteCurrent) { CTravelEntry *pte = _pteRoot; _pteRoot = _pteRoot->GetNext(); _cbTotalSize -= pte->Size(); pte->RemoveSelf(); ASSERT(_cbTotalSize == _pteRoot->ListSize()); } } HRESULT CTravelLog::UpdateEntry(IUnknown *punk, BOOL fIsLocalAnchor) { CTravelEntry *pte = _pteUpdate ? _pteUpdate : _pteCurrent; // this can happen under weird stress conditions, evidently if (!pte) return E_FAIL; _cbTotalSize -= pte->Size(); HRESULT hr = pte->Update(punk, fIsLocalAnchor); _cbTotalSize += pte->Size(); ASSERT(_cbTotalSize == _pteRoot->ListSize()); // Debug prints need to be before _Prune() since pte can get freed by _Prune() resulting // in a crash if pte->Size() is called TraceMsg(TF_TRAVELLOG, "TL[%X]::UpdateEntry pte->Size() = %d", this, pte->Size()); TraceMsg(TF_TRAVELLOG, "TL[%X]::UpdateEntry punk = %X, IsLocal = %d, hr = %X", this, punk, fIsLocalAnchor, hr); _Prune(); _pteUpdate = NULL; return hr; } HRESULT CTravelLog::UpdateExternal(IUnknown *punk, IUnknown *punkHLBrowseContext) { CTravelEntry *pte = _pteUpdate ? _pteUpdate : _pteCurrent; ASSERT(punk); ASSERT(pte); ASSERT(punkHLBrowseContext); if (pte) return pte->UpdateExternal(punk, punkHLBrowseContext); return E_FAIL; } HRESULT CTravelLog::Travel(IUnknown *punk, int iOffset) { ASSERT(punk); HRESULT hr = E_FAIL; CTravelEntry *pte; TraceMsg(TF_TRAVELLOG, "TL[%X]::Travel entered with punk = %X, iOffset = %d", this, punk, iOffset); if (SUCCEEDED(_FindEntryByOffset(punk, iOffset, &pte))) { #ifdef DEBUG TCHAR szPath[MAX_PATH]; LPITEMIDLIST pidl; pte->GetPidl(&pidl); SHGetNameAndFlags(pidl, SHGDN_FORPARSING, szPath, ARRAYSIZE(szPath), NULL); ILFree(pidl); TraceMsgW(TF_TRAVELLOG, "TL[%X]::URL %s", this, szPath); #endif // we will update where we are before we move away... // but external navigates dont go through the normal activation // so we dont want to setup the external to be updated // _pteUpdate is also what allows us to Revert(). if (!_pteCurrent->IsExternal() && !_pteUpdate) _pteUpdate = _pteCurrent; _pteCurrent = pte; hr = _pteCurrent->Invoke(punk); // // if the entry bails with an error, then we need to reset ourself // to what we were. right now, the only place this should happen // is if an Abort was returned from SetPositionCookie // because somebody aborted during before navigate. // but i think that any error means that we can legitimately Revert(). // if (FAILED(hr)) { Revert(); } } TraceMsg(TF_TRAVELLOG, "TL[%X]::Travel exited with hr = %X", this, hr); return hr; } //+--------------------------------------------------------------------------- // // Method : CTravelLog::TravelToUrl // // Interface : ITravelLogEx // // Synopsis : Travels to the specified URL in the travel log. // //---------------------------------------------------------------------------- HRESULT CTravelLog::TravelToUrl(IUnknown * punk, UINT uiCP, LPOLESTR lpszUrl) { ASSERT(punk); ASSERT(lpszUrl); HRESULT hr; LPITEMIDLIST pidl; CTravelEntry * pte = NULL; TCHAR szUrl[INTERNET_MAX_URL_LENGTH]; DWORD cchOut = ARRAYSIZE(szUrl); hr = UrlCanonicalize(lpszUrl, szUrl, &cchOut, URL_ESCAPE_SPACES_ONLY); if (SUCCEEDED(hr)) { hr = IEParseDisplayName(uiCP, szUrl, &pidl); if (SUCCEEDED(hr)) { hr = _FindEntryByPidl(punk, pidl, &pte); ILFree(pidl); if (SUCCEEDED(hr)) { // We will update where we are before we move away... // but external navigates don't go through the normal activation // so we dont want to setup the external to be updated // _pteUpdate is also what allows us to Revert(). // if (!_pteCurrent->IsExternal() && !_pteUpdate) { _pteUpdate = _pteCurrent; } _pteCurrent = pte; hr = _pteCurrent->Invoke(punk); // If the entry bails with an error, then we need to reset ourself // to what we were. Right now, the only place this should happen // is if an Abort was returned from SetPositionCookie // because somebody aborted during before navigate. // But i think that any error means that we can legitimately Revert(). // if (FAILED(hr)) { Revert(); } } } } TraceMsg(TF_TRAVELLOG, "TL[%X]::TravelToUrl exited with hr = %X", this, hr); return hr; } HRESULT CTravelLog::_FindEntryByOffset(IUnknown *punk, int iOffset, CTravelEntry **ppte) { CTravelEntry *pte = _pteCurrent; BOOL fAllowLocalAnchor = TRUE; if (iOffset < 0) { while (iOffset && pte) { pte = pte->GetPrev(); if (pte && pte->CanInvoke(punk, fAllowLocalAnchor)) { iOffset++; fAllowLocalAnchor = fAllowLocalAnchor && pte->IsLocalAnchor(); } } } else if (iOffset > 0) { while (iOffset && pte) { pte = pte->GetNext(); if (pte && pte->CanInvoke(punk, fAllowLocalAnchor)) { iOffset--; fAllowLocalAnchor = fAllowLocalAnchor && pte->IsLocalAnchor(); } } } if (pte) { *ppte = pte; return S_OK; } return E_FAIL; } HRESULT CTravelLog::GetTravelEntry(IUnknown *punk, int iOffset, ITravelEntry **ppte) { HRESULT hr; BOOL fCheckExternal = FALSE; if (iOffset == TLOG_BACKEXTERNAL) { iOffset = TLOG_BACK; fCheckExternal = TRUE; } if (iOffset == 0) { // APPCOMPAT - going back and fore between external apps is dangerous - zekel 24-JUN-97 // we always fail if the current is external // this is because word will attempt to navigate us to // the same url instead of FORE when the user selects // it from the drop down. if (_pteCurrent && _pteCurrent->IsExternal()) { hr = E_FAIL; ASSERT(!_pteCurrent->GetPrev()); TraceMsg(TF_TRAVELLOG, "TL[%X]::GetTravelEntry current is External", this); goto Quit; } } CTravelEntry *pte; hr = _FindEntryByOffset(punk, iOffset, &pte); // // If TLOG_BACKEXTERNAL is specified, we return S_OK only if the previous // entry is external. // if (fCheckExternal && SUCCEEDED(hr)) { if (!pte->IsExternal()) { hr = E_FAIL; } TraceMsg(TF_TRAVELLOG, "TL[%X]::GetTravelEntry(BACKEX)", this); } if (ppte && SUCCEEDED(hr)) { hr = pte->QueryInterface(IID_PPV_ARG(ITravelEntry, ppte)); } Quit: TraceMsg(TF_TRAVELLOG, "TL[%X]::GetTravelEntry iOffset = %d, hr = %X", this, iOffset, hr); return hr; } //+--------------------------------------------------------------------------- // // Method : CTravelLog::FindTravelEntry // // Synopsis : Finds the travel entry with the specified PIDL and returns // the ITravelEntry interface of the entry. // //---------------------------------------------------------------------------- HRESULT CTravelLog::FindTravelEntry(IUnknown *punk, LPCITEMIDLIST pidl, ITravelEntry **ppte) { CTravelEntry * pte = _pteRoot; _FindEntryByPidl(punk, pidl, &pte); if (pte) { return pte->QueryInterface(IID_PPV_ARG(ITravelEntry, ppte)); } *ppte = NULL; return E_FAIL; } //+--------------------------------------------------------------------------- // // Method : CTravelLog::_FindEntryByPidl // // Synopsis : Finds and returns the travel entry with the specified PIDL. // This private method returns a CTravelEntry instead of // an ITravelEntry. // //---------------------------------------------------------------------------- HRESULT CTravelLog::_FindEntryByPidl(IUnknown * punk, LPCITEMIDLIST pidl, CTravelEntry ** ppte) { CTravelEntry * pte = _pteRoot; BOOL fAllowLocalAnchor = TRUE; ASSERT(punk); ASSERT(pidl); ASSERT(ppte); while (pte) { if (pte->CanInvoke(punk, fAllowLocalAnchor) && pte->IsEqual(pidl)) { *ppte = pte; return S_OK; } fAllowLocalAnchor = fAllowLocalAnchor && pte->IsLocalAnchor(); pte = pte->GetNext(); } *ppte = NULL; return E_FAIL; } //+--------------------------------------------------------------------------- // // Method : CTravelLog::FindEntryByPunk // // Interface : ITravelLogEx // // Synopsis : Find the entry object given its punk. // //---------------------------------------------------------------------------- HRESULT CTravelLog::_FindEntryByPunk(IUnknown * punk, ITravelLogEntry *pteSearch, CTravelEntry ** ppte) { CTravelEntry *pte = _pteRoot; ITravelEntry *pteCur; BOOL fAllowLocalAnchor = TRUE; ASSERT(ppte); // check for the current entry. // often the current entry will fail CanInvoke because it's incomplete at this time. if (IsSameObject(pteSearch, SAFECAST(_pteCurrent, ITravelEntry*))) { *ppte = _pteCurrent; return S_OK; } while (pte) { pteCur = SAFECAST(pte, ITravelEntry*); if ((pte->CanInvoke(punk, fAllowLocalAnchor)) && IsSameObject(pteCur, pteSearch)) { *ppte = pte; return S_OK; } fAllowLocalAnchor = fAllowLocalAnchor && pte->IsLocalAnchor(); pte = pte->GetNext(); } *ppte = NULL; return E_FAIL; } //+--------------------------------------------------------------------------- // // Method : CTravelLog::FindTravelEntryWithUrl // // Interface : ITravelLogEx // // Synopsis : Finds and returns the travel entry with the specified URL. // //---------------------------------------------------------------------------- HRESULT CTravelLog::FindTravelEntryWithUrl(IUnknown * punk, UINT uiCP, LPOLESTR lpszUrl, ITravelEntry ** ppte) { LPITEMIDLIST pidl; HRESULT hr = E_FAIL; ASSERT(punk); ASSERT(lpszUrl); ASSERT(ppte); if (SUCCEEDED(IEParseDisplayNameWithBCW(uiCP, lpszUrl, NULL, &pidl))) { hr = FindTravelEntry(punk, pidl, ppte); ILFree(pidl); } return hr; } HRESULT CTravelLog::Clone(ITravelLog **pptl) { CTravelLog *ptl = new CTravelLog(); HRESULT hr = S_OK; if (ptl && _pteCurrent) { // first set the current pointer hr = _pteCurrent->Clone(&ptl->_pteCurrent); if (SUCCEEDED(hr)) { ptl->_cbTotalSize = _cbTotalSize; CTravelEntry *pteSrc; CTravelEntry *pteClone, *pteDst = ptl->_pteCurrent; // then we need to loop forward and set each for (pteSrc = _pteCurrent->GetNext(), pteDst = ptl->_pteCurrent; pteSrc; pteSrc = pteSrc->GetNext()) { ASSERT(pteDst); if (FAILED(pteSrc->Clone(&pteClone))) break; ASSERT(pteClone); pteDst->SetNext(pteClone); pteDst = pteClone; } //then loop back and set them all for (pteSrc = _pteCurrent->GetPrev(), pteDst = ptl->_pteCurrent; pteSrc; pteSrc = pteSrc->GetPrev()) { ASSERT(pteDst); if (FAILED(pteSrc->Clone(&pteClone))) break; ASSERT(pteClone); pteDst->SetPrev(pteClone); pteDst = pteClone; } // the root is the furthest back we could go ptl->_pteRoot = pteDst; } } else hr = E_OUTOFMEMORY; if (SUCCEEDED(hr)) { ptl->QueryInterface(IID_PPV_ARG(ITravelLog, pptl)); } else { *pptl = NULL; } if (ptl) ptl->Release(); TraceMsg(TF_TRAVELLOG, "TL[%X]::Clone hr = %x, ptlClone = %X", this, hr, ptl); return hr; } // HACKHACK: 3rd parameter used to be idsTemplate, which we would use to grab the // string template. However, since there's no way the caller can specify the hinst // of the module in which to look for this resource, this broke in the shdocvw / // browseui split (callers would pass offsets into browseui.dll; we'd look for them in // shdocvw.dll). My solution is is to ignore this parameter entirely and assume that: // // if iOffset is negative, the caller wants the "back to" text // else, the caller wants the "forward to" text // // tjgreen 14-july-98. // HRESULT CTravelLog::GetToolTipText(IUnknown *punk, int iOffset, int, LPWSTR pwzText, DWORD cchText) { TraceMsg(TF_TRAVELLOG, "TL[%X]::ToolTip entering iOffset = %d, ptlClone = %X", this, iOffset); ASSERT(pwzText); ASSERT(cchText); *pwzText = 0; CTravelEntry *pte; HRESULT hr = _FindEntryByOffset(punk, iOffset, &pte); if (SUCCEEDED(hr)) { ASSERT(pte); TCHAR szName[MAX_URL_STRING]; pte->GetDisplayName(szName, ARRAYSIZE(szName), 0); int idsTemplate = (iOffset < 0) ? IDS_NAVIGATEBACKTO : IDS_NAVIGATEFORWARDTO; TCHAR szTemplate[80]; if (MLLoadString(idsTemplate, szTemplate, ARRAYSIZE(szTemplate))) { DWORD cchTemplateLen = lstrlen(szTemplate); DWORD cchLen = cchTemplateLen + lstrlen(szName); if (cchLen > cchText) { // so that we don't overflow the pwzText buffer // review: do we even need this now that we are using StringCchPrintf below? szName[cchText - cchTemplateLen - 1] = 0; } StringCchPrintf(pwzText, cchText, szTemplate, szName); } else hr = E_UNEXPECTED; } TraceMsg(TF_TRAVELLOG, "TL[%X]::ToolTip exiting hr = %X, pwzText = %ls", this, hr, pwzText); return hr; } HRESULT CTravelLog::InsertMenuEntries(IUnknown *punk, HMENU hmenu, int iIns, int idFirst, int idLast, DWORD dwFlags) { ASSERT(idLast >= idFirst); ASSERT(hmenu); ASSERT(punk); int cItemsBack = idLast - idFirst + 1; int cItemsFore = 0; CTravelEntry *pte; LONG cAdded = 0; TraceMsg(TF_TRAVELLOG, "TL[%X]::InsertMenuEntries entered on punk = %X, hmenu = %X, iIns = %d, idRange = %d-%d, flags = %X", this, punk, hmenu, iIns, idFirst, idLast, dwFlags); ASSERT(cItemsFore >= 0); ASSERT(cItemsBack >= 0); if (IsFlagSet(dwFlags, TLMENUF_INCLUDECURRENT)) cItemsBack--; if (IsFlagSet(dwFlags, TLMENUF_BACKANDFORTH)) { cItemsFore = cItemsBack / 2; cItemsBack = cItemsBack - cItemsFore; } else if (IsFlagSet(dwFlags, TLMENUF_FORE)) { cItemsFore = cItemsBack; cItemsBack = 0; } TCHAR szName[40]; UINT uFlags = MF_STRING | MF_ENABLED | MF_BYPOSITION; while (cItemsFore) { if (SUCCEEDED(_FindEntryByOffset(punk, cItemsFore, &pte))) { pte->GetDisplayName(szName, ARRAYSIZE(szName), TEGDN_FORSYSTEM); ASSERT(*szName); FixAmpersands(szName, ARRAYSIZE(szName)); InsertMenu(hmenu, iIns, uFlags, idLast, szName); cAdded++; TraceMsg(TF_TRAVELLOG, "TL[%X]::IME Fore id = %d, szName = %s", this, idLast, szName); } cItemsFore--; idLast--; } if (IsFlagSet(dwFlags, TLMENUF_INCLUDECURRENT)) { // clear the name *szName = 0; //have to get the title from the actual pbs LPITEMIDLIST pidl = NULL; IBrowserService *pbs; if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs)))) { pbs->GetPidl(&pidl); WCHAR wzTitle[MAX_PATH]; if (SUCCEEDED(pbs->GetTitle(NULL, wzTitle, ARRAYSIZE(wzTitle)))) { StrCpyN(szName, wzTitle, ARRAYSIZE(szName)); } else if (pidl) { GetUnescapedUrlIfAppropriate(pidl, szName, ARRAYSIZE(szName)); } pbs->Release(); } if (!SHIsDisplayable(szName, g_fRunOnFE, g_bRunOnNT5) && pidl) { // Display name isn't system-displayable. Just use the path/url instead. SHTitleFromPidl(pidl, szName, ARRAYSIZE(szName), FALSE); } if (!(*szName)) TraceMsg(TF_ERROR, "CTravelLog::InsertMenuEntries -- failed to find title for current entry"); ILFree(pidl); FixAmpersands(szName, ARRAYSIZE(szName)); InsertMenu(hmenu, iIns, uFlags | (IsFlagSet(dwFlags, TLMENUF_CHECKCURRENT) ? MF_CHECKED : 0), idLast, szName); cAdded++; TraceMsg(TF_TRAVELLOG, "TL[%X]::IME Current id = %d, szName = %s", this, idLast, szName); idLast--; } if (IsFlagSet(dwFlags, TLMENUF_BACKANDFORTH)) { // we need to reverse the order of insertion for back // when both directions are displayed int i; for (i = 1; i <= cItemsBack; i++, idLast--) { if (SUCCEEDED(_FindEntryByOffset(punk, -i, &pte))) { pte->GetDisplayName(szName, ARRAYSIZE(szName), TEGDN_FORSYSTEM); ASSERT(*szName); FixAmpersands(szName, ARRAYSIZE(szName)); InsertMenu(hmenu, iIns, uFlags, idLast, szName); cAdded++; TraceMsg(TF_TRAVELLOG, "TL[%X]::IME Back id = %d, szName = %s", this, idLast, szName); } } } else while (cItemsBack) { if (SUCCEEDED(_FindEntryByOffset(punk, -cItemsBack, &pte))) { pte->GetDisplayName(szName, ARRAYSIZE(szName), TEGDN_FORSYSTEM); ASSERT(*szName); FixAmpersands(szName, ARRAYSIZE(szName)); InsertMenu(hmenu, iIns, uFlags, idLast, szName); cAdded++; TraceMsg(TF_TRAVELLOG, "TL[%X]::IME Back id = %d, szName = %s", this, idLast, szName); } cItemsBack--; idLast--; } TraceMsg(TF_TRAVELLOG, "TL[%X]::InsertMenuEntries exiting added = %d", this, cAdded); return cAdded ? S_OK : S_FALSE; } DWORD CTravelLog::CountEntries(IUnknown *punk) { CTravelEntry *pte = _pteRoot; DWORD dw = 0; BOOL fAllowLocalAnchor = TRUE; while (pte) { if (pte->CanInvoke(punk, fAllowLocalAnchor)) dw++; fAllowLocalAnchor = fAllowLocalAnchor && pte->IsLocalAnchor(); pte = pte->GetNext(); } TraceMsg(TF_TRAVELLOG, "TL[%X]::CountEntries count = %d", this, dw); return dw; } HRESULT CTravelLog::Revert(void) { // this function should only be called when // we have travelled, and we stop the travel before finishing if (_pteUpdate) { // trade them back _pteCurrent = _pteUpdate; _pteUpdate = NULL; return S_OK; } return E_FAIL; } // // delete nodes belonging to the frameset pte // void CTravelLog::_DeleteFrameSetEntry(IUnknown *punk, CTravelEntry *pte) { ASSERT(pte); CTravelEntry *ptetmp = pte; BOOL fAllowLocalAnchor = TRUE; while (ptetmp && ptetmp != _pteCurrent) ptetmp = ptetmp->GetNext(); if (ptetmp) { // entry on left of _pteCurrent , delete on left do { if (pte == _pteRoot) _pteRoot = pte->GetNext(); ptetmp = pte; pte = pte->GetPrev(); fAllowLocalAnchor = fAllowLocalAnchor && ptetmp->IsLocalAnchor(); _cbTotalSize -= ptetmp->Size(); ptetmp->RemoveSelf(); } while (pte && !(pte->CanInvoke(punk, fAllowLocalAnchor))); } else if (pte) { do { ptetmp = pte; pte = pte->GetNext(); fAllowLocalAnchor = fAllowLocalAnchor && ptetmp->IsLocalAnchor(); _cbTotalSize -= ptetmp->Size(); ptetmp->RemoveSelf(); } while (pte && !(pte->CanInvoke(punk, fAllowLocalAnchor))); } } //+--------------------------------------------------------------------------- // // Method : CTravelLog::DeleteIndexEntry // // Interface : ITravelLogEx // // Synopsis : Delete the entry given by index. // //---------------------------------------------------------------------------- HRESULT CTravelLog::DeleteIndexEntry(IUnknown *punk, int index) { HRESULT hres = E_FAIL; CTravelEntry *pte; IBrowserService *pbs; BOOL fAllowLocalAnchor = TRUE; ASSERT(punk); if (index == 0) // don't delete current entry return hres; hres = _FindEntryByOffset(punk, index, &pte); if (SUCCEEDED(hres)) { _DeleteFrameSetEntry(punk, pte); ASSERT(_cbTotalSize == _pteRoot->ListSize()); if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs)))) { pbs->UpdateBackForwardState(); pbs->Release(); } } return hres; } //+--------------------------------------------------------------------------- // // Method : CTravelLog::DeleteUrlEntry // // Interface : ITravelLogEx // // Synopsis : Delete all the entries given by URL. Fails for current entry. // //---------------------------------------------------------------------------- HRESULT CTravelLog::DeleteUrlEntry(IUnknown *punk, UINT uiCP, LPOLESTR lpszUrl) { HRESULT hres = E_FAIL; CTravelEntry *pte; IBrowserService *pbs; LPITEMIDLIST pidl; BOOL fAllowLocalAnchor = TRUE; int count = 0; ASSERT(punk); if (SUCCEEDED(IEParseDisplayNameWithBCW(uiCP, lpszUrl, NULL, &pidl))) { // delete only if different from current if (!_pteCurrent->IsEqual(pidl)) { hres = S_OK; while(SUCCEEDED(_FindEntryByPidl(punk, pidl, &pte))) { _DeleteFrameSetEntry(punk, pte); count++; ASSERT(_cbTotalSize == _pteRoot->ListSize()); } } ILFree(pidl); if (count && SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs)))) { pbs->UpdateBackForwardState(); pbs->Release(); } } return hres; } //+--------------------------------------------------------------------------- // // Method : CTravelLog::DeleteEntry // // Interface : ITravelLogEx // // Synopsis : Delete the entries given by punk. Fails for current entry. // //---------------------------------------------------------------------------- HRESULT CTravelLog::DeleteEntry(IUnknown *punk, ITravelLogEntry *ptleDelete) { HRESULT hres; CTravelEntry *pte; BOOL fAllowLocalAnchor = TRUE; IBrowserService *pbs; ASSERT(punk); hres = _FindEntryByPunk(punk, ptleDelete, &pte); if (SUCCEEDED(hres) && pte != _pteCurrent) // don't remove current { _DeleteFrameSetEntry(punk, pte); ASSERT(_cbTotalSize == _pteRoot->ListSize()); if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs)))) { pbs->UpdateBackForwardState(); pbs->Release(); } } else { hres = E_FAIL; } return hres; } //+--------------------------------------------------------------------------- // // Method : CTravelLog::CountEntryNodes // // Synopsis : Counts Back/Forward entries including the current one // as given by dwFlags // //---------------------------------------------------------------------------- HRESULT CTravelLog::CountEntryNodes(IUnknown *punk, DWORD dwFlags, DWORD *pdwCount) { CTravelEntry *pte; BOOL fAllowLocalAnchor = TRUE; ASSERT(punk); DWORD dwCount = 0; if (!_pteCurrent) { *pdwCount = 0; return S_OK; } if (IsFlagSet(dwFlags, TLMENUF_BACK)) { pte = _pteRoot; while (pte != _pteCurrent) { if (pte->CanInvoke(punk, fAllowLocalAnchor)) { dwCount++; fAllowLocalAnchor = fAllowLocalAnchor && pte->IsLocalAnchor(); } pte = pte->GetNext(); } } if (IsFlagSet(dwFlags, TLMENUF_INCLUDECURRENT)) { if (_pteCurrent->CanInvoke(punk, fAllowLocalAnchor)) { dwCount++; fAllowLocalAnchor = fAllowLocalAnchor && _pteCurrent->IsLocalAnchor(); } } if (IsFlagSet(dwFlags, TLMENUF_FORE)) { pte = _pteCurrent->GetNext(); while (pte) { if (pte->CanInvoke(punk, fAllowLocalAnchor)) { dwCount++; fAllowLocalAnchor = fAllowLocalAnchor && pte->IsLocalAnchor(); } pte = pte->GetNext(); } } *pdwCount = dwCount; TraceMsg(TF_TRAVELLOG, "TL[%X]::CountEntryNodes count = %d", this, *pdwCount); return S_OK; } //+--------------------------------------------------------------------------- // // Method : CTravelLog::CreateEnumEntry // // Synopsis : Returns an enumerator object for the back/fore travel entries // as selected by the dwFlags option // //---------------------------------------------------------------------------- HRESULT CTravelLog::CreateEnumEntry(IUnknown *punk, IEnumTravelLogEntry **ppEnum, DWORD dwFlags) { ASSERT(punk); ASSERT(ppEnum); *ppEnum = 0; HRESULT hr = E_OUTOFMEMORY; CEnumEntry *penum = new CEnumEntry(); if (penum) { penum->Init(this, punk, 0, dwFlags); *ppEnum = SAFECAST(penum, IEnumTravelLogEntry *); hr = S_OK; } return hr; } //+--------------------------------------------------------------------------- // // Method : CTravelLogEx::InsertEntry // // Synopsis : Inserts an entry into the specified position in the // travellog and calls Update with the given IUnknown. // //---------------------------------------------------------------------------- HRESULT CTravelLog::InsertEntry(IUnknown *punkBrowser, ITravelLogEntry *pteRelativeTo, BOOL fPrepend, IUnknown* punkTLClient, ITravelLogEntry **ppEntry) { TraceMsg(TF_TRAVELLOG, "TL[%X]::InsertEntry", this); ASSERT(punkBrowser); CTravelEntry * pteRelative; _FindEntryByPunk(punkBrowser, pteRelativeTo, &pteRelative); if (!pteRelative) pteRelative = _pteCurrent; CTravelEntry *pte = new CTravelEntry(FALSE); if (!pte) return E_OUTOFMEMORY; if (fPrepend) { // keep relative alive as it's reconnected pteRelative->AddRef(); pte->SetPrev(pteRelative->GetPrev()); pteRelative->SetPrev(pte); if (pteRelative == _pteRoot) { _pteRoot = pte; } } else { CTravelEntry * pteNext = pteRelative->GetNext(); if (pteNext) pteNext->AddRef(); pte->SetNext(pteNext); pteRelative->SetNext(pte); } // update will fill in all the data from the passed in TL Client HRESULT hres = pte->Update(punkTLClient, FALSE); _cbTotalSize += pte->Size(); ASSERT(_cbTotalSize == _pteRoot->ListSize()); IBrowserService *pbs; if (SUCCEEDED(punkBrowser->QueryInterface(IID_PPV_ARG(IBrowserService, &pbs)))) { pbs->UpdateBackForwardState(); pbs->Release(); hres = S_OK; } // return the ITLEntry for the new entry if (SUCCEEDED(hres) && ppEntry) { hres = pte->QueryInterface(IID_PPV_ARG(ITravelLogEntry, ppEntry)); } return hres; } //+--------------------------------------------------------------------------- // // Method : CTravelLog::TravelToEntry // // Synopsis : Travels directly to the specified entry. // Invoke cannot be called directly due to update strangeness. // //---------------------------------------------------------------------------- HRESULT CTravelLog::TravelToEntry( IUnknown *punkBrowser, ITravelLogEntry *pteDestination) { HRESULT hr = E_FAIL; CTravelEntry *pte = NULL; ASSERT(punkBrowser); ASSERT(pteDestination); _FindEntryByPunk(punkBrowser, pteDestination , &pte); if (pte) { if (!_pteCurrent->IsExternal() && !_pteUpdate) _pteUpdate = _pteCurrent; _pteCurrent = pte; hr = pte->Invoke(punkBrowser); if (FAILED(hr)) { Revert(); } } return hr; } HRESULT CreateTravelLog(ITravelLog **pptl) { HRESULT hres; CTravelLog *ptl = new CTravelLog(); if (ptl) { hres = ptl->QueryInterface(IID_PPV_ARG(ITravelLog, pptl)); ptl->Release(); } else { *pptl = NULL; hres = E_OUTOFMEMORY; } return hres; } CEnumEntry::CEnumEntry() :_cRef(1) { ASSERT(!_ptl); ASSERT(!_punk); TraceMsg(TF_TRAVELLOG, "EET[%X] created ", this); } CEnumEntry::~CEnumEntry() { SAFERELEASE(_ptl); SAFERELEASE(_punk); TraceMsg(TF_TRAVELLOG, "EET[%X] destroyed ", this); } HRESULT CEnumEntry::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CEnumEntry, IEnumTravelLogEntry), { 0 }, }; return QISearch(this, qit, riid, ppvObj); } ULONG CEnumEntry::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CEnumEntry::Release() { ASSERT(0 != _cRef); ULONG cRef = InterlockedDecrement(&_cRef); if (0 == cRef) { delete this; } return cRef; } void CEnumEntry::Init(CTravelLog *ptl, IUnknown *punk, DWORD dwOffset, DWORD dwFlags) { ASSERT(ptl); ASSERT(punk); _ptl = ptl; _dwFlags = dwFlags; _punk = punk; _dwOffset = dwOffset; _lStart = 0; _ptl->AddRef(); _punk->AddRef(); SetBase(); } void CEnumEntry::SetBase() { ITravelEntry *ptetmp; // the start is always computed relative to the current entry if (IsFlagSet(_dwFlags, TLEF_RELATIVE_FORE|TLEF_RELATIVE_BACK)) { _lStart = -1; while (SUCCEEDED(_ptl->GetTravelEntry(_punk, _lStart, &ptetmp))) { _lStart--; ptetmp->Release(); } _lStart++; } else if (!IsFlagSet(_dwFlags, TLEF_RELATIVE_INCLUDE_CURRENT)) _lStart = IsFlagSet(_dwFlags, TLEF_RELATIVE_BACK) ? -1: 1; } HRESULT CEnumEntry::Reset() { _dwOffset = 0; // base changes when add/delete entries SetBase(); return S_OK; } HRESULT CEnumEntry::Skip(ULONG cElt) { HRESULT hres; ITravelEntry *pte; ULONG uCount; LONG lIndex; BOOL fToRight = IsFlagSet(_dwFlags, TLEF_RELATIVE_FORE); BOOL fIncludeCurrent = IsFlagSet(_dwFlags, TLEF_RELATIVE_INCLUDE_CURRENT); for (uCount = 0; uCount < cElt; uCount++) { lIndex = fToRight ? _lStart + _dwOffset : _lStart - _dwOffset; if (lIndex || fIncludeCurrent) { if (SUCCEEDED(hres = _ptl->GetTravelEntry(_punk, lIndex, &pte))) { _dwOffset++; pte->Release(); } else break; } else { _dwOffset++; uCount--; } } if (uCount != cElt) hres = S_FALSE; return hres; } HRESULT CEnumEntry::Next(ULONG cElt, ITravelLogEntry **rgpte2, ULONG *pcEltFetched) { HRESULT hres = S_OK; ULONG uCount = 0; ITravelEntry *pte; LONG lIndex; BOOL fToRight; BOOL fIncludeCurrent; fToRight = IsFlagSet(_dwFlags, TLEF_RELATIVE_FORE); fIncludeCurrent = IsFlagSet(_dwFlags, TLEF_RELATIVE_INCLUDE_CURRENT); if (pcEltFetched) *pcEltFetched = 0; for (uCount = 0; uCount < cElt; uCount++) { lIndex = fToRight ? _lStart + _dwOffset : _lStart - _dwOffset; if (lIndex || fIncludeCurrent) { hres = _ptl->GetTravelEntry(_punk, lIndex, &pte); if (SUCCEEDED(hres)) { _dwOffset++; pte->QueryInterface(IID_PPV_ARG(ITravelLogEntry, &rgpte2[uCount])); pte->Release(); } else break; } else { _dwOffset++; uCount--; } } if (pcEltFetched ) *pcEltFetched = uCount; if (uCount != cElt) { hres = S_FALSE; for(;uCount < cElt; uCount++) rgpte2[uCount] = 0; } return hres; } STDMETHODIMP CEnumEntry::Clone(IEnumTravelLogEntry **ppEnum) { HRESULT hres = E_OUTOFMEMORY; CEnumEntry *penum; ASSERT(ppEnum); *ppEnum = 0; penum = new CEnumEntry(); if (penum) { penum->Init(_ptl, _punk, _dwOffset, _dwFlags); *ppEnum = SAFECAST(penum, IEnumTravelLogEntry*); hres = S_OK; } return hres; } // Helper object for creating new travel entries class CPublicTravelLogCreateHelper : public ITravelLogClient, IPersistHistory { public: // *** IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef() ; STDMETHODIMP_(ULONG) Release(); // ITravelLogClient methods STDMETHODIMP FindWindowByIndex(DWORD dwID, IUnknown **ppunk); STDMETHODIMP GetWindowData(WINDOWDATA *pwindata); STDMETHODIMP LoadHistoryPosition(LPOLESTR pszUrlLocation, DWORD dwCookie) { return SetPositionCookie(dwCookie); } // IPersist methods. (dummy) STDMETHODIMP GetClassID(CLSID *pClassID) { ASSERT(FALSE); return E_NOTIMPL; } // IPersistHistory methods. (dummy) // These should never be called, but Update QI's the client to see if it supports IPH. STDMETHODIMP LoadHistory(IStream *pStream, IBindCtx *pbc) { return E_NOTIMPL; } STDMETHODIMP SaveHistory(IStream *pStream) { ASSERT(FALSE); return S_OK; } STDMETHODIMP SetPositionCookie(DWORD dwPositioncookie) { return E_NOTIMPL; } STDMETHODIMP GetPositionCookie(DWORD *pdwPositioncookie) { return E_NOTIMPL; } CPublicTravelLogCreateHelper(IBrowserService *pbs, LPCOLESTR pszUrl, LPCOLESTR pszTitle); protected: ~CPublicTravelLogCreateHelper(); LONG _cRef; IBrowserService *_pbs; LPOLESTR _pszUrl; LPOLESTR _pszTitle; }; CPublicTravelLogCreateHelper::CPublicTravelLogCreateHelper(IBrowserService *pbs, LPCOLESTR pszUrl, LPCOLESTR pszTitle) : _pbs(pbs), _cRef(1) { ASSERT(_pbs); ASSERT(!_pszUrl); ASSERT(!_pszTitle); ASSERT(pszUrl); ASSERT(pszTitle); if (_pbs) _pbs->AddRef(); if (pszUrl) { SHStrDup(pszUrl, &_pszUrl); } if (pszTitle) { SHStrDup(pszTitle, &_pszTitle); } TraceMsg(TF_TRAVELLOG, "TPLCH[%X] created", this); } CPublicTravelLogCreateHelper::~CPublicTravelLogCreateHelper() { SAFERELEASE(_pbs); CoTaskMemFree(_pszUrl); CoTaskMemFree(_pszTitle); TraceMsg(TF_TRAVELLOG, "TPLCH[%X] destroyed ", this); } HRESULT CPublicTravelLogCreateHelper ::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CPublicTravelLogCreateHelper , ITravelLogClient), QITABENT(CPublicTravelLogCreateHelper , IPersistHistory), QITABENT(CPublicTravelLogCreateHelper , IPersist), { 0 }, }; return QISearch(this, qit, riid, ppvObj); } ULONG CPublicTravelLogCreateHelper ::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CPublicTravelLogCreateHelper ::Release() { ASSERT(0 != _cRef); ULONG cRef = InterlockedDecrement(&_cRef); if (0 == cRef) { delete this; } return cRef; } // ITravelLogClient::FindWindowByIndex HRESULT CPublicTravelLogCreateHelper::FindWindowByIndex(DWORD dwID, IUnknown **ppunk) { *ppunk = NULL; return E_NOTIMPL; } // ITravelLogClient::GetWindowData // turns around and talks to the browser HRESULT CPublicTravelLogCreateHelper::GetWindowData(WINDOWDATA *pwindata) { ITravelLogClient2 *pcli; HRESULT hres = _pbs->QueryInterface(IID_PPV_ARG(ITravelLogClient2, &pcli)); if (SUCCEEDED(hres)) hres = pcli->GetDummyWindowData(_pszUrl, _pszTitle, pwindata); if (pcli) pcli->Release(); return SUCCEEDED(hres) ? S_OK : E_FAIL; } // Implements the publicly exposed interface ITravelLogStg (can be QS'd for from top browser) class CPublicTravelLog : public ITravelLogStg { public: // *** IUnknown STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj); STDMETHODIMP_(ULONG) AddRef() ; STDMETHODIMP_(ULONG) Release(); // *** ITravelLogStg specific methods STDMETHODIMP CreateEntry(LPCOLESTR pszUrl, LPCOLESTR pszTitle, ITravelLogEntry *ptleRelativeTo, BOOL fPrepend, ITravelLogEntry **pptle); STDMETHODIMP TravelTo(ITravelLogEntry *ptle); STDMETHODIMP EnumEntries(TLENUMF flags, IEnumTravelLogEntry **ppenum); STDMETHODIMP FindEntries(TLENUMF flags, LPCOLESTR pszUrl, IEnumTravelLogEntry **ppenum); STDMETHODIMP GetCount(TLENUMF flags, DWORD *pcEntries); STDMETHODIMP RemoveEntry(ITravelLogEntry *ptle); STDMETHODIMP GetRelativeEntry(int iOffset, ITravelLogEntry **ptle); CPublicTravelLog(IBrowserService *pbs, ITravelLogEx *ptlx); protected: ~CPublicTravelLog(); LONG _cRef; IBrowserService *_pbs; ITravelLogEx *_ptlx; }; CPublicTravelLog::CPublicTravelLog(IBrowserService *pbs, ITravelLogEx *ptlx) : _pbs(pbs), _ptlx(ptlx), _cRef(1) { ASSERT(pbs); ASSERT(ptlx); // We don't addref _pbs because we are always contained within the browser serivce, // so avoid the circular ref. if (_ptlx) _ptlx->AddRef(); TraceMsg(TF_TRAVELLOG, "TLP[%X] created", this); } CPublicTravelLog::~CPublicTravelLog() { // We don't need to release _pbs because we are always contained within the browser serivce, // so we didn't addref to avoid the circular ref so don't release SAFERELEASE(_ptlx); TraceMsg(TF_TRAVELLOG, "TLP[%X] destroyed ", this); } HRESULT CPublicTravelLog::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CPublicTravelLog, ITravelLogStg), // IID_ITravelLogStg { 0 }, }; return QISearch(this, qit, riid, ppvObj); } ULONG CPublicTravelLog::AddRef() { return InterlockedIncrement(&_cRef); } ULONG CPublicTravelLog::Release() { ASSERT(0 != _cRef); ULONG cRef = InterlockedDecrement(&_cRef); if (0 == cRef) { delete this; } return cRef; } //+--------------------------------------------------------------------------- // // Method : CPublicTravelLog::CreateEntry // // Interface : ITravelLogStg // // Synopsis : Insert a new dummy entry. // Creates an entry in the travel log and passes CPTHCEHelper // as travel log client; that gets called back and fills in the // data from the browser. // //---------------------------------------------------------------------------- HRESULT CPublicTravelLog::CreateEntry(LPCOLESTR pszUrl, LPCOLESTR pszTitle, ITravelLogEntry *ptleRelativeTo, BOOL fPrepend, ITravelLogEntry **pptle) { HRESULT hres = E_FAIL; CPublicTravelLogCreateHelper * ptlch; ITravelLogClient *ptlc; ptlch = new CPublicTravelLogCreateHelper(_pbs, pszUrl, pszTitle); if (!ptlch) return E_OUTOFMEMORY; ptlc = SAFECAST(ptlch, ITravelLogClient *); if (ptlc) { // Create TLogEntry and have it get its data from the helper. hres = _ptlx->InsertEntry(_pbs, ptleRelativeTo, fPrepend, ptlc, pptle); } ptlc->Release(); return hres; } HRESULT CPublicTravelLog::TravelTo(ITravelLogEntry *ptle) { if (ptle) return _ptlx->TravelToEntry(_pbs, ptle); else return E_POINTER; } //+--------------------------------------------------------------------------- // // Method : CPublicTravelLog::EnumEntries // // Interface : ITravelLogStg // // Synopsis : Get an enumerators for specific entries given by flags. // Flags should match with those used by ITravelLogEx! // //---------------------------------------------------------------------------- HRESULT CPublicTravelLog::EnumEntries(TLENUMF flags, IEnumTravelLogEntry **ppenum) { return _ptlx->CreateEnumEntry(_pbs, ppenum, flags); } //+--------------------------------------------------------------------------- // // Method : CPublicTravelLog::FindEntries // // Interface : ITravelLogStg // // Synopsis : Allow to retrieve duplicate entries. // Flags should match with those used by ITravelLogEx! // //---------------------------------------------------------------------------- HRESULT CPublicTravelLog::FindEntries(TLENUMF flags, LPCOLESTR pszUrl, IEnumTravelLogEntry **ppenum) { return E_NOTIMPL; } //+--------------------------------------------------------------------------- // // Method : CPublicTravelLog::GetCount // // Interface : ITravelLogStg // // Synopsis : Public methods to get ITravelLogEx count. // Flags should match with those used by ITravelLogEx! // //---------------------------------------------------------------------------- HRESULT CPublicTravelLog::GetCount(TLENUMF flags, DWORD *pcEntries) { return _ptlx->CountEntryNodes(_pbs, flags, pcEntries); } //+--------------------------------------------------------------------------- // // Method : CPublicTravelLog::RemoveEntry // // Interface : ITravelLogStg // // Synopsis : Delete the entry ant its frameset. // //---------------------------------------------------------------------------- HRESULT CPublicTravelLog::RemoveEntry(ITravelLogEntry *ptle) { HRESULT hr = E_FAIL; if (ptle) hr = _ptlx->DeleteEntry(_pbs, ptle); return hr; } HRESULT CPublicTravelLog::GetRelativeEntry(int iOffset, ITravelLogEntry **pptle) { HRESULT hr = E_FAIL; ITravelEntry* pte; ITravelLog* ptl; if (SUCCEEDED(_ptlx->QueryInterface(IID_PPV_ARG(ITravelLog, &ptl)))) { hr = ptl->GetTravelEntry(_pbs, iOffset, &pte); if (SUCCEEDED(hr) && pte) { hr = pte->QueryInterface(IID_PPV_ARG(ITravelLogEntry, pptle)); pte->Release(); } ptl->Release(); } return hr; } // public method used by the browser to create us HRESULT CreatePublicTravelLog(IBrowserService *pbs, ITravelLogEx* ptlx, ITravelLogStg **pptlstg) { HRESULT hres; CPublicTravelLog *ptlp = new CPublicTravelLog(pbs, ptlx); if (ptlp) { hres = ptlp->QueryInterface(IID_PPV_ARG(ITravelLogStg, pptlstg)); ptlp->Release(); } else { *pptlstg = NULL; hres = E_OUTOFMEMORY; } return hres; }