2809 lines
73 KiB
C++
2809 lines
73 KiB
C++
#include "priv.h"
|
|
#include "dspsprt.h"
|
|
#include <hlink.h>
|
|
#include "iface.h"
|
|
#include "resource.h"
|
|
#include <mluisupp.h>
|
|
#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<LeadByte> 0x00]
|
|
// [0x<TrailByte> 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;
|
|
}
|
|
|