|
|
#include "priv.h"
#ifdef FEATURE_PICS
#include "asyncrat.h"
#include <ratings.h>
#include "dochost.h"
#include <mshtmdid.h>
/* There is a PicsQuery structure in the following global array for each
* outstanding query. It records the address of the PicsData structure in * the corresponding w3doc, the window handle corresponding to the Mwin, * and a serial number. This way, RatingObtainQueryCallback can tell if * the page the query corresponds to still exists, before posting a message; * and PicsDataMessageLoop can tell if the doc still exists when the message * finally gets delivered. * * The array is dynamically allocated and is protected by the main HTML * critical section. */
HDSA g_haQueries = NULL; DWORD g_dwPicsSerial = 1L; const UINT c_cQueryAllocSize = 8; /* should be plenty by default */ UINT g_crefQueries = 0;
/* AddPicsQuery - add an outstanding PICS query to the list, given a window
* handle to send a completion message to. Returns the serial number of the * query for later reference. */ DWORD _AddPicsQuery(HWND hwnd) { ENTERCRITICAL; DWORD dwRet = 0; if (g_haQueries == NULL) { g_haQueries = DSA_Create(sizeof(PicsQuery), c_cQueryAllocSize); } if (g_haQueries != NULL) { PicsQuery q; q.dwSerial = ::g_dwPicsSerial++; q.hwnd = hwnd; q.lpvRatingDetails = NULL; if (DSA_InsertItem(g_haQueries, DA_LAST, &q) >= 0) dwRet = q.dwSerial; } LEAVECRITICAL; return dwRet; }
/* RemovePicsQuery - remove an outstanding query based on its serial number.
*/ void _RemovePicsQuery(DWORD dwSerial) { ENTERCRITICAL; if (g_haQueries != NULL) { UINT cQueries = DSA_GetItemCount(g_haQueries); PicsQuery *pq = NULL; for (UINT i=0; i<cQueries; i++) { pq = (PicsQuery *)DSA_GetItemPtr(g_haQueries, i); if (pq != NULL && pq->dwSerial == dwSerial) break; } if (pq != NULL) { if (pq->lpvRatingDetails != NULL) ::RatingFreeDetails(pq->lpvRatingDetails); DSA_DeleteItem(g_haQueries, i); } } LEAVECRITICAL; }
/* GetPicsQuery - get a copy of an outstanding PICS query record, given its
* serial number. Returns TRUE if found. */ BOOL _GetPicsQuery(DWORD dwSerial, PicsQuery *pOut) { ENTERCRITICAL; PicsQuery *pq = NULL; if (g_haQueries != NULL) { UINT cQueries = DSA_GetItemCount(g_haQueries); for (UINT i=0; i<cQueries; i++) { pq = (PicsQuery *)DSA_GetItemPtr(g_haQueries, i); if (pq != NULL && pq->dwSerial == dwSerial) break; } if (pq != NULL) { *pOut = *pq; pq->lpvRatingDetails = NULL; /* caller's copy owns this now */ } } LEAVECRITICAL; return pq != NULL; }
/* _RefPicsQueries - add a reference to the async query array */ void _RefPicsQueries(void) { ENTERCRITICAL;
++g_crefQueries;
LEAVECRITICAL; }
/* _ReleasePicsQueries - cleanup all memory associated with outstanding queries
*/ void _ReleasePicsQueries(void) { ENTERCRITICAL; if (!--g_crefQueries) { if (g_haQueries != NULL) { UINT cQueries = DSA_GetItemCount(g_haQueries); for (UINT i=0; i<cQueries; i++) { PicsQuery *pq = (PicsQuery *)DSA_GetItemPtr(g_haQueries, i); if (pq != NULL && pq->lpvRatingDetails != NULL) { RatingFreeDetails(pq->lpvRatingDetails); } } DSA_Destroy(g_haQueries); g_haQueries = NULL; // leave g_dwPicsSerial as it is, just in case we start up again
} } LEAVECRITICAL; }
/* PostPicsMessage - formats up a custom window message to signal that a
* query is complete. Format is WM_PICS_STATUS(hresult,dwSerial). Other * information (the rating details blob obtained from RatingCheckUserAccess) * is stored in the query record for safekeeping. * * Returns TRUE if a message was posted successfully to the right window. */ BOOL _PostPicsMessage(DWORD dwSerial, HRESULT hr, LPVOID lpvRatingDetails) { BOOL fRet = FALSE; ENTERCRITICAL; if (g_haQueries != NULL) { PicsQuery *pq = NULL; UINT cQueries = DSA_GetItemCount(g_haQueries); for (UINT i=0; i<cQueries; i++) { pq = (PicsQuery *)DSA_GetItemPtr(g_haQueries, i); if (pq != NULL && pq->dwSerial == dwSerial) break; } if (pq != NULL) { pq->lpvRatingDetails = lpvRatingDetails; fRet = PostMessage(pq->hwnd, WM_PICS_ASYNCCOMPLETE, (WPARAM)hr, (LPARAM)dwSerial); if (!fRet) { /* oops, couldn't post message, don't keep copy of details */ pq->lpvRatingDetails = NULL; } } } LEAVECRITICAL; return fRet; }
/* Class CPicsRootDownload manages the download of the root document of a
* site, to get ratings from it. */
CPicsRootDownload::CPicsRootDownload(IOleCommandTarget *pctParent, BOOL fFrameIsOffline, BOOL fFrameIsSilent) { m_cRef = 1; m_pctParent = pctParent; m_pctParent->AddRef(); m_pole = NULL; m_pctObject = NULL; m_pBinding = NULL; m_fFrameIsOffline = fFrameIsOffline ? TRUE : FALSE; m_fFrameIsSilent = fFrameIsSilent ? TRUE : FALSE; }
CPicsRootDownload::~CPicsRootDownload() { ATOMICRELEASE(m_pctParent);
CleanUp();
ATOMICRELEASE(m_pBinding);
ATOMICRELEASE(m_pBindCtx); }
HRESULT CPicsRootDownload::StartDownload(IMoniker *pmk) { IUnknown *punk = NULL; HRESULT hr;
hr = CreateBindCtx(0, &m_pBindCtx); if (FAILED(hr)) goto LErrExit;
/*
hr = m_pBindCtx->RegisterObjectParam(BROWSER_OPTIONS_OBJECT_NAME, (IBrowseControl *)this); if (FAILED(hr)) goto LErrExit; */
//
// Associate the client site as an object parameter to this
// bind context so that Trident can pick it up while processing
// IPersistMoniker::Load().
//
m_pBindCtx->RegisterObjectParam(WSZGUID_OPID_DocObjClientSite, SAFECAST(this, IOleClientSite*));
hr = RegisterBindStatusCallback(m_pBindCtx, (IBindStatusCallback *)this, 0, 0L); if (FAILED(hr)) goto LErrExit;
hr = pmk->BindToObject(m_pBindCtx, NULL, IID_IUnknown, (LPVOID*)&punk);
if (SUCCEEDED(hr) || hr==E_PENDING) { hr = S_OK;
//
// If moniker happen to return the object synchronously, emulate
// OnDataAvailable callback and OnStopBinding.
//
if (punk) { OnObjectAvailable(IID_IUnknown, punk); OnStopBinding(hr, NULL); punk->Release(); } } else { /* OnStopBinding can be called by URLMON within the BindToObject
* call in some cases. So, don't call it ourselves if it's * already been called (we can tell by looking whether our * bind context still exists). */ if (m_pBindCtx != NULL) { OnStopBinding(hr, NULL); } }
LErrExit: if (FAILED(hr) && (m_pBindCtx != NULL)) { m_pBindCtx->Release(); m_pBindCtx = NULL; }
return hr; }
/* _NotifyEndOfDocument is used in all the error cases to make sure the caller
* gets a notification of some sort. The case where this function does not * send a notification is if we have a valid OLE object -- in that case, we're * assuming that we have it because we know it supports PICS, therefore we're * expecting it to send such a notification to the parent itself. */ void CPicsRootDownload::_NotifyEndOfDocument(void) { if (m_pole == NULL) { if (m_pctParent != NULL) { m_pctParent->Exec(&CGID_ShellDocView, SHDVID_NOMOREPICSLABELS, 0, NULL, NULL); } } }
HRESULT CPicsRootDownload::_Abort() { if (m_pBinding) { return m_pBinding->Abort(); } return S_FALSE; }
void CPicsRootDownload::CleanUp() { _Abort();
if (m_pctObject != NULL) { VARIANTARG v; v.vt = VT_UNKNOWN; v.punkVal = NULL; m_pctObject->Exec(&CGID_ShellDocView, SHDVID_CANSUPPORTPICS, 0, &v, NULL); m_pctObject->Exec(NULL, OLECMDID_STOP, NULL, NULL, NULL); ATOMICRELEASE(m_pctObject); }
LPOLECLIENTSITE pcs; if (m_pole && SUCCEEDED(m_pole->GetClientSite(&pcs)) && pcs) { if (pcs == SAFECAST(this, LPOLECLIENTSITE)) { m_pole->SetClientSite(NULL); } pcs->Release(); }
ATOMICRELEASE(m_pole); }
// IUnknown members
STDMETHODIMP CPicsRootDownload::QueryInterface(REFIID riid, void **punk) { *punk = NULL;
if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IsPicsBrowser)) *punk = (IUnknown *)(IBindStatusCallback *)this; else if (IsEqualIID(riid, IID_IBindStatusCallback)) *punk = (IBindStatusCallback *)this; else if (IsEqualIID(riid, IID_IOleClientSite)) *punk = (IOleClientSite *)this; else if (IsEqualIID(riid, IID_IServiceProvider)) *punk = (IServiceProvider *)this; else if (IsEqualIID(riid, IID_IDispatch)) *punk = (IDispatch *)this;
if (*punk != NULL) { ((IUnknown *)(*punk))->AddRef(); return S_OK; } return E_NOINTERFACE; }
STDMETHODIMP_(ULONG) CPicsRootDownload::AddRef(void) { ++m_cRef; TraceMsg(TF_SHDREF, "CPicsRootDownload(%x)::AddRef called, new m_cRef=%d", this, m_cRef); return m_cRef; }
STDMETHODIMP_(ULONG) CPicsRootDownload::Release(void) { UINT crefNew = --m_cRef;
TraceMsg(TF_SHDREF, "CPicsRootDownload(%x)::Release called, new m_cRef=%d", this, m_cRef);
if (!crefNew) delete this;
return crefNew; }
// IBindStatusCallback methods
STDMETHODIMP CPicsRootDownload::OnStartBinding(DWORD dwReserved, IBinding* pbinding) { if (m_pBinding != NULL) m_pBinding->Release();
m_pBinding = pbinding;
if (m_pBinding != NULL) m_pBinding->AddRef();
return S_OK; }
STDMETHODIMP CPicsRootDownload::GetPriority(LONG* pnPriority) { return E_NOTIMPL; }
STDMETHODIMP CPicsRootDownload::OnLowResource(DWORD dwReserved) { return E_NOTIMPL; }
STDMETHODIMP CPicsRootDownload::OnProgress(ULONG ulProgress, ULONG ulProgressMax, ULONG ulStatusCode, LPCWSTR pwzStatusText) { /* If the root document's data type is not HTML, don't try to get any
* ratings out of it, just abort. */ if (ulStatusCode == BINDSTATUS_CLASSIDAVAILABLE) { BOOL fContinueDownload = FALSE;
CLSID clsid; // CLSIDFromString is prototyped wrong, non const first param
HRESULT hresT = CLSIDFromString((WCHAR *)pwzStatusText, &clsid); if (SUCCEEDED(hresT)) { LPWSTR pwzProgID = NULL; hresT = ProgIDFromCLSID(clsid, &pwzProgID); if (SUCCEEDED(hresT)) { if (StrCmp(pwzProgID, L"htmlfile") == 0) { fContinueDownload = TRUE; } OleFree(pwzProgID); } }
if (!fContinueDownload) { _Abort(); } }
return S_OK; }
STDMETHODIMP CPicsRootDownload::OnStopBinding(HRESULT hrResult, LPCWSTR szError) { /* Some of the cleanup we do in here (RevokeObjectParam is suspect?) could
* remove our last reference, causing the Releases at the end to fault. * Guard against this with an AddRef/Release. Dochost does this too. * * WARNING - if URLMON is calling back through this object, shouldn't he * have a reference to us? If so, where is it? */ AddRef();
/* Notify the caller that we've got to the end of the document */ _NotifyEndOfDocument(); m_pBindCtx->RevokeObjectParam(WSZGUID_OPID_DocObjClientSite); ::RevokeBindStatusCallback(m_pBindCtx, (IBindStatusCallback *)this); ATOMICRELEASE(m_pBinding); ATOMICRELEASE(m_pBindCtx);
/* Undo above AddRef(). */ Release();
return S_OK; }
void SetBindfFlagsBasedOnAmbient(BOOL fAmbientOffline, DWORD *pgrfBindf);
STDMETHODIMP CPicsRootDownload::GetBindInfo(DWORD* pgrfBINDF, BINDINFO* pbindInfo) { if ( !pgrfBINDF || !pbindInfo || !pbindInfo->cbSize ) return E_INVALIDARG;
*pgrfBINDF = BINDF_ASYNCHRONOUS | BINDF_ASYNCSTORAGE; *pgrfBINDF |= BINDF_GETNEWESTVERSION;
if(m_fFrameIsSilent) { *pgrfBINDF |= BINDF_NO_UI; } else { *pgrfBINDF &= ~BINDF_NO_UI; }
SetBindfFlagsBasedOnAmbient(BOOLIFY(m_fFrameIsOffline), pgrfBINDF); // clear BINDINFO except cbSize
DWORD cbSize = pbindInfo->cbSize; ZeroMemory( pbindInfo, cbSize ); pbindInfo->cbSize = cbSize;
pbindInfo->dwBindVerb = BINDVERB_GET;
return S_OK; }
STDMETHODIMP CPicsRootDownload::OnDataAvailable(DWORD grfBSCF, DWORD dwSize, FORMATETC *pfmtetc, STGMEDIUM* pstgmed) { return E_NOTIMPL; }
STDMETHODIMP CPicsRootDownload::OnObjectAvailable(REFIID riid, IUnknown* punk) { if (SUCCEEDED(punk->QueryInterface(IID_IOleCommandTarget, (LPVOID *)&m_pctObject))) { VARIANTARG v; v.vt = VT_UNKNOWN; v.punkVal = (IOleCommandTarget *)m_pctParent; HRESULT hresT = m_pctObject->Exec(&CGID_ShellDocView, SHDVID_CANSUPPORTPICS, 0, &v, NULL); if (hresT == S_OK) { hresT = punk->QueryInterface(IID_IOleObject, (LPVOID *)&m_pole); if (FAILED(hresT)) m_pole = NULL; } }
if (m_pole == NULL) { ATOMICRELEASE(m_pctObject); _Abort(); }
return S_OK; }
// IOleClientSite
STDMETHODIMP CPicsRootDownload::SaveObject(void) { return E_NOTIMPL; }
STDMETHODIMP CPicsRootDownload::GetMoniker(DWORD, DWORD, IMoniker **) { return E_NOTIMPL; }
STDMETHODIMP CPicsRootDownload::GetContainer(IOleContainer **) { return E_NOTIMPL; }
STDMETHODIMP CPicsRootDownload::ShowObject(void) { return E_NOTIMPL; }
STDMETHODIMP CPicsRootDownload::OnShowWindow(BOOL fShow) { return E_NOTIMPL; }
STDMETHODIMP CPicsRootDownload::RequestNewObjectLayout(void) { return E_NOTIMPL; }
// IServiceProvider (must be QI'able from IOleClientSite)
STDMETHODIMP CPicsRootDownload::QueryService(REFGUID guidService, REFIID riid, void **ppvObj) { if (IsEqualGUID(guidService, SID_STopLevelBrowser)) { if (IsEqualIID(riid, IID_IsPicsBrowser)) return QueryInterface(riid, ppvObj); return E_NOINTERFACE; }
return E_FAIL; }
// IDispatch
HRESULT CPicsRootDownload::Invoke(DISPID dispidMember, REFIID iid, LCID lcid, WORD wFlags, DISPPARAMS FAR* pdispparams, VARIANT FAR* pVarResult,EXCEPINFO FAR* pexcepinfo,UINT FAR* puArgErr) { if (!pVarResult) return E_INVALIDARG;
if (wFlags == DISPATCH_PROPERTYGET) { switch (dispidMember) { case DISPID_AMBIENT_DLCONTROL : // We support IDispatch so that Trident can ask us to control the
// download. By specifying all the following flags, and by NOT
// specifying DLCTL_DLIMAGES, DLCTL_VIDEOS, or DLCTL_BGSOUNDS,
// we ensure we only download the HTML doc itself, and not a lot
// of associated things that aren't going to help us find a META
// tag.
pVarResult->vt = VT_I4; pVarResult->lVal = DLCTL_SILENT | DLCTL_NO_SCRIPTS | DLCTL_NO_JAVA | DLCTL_NO_RUNACTIVEXCTLS | DLCTL_NO_DLACTIVEXCTLS | DLCTL_NO_FRAMEDOWNLOAD | DLCTL_NO_CLIENTPULL; break; default: return DISP_E_MEMBERNOTFOUND; } return S_OK; }
return DISP_E_MEMBERNOTFOUND; }
#endif /* FEATURE_PICS */
|