///
/// Copyright © 2003-2008 JetBrains s.r.o.
/// You may distribute under the terms of the GNU General Public License, as published by the Free Software Foundation, version 2 (see License.txt in the repository root folder).
///
// JetBrains Omea Mshtml Browser Component
//
// Implements the Web browser component wrapping with full-scale customization, including view options and security settings & zones.
// Consists of an unmanaged part (C++ ATL, raw hosting, a composite ActiveX control) and a managed part (JScript.NET, Windows Forms control around the unmanaged ActiveX control plus AbstractWebBrowser proxy-inheritor).
// The unmanaged parts server as a wrapper for the custom interfaces only, and should not carry out any meaningful processing. All the events should be delegated to the managed part for processing.
//
// This file belongs to the unmanaged part and defines the CMshtmlBrowser class.
// This class implements the control part of the composite ActiveX control, that is, enables hosting in the ActiveX containers. Also, it holds all the On… functions that invoke the managed wrapper part. This invocation simulates the OLE Events mechanism with the sole difference that it is capable of using the properties and does not support multicasting (delivering to multiple consumers).
//
// © JetBrains Inc, 2004
// Written by (H) Serge Baltic
//
#include "stdafx.h"
#include "MshtmlBrowser.h"
#include "MshtmlHostWindow.h"
#include ".\mshtmlbrowser.h"
const GUID CMshtmlBrowser::CGID_IWebBrowser = { 0xED016940L,0xBD5B,0x11cf, { 0xBA, 0x4E,0x00,0xC0,0x4F,0xD7,0x08,0x16 } };
// Declare the trace category for verbose CMshtmlBrowser output
//CTraceCategory TRACE_VERBOSE( _T("MshtmlVerboseTrace"), 4 );
// CMshtmlBrowser
CMshtmlBrowser::CMshtmlBrowser()
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
m_bWindowOnly = true;
m_pSite = NULL;
// Initialize the stock properties
m_bEnabled = true;
m_nBorderStyle = 0; // No border
m_bTabStop = true;
// UserMode init
m_bUserMode = false; // In Design Mode by default
// Security settings
m_nPermissionSet = PS_Nothing; // Disallow all by default
m_nSecurityZone = 0;
}
CMshtmlBrowser::~CMshtmlBrowser()
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
}
_COM_SMARTPTR_TYPEDEF(IAxWinAmbientDispatch, __uuidof(IAxWinAmbientDispatch));
void CMshtmlBrowser::TryInstantiate()
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
if(m_oBrowser != NULL)
return;
AtlAxWinInit();
CComPtr spUnkContainer;
// Create the host window for MSHTML (if it does not exist yet)
if(m_pSite == NULL)
{
CComObject* theHostWindow;
CHECK(CComObject::CreateInstance(&theHostWindow));
m_pSite = theHostWindow; // Store it for accessing later
m_pSite->SetControlPart(this); // Tell who is the control around the inlying container (for callbacks and settings)
}
spUnkContainer = (IUnknown*)(IOleClientSite*)m_pSite; // Get IUnknown-pointer to the container
//_com_util::CheckError(CMshtmlHostWindow::_CreatorClass::CreateInstance(NULL, IID_IUnknown, (void**)&spUnkContainer)); // Old creation, as it exists in the internals of ATL
//theHostWindow->SetAmbientDispatch(this); // Set the ambient-dispatch to be queried for the ambient container properties through the IDispatch::Invoke
if(m_oParentCallback != NULL)
m_pSite->SetAmbientDispatch(m_oParentCallback);
CComPtr pAxWindow;
COM_CHECK(spUnkContainer, QueryInterface(IID_IAxWinHostWindow, (void**)&pAxWindow));
IAxWinAmbientDispatchPtr oIEHostDisp = (IUnknown*)pAxWindow;
IUnknownPtr oIEUnk;
//COM_CHECK(pAxWindow, CreateControlEx(L"Mozilla.Browser", m_hWnd, NULL, &oIEUnk, IID_NULL, NULL)); // mAzila
COM_CHECK(pAxWindow, CreateControlEx(L"about:blank", m_hWnd, NULL, &oIEUnk, IID_NULL, NULL)); // MSHTML
m_oBrowser = oIEUnk;
//pAxWindow->SetExternalDispatch(static_cast(this)); // Set by the IDocHostUIHandler
oIEHostDisp->put_OptionKeyPath(L"Software\\JetBrains\\Omea\\Internet Explorer");
// Sink the browser's events
IWebBrowserEventsSinkImpl::DispEventAdvise( m_oBrowser, &DIID_DWebBrowserEvents );
IWebBrowserEvents2SinkImpl::DispEventAdvise( m_oBrowser, &DIID_DWebBrowserEvents2 );
// Set the UserMode/DesignMode
CHECK(GetAmbientUserMode(m_bUserMode));
IAxWinAmbientDispatchPtr oAmbientWin = (IUnknown*)spUnkContainer;
if(oAmbientWin != NULL)
{
TRACE(_T("Setting UserMode to %d\n"), m_bUserMode);
COM_CHECK(oAmbientWin, put_UserMode(m_bUserMode ? VARIANT_TRUE : VARIANT_FALSE));
}
else
TRACE(_T("Cannot set UserMode to %d\n"), m_bUserMode);
// Signal that the browser has been created and can be used from now on
OnBrowserCreated();
}
STDMETHODIMP CMshtmlBrowser::get_AmbientDlControl( LONG *pVal )
{
*pVal = DLCTL_DLIMAGES | DLCTL_VIDEOS | DLCTL_BGSOUNDS; // No restrictions by default
// Query the ParentCallback about special settings imposed on the browser, if available
if(m_oParentCallback != NULL)
{
try
{
// Declarations for the invocations
DISPID dispid;
OLECHAR FAR* szMember;
DISPPARAMS dispparams = { NULL, NULL, 0, 0 };
_variant_t vtRet;
// ShowImages (also affects the other multimedia stuff)
try
{
szMember = L"ShowImages";
COM_CHECK(m_oParentCallback, GetIDsOfNames( IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid));
COM_CHECK(m_oParentCallback, Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispparams, &vtRet, NULL, NULL));
if( (bool)vtRet )
*pVal &= ~( DLCTL_DLIMAGES | DLCTL_VIDEOS | DLCTL_BGSOUNDS ); // Set these three flags off
}
COM_CATCH();
// Disable scripts and java and some related stuff if we're working in the "Nothing" permission set
if( m_nPermissionSet == PS_Nothing )
*pVal |= DLCTL_NO_SCRIPTS | DLCTL_NO_JAVA | DLCTL_NO_DLACTIVEXCTLS | DLCTL_NO_RUNACTIVEXCTLS | DLCTL_NO_FRAMEDOWNLOAD | DLCTL_NO_BEHAVIORS | DLCTL_NO_CLIENTPULL;
// Silent Mode (no UI pops up during the download)
try
{
szMember = L"SilentMode";
COM_CHECK(m_oParentCallback, GetIDsOfNames( IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid));
COM_CHECK(m_oParentCallback, Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispparams, &vtRet, NULL, NULL));
if( (bool)vtRet )
*pVal |= DLCTL_SILENT; // Set this flag on
}
COM_CATCH();
// Force Offline Mode ()
try
{
szMember = L"OfflineMode";
COM_CHECK(m_oParentCallback, GetIDsOfNames( IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid));
COM_CHECK(m_oParentCallback, Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispparams, &vtRet, NULL, NULL));
if( (bool)vtRet )
*pVal |= DLCTL_FORCEOFFLINE; // Set this flag on
}
COM_CATCH();
}
COM_CATCH();
}
TRACE(_T("[OMEA.MSHTML] DLCONTROL"));
return S_OK;
}
STDMETHODIMP CMshtmlBrowser::Navigate(BSTR URI)
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
if(m_oBrowser == NULL) // Not created yet
return E_UNEXPECTED;
try
{
TryInstantiate();
m_oBrowser->Navigate(URI);
}
COM_CATCH_RETURN();
return S_OK;
}
STDMETHODIMP CMshtmlBrowser::ShowHtml(IStream* HtmlData)
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
TryUIDeactivate();
try
{
TryInstantiate();
if(m_oBrowser->Document == NULL) // Not navigated to a page yet
return E_UNEXPECTED;
// Feed the browser with data
COM_CHECK(IPersistStreamInitPtr(m_oBrowser->Document), Load(HtmlData));
}
COM_CATCH_RETURN();
return S_OK;
}
STDMETHODIMP CMshtmlBrowser::ShowHtmlText(BSTR HtmlText)
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
try
{
TryInstantiate();
if(m_oBrowser->Document == NULL) // Not navigated to a page yet
return E_UNEXPECTED;
UINT nCbStringLen = ocslen(HtmlText) * sizeof(OLECHAR);
UINT nCreateSize = nCbStringLen + 2; // Reserve some place for the BOM (byte-order-mark)
HGLOBAL hGlobal = GlobalAlloc(GMEM_MOVEABLE, nCreateSize);
if (hGlobal)
{
CComPtr spStream;
BYTE* pBytes = (BYTE*) GlobalLock(hGlobal);
pBytes[0] = 0xFF; // Write the BOM, byte 1
pBytes[1] = 0xFE; // Write the BOM, byte 2
memcpy(pBytes + 2, HtmlText, nCbStringLen);
GlobalUnlock(hGlobal);
HRESULT hr = CreateStreamOnHGlobal(hGlobal, TRUE, &spStream);
if(SUCCEEDED(hr))
{
// Try the main loading scheme (thru the IPersistInit interface)
hr = ShowHtml(spStream);
// If the main scheme has failed, try the backup one
if(FAILED(hr))
{
TRACE(_T("The main content uploading scheme has failed for the browser (%#010X), falling back to simple upload."), hr);
m_oBrowser->Navigate(L"about:blank"); // Clean up
while(m_oBrowser->ReadyState < 4) // Wait until cleanup completes
Sleep(10);
IDispatchPtr oDoc = m_oBrowser->Document;
DISPID dispid;
OLECHAR FAR* szMember = L"write";
COM_CHECK(m_oParentCallback, GetIDsOfNames( IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid));
_variant_t avtParams[] = { _variant_t(HtmlText) };
DISPPARAMS dispparams = { avtParams, NULL, sizeof(avtParams) / sizeof(*avtParams), 0 };
COM_CHECK(m_oParentCallback, Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, NULL, NULL, NULL));
}
}
else
{
GlobalFree(hGlobal);
_com_util::CheckError(hr);
}
}
}
COM_CATCH_RETURN();
return S_OK;
}
STDMETHODIMP CMshtmlBrowser::TranslateAccelerator(MSG *pMsg)
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
if(!m_bUserMode)
return S_FALSE; // No keyboard processing in Design Mode
// Check if window deactivation has occured for us or the underlying control, and perform ui-deactivation in this case
if(pMsg->message == WM_KILLFOCUS)
UIDeactivate();
// The underlying control has the first chance to crack at the Windows messages received by this window
try
{
// See if we should invoke the callback (do not make calls for ordinary keys to improve performance)
/* // TODO: check if this drops the performance significantly
bool bCallback = false;
if(pMsg->message == WM_KEYDOWN)
{
// Special processing for the spacebar key
if(pMsg->wParam == 0x20)
bCallback = true;
// If a modifier key is pressed with an ordinary key
else if((GetKeyState(VK_CONTROL) & 0x8000) || (GetKeyState(VK_MENU) & 0x8000))
bCallback = true;
}
else if(pMsg->message == WM_SYSKEYDOWN)
bCallback = true;
*/
// Callback!
//if((bCallback) && (OnBeforeKeyDown((long)pMsg->wParam, !!(GetKeyState(VK_CONTROL) & 0x8000), !!(GetKeyState(VK_MENU) & 0x8000), !!(GetKeyState(VK_SHIFT) & 0x8000))))
// return S_OK; // Handled by us, do not let to go on
// Call a handler for key-down messages
if(((pMsg->message == WM_KEYDOWN) || (pMsg->message == WM_SYSKEYDOWN)) && (OnBeforeKeyDown((long)pMsg->wParam, !!(GetKeyState(VK_CONTROL) & 0x8000), !!(GetKeyState(VK_MENU) & 0x8000), !!(GetKeyState(VK_SHIFT) & 0x8000))))
return S_OK; // Handled by us, do not let it go on to MSHTML
// Call a handler for key-up messages
if(((pMsg->message == WM_KEYUP) || (pMsg->message == WM_SYSKEYUP)) && (OnBeforeKeyUp((long)pMsg->wParam, !!(GetKeyState(VK_CONTROL) & 0x8000), !!(GetKeyState(VK_MENU) & 0x8000), !!(GetKeyState(VK_SHIFT) & 0x8000))))
return S_OK;
// Call a handler for char messages
if(((pMsg->message == WM_CHAR) || (pMsg->message == WM_SYSCHAR)) && (OnBeforeKeyPress((long)pMsg->wParam, !!(GetKeyState(VK_CONTROL) & 0x8000), !!(GetKeyState(VK_MENU) & 0x8000), !!(GetKeyState(VK_SHIFT) & 0x8000))))
return S_OK;
/*if( (m_oBrowser != NULL) &&
(((pMsg->message >= WM_KEYFIRST) && (pMsg->message <= WM_KEYLAST)) ||
((pMsg->message >= WM_MOUSEFIRST) && (pMsg->message <= WM_MOUSELAST))) )*/
if(((IOleInPlaceActiveObjectPtr)m_oBrowser)->TranslateAccelerator(pMsg) == S_OK) // Check if the message has been processed by the control
{
TRACE(_T("[OMEA.MSHTML] Message was processed by the browser.\n"));
return S_OK;
}
else
TRACE(_T("[OMEA.MSHTML] Message was NOT processed by the browser.\n"));
}
COM_CATCH();
// Delegate processing to the parent
return IOleInPlaceActiveObjectImpl::TranslateAccelerator(pMsg);
}
STDMETHODIMP CMshtmlBrowser::DoVerb( LONG iVerb, /* Value representing verb to be performed */ LPMSG lpmsg, /* Pointer to structure that describes Windows */ /* message */ IOleClientSite *pActiveSite, /* Pointer to active client site */ LONG lindex, /* Reserved */ HWND hwndParent, /* Handle of window containing the object */ LPCRECT lprcPosRect /* Pointer to object's display rectangle */ )
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
TRACE(_T("[MSHTML] Doing Verb %d\r\n"), iVerb); // TODO: remove
try
{
// Call the base class implementation that applies to this very control
IOleObjectImpl::DoVerb(iVerb, lpmsg, pActiveSite, lindex, hwndParent, lprcPosRect);
// Create the inlying control if necessary
TryInstantiate();
// Delegate verb processing to the inlying control
if(m_oBrowser != NULL)
COM_CHECK(((IOleObjectPtr)m_oBrowser), DoVerb(iVerb, lpmsg, pActiveSite, lindex, hwndParent, lprcPosRect));
}
COM_CATCH_RETURN();
return S_OK;
}
STDMETHODIMP CMshtmlBrowser::EnumVerbs( IEnumOLEVERB **ppEnumOleVerb /* Address of output variable that receives the IEnumOleVerb interface pointer*/ )
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
try
{
// Create the inlying control if necessary
TryInstantiate();
// Delegate operation to the inlying control
if(m_oBrowser != NULL)
COM_CHECK(((IOleObjectPtr)m_oBrowser), EnumVerbs(ppEnumOleVerb));
}
COM_CATCH_RETURN();
return S_OK;
}
STDMETHODIMP CMshtmlBrowser::SetHostNames( /* [in] */ LPCOLESTR szContainerApp, /* [unique][in] */ LPCOLESTR szContainerObj)
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
try
{
// Create the inlying control if necessary
TryInstantiate();
// Delegate operation to the inlying control
if(m_oBrowser != NULL)
COM_CHECK(((IOleObjectPtr)m_oBrowser), SetHostNames(szContainerApp, szContainerObj));
}
COM_CATCH_RETURN();
return S_OK;
}
STDMETHODIMP CMshtmlBrowser::InitFromData( /* [unique][in] */ IDataObject *pDataObject, /* [in] */ BOOL fCreation, /* [in] */ DWORD dwReserved)
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
try
{
// Create the inlying control if necessary
TryInstantiate();
// Delegate operation to the inlying control
if(m_oBrowser != NULL)
COM_CHECK(((IOleObjectPtr)m_oBrowser), InitFromData(pDataObject, fCreation, dwReserved));
}
COM_CATCH_RETURN();
return S_OK;
}
STDMETHODIMP CMshtmlBrowser::GetClipboardData( /* [in] */ DWORD dwReserved, /* [out] */ IDataObject **ppDataObject)
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
try
{
// Create the inlying control if necessary
TryInstantiate();
// Delegate operation to the inlying control
if(m_oBrowser != NULL)
COM_CHECK(((IOleObjectPtr)m_oBrowser), GetClipboardData(dwReserved, ppDataObject));
}
COM_CATCH_RETURN();
return S_OK;
}
STDMETHODIMP CMshtmlBrowser::Update( void )
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
try
{
// Create the inlying control if necessary
TryInstantiate();
// Delegate operation to the inlying control
if(m_oBrowser != NULL)
COM_CHECK(((IOleObjectPtr)m_oBrowser), Update());
}
COM_CATCH_RETURN();
return S_OK;
}
STDMETHODIMP CMshtmlBrowser::IsUpToDate( void)
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
try
{
// Create the inlying control if necessary
TryInstantiate();
// Delegate operation to the inlying control
if(m_oBrowser != NULL)
COM_CHECK(((IOleObjectPtr)m_oBrowser), IsUpToDate());
}
COM_CATCH_RETURN();
return S_OK;
}
STDMETHODIMP CMshtmlBrowser::SetColorScheme( /* [in] */ LOGPALETTE *pLogpal)
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
try
{
// Create the inlying control if necessary
TryInstantiate();
// Delegate operation to the inlying control
if(m_oBrowser != NULL)
COM_CHECK(((IOleObjectPtr)m_oBrowser), SetColorScheme(pLogpal));
}
COM_CATCH_RETURN();
return S_OK;
}
STDMETHODIMP CMshtmlBrowser::get_HtmlDocument(IDispatch** pVal)
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
*pVal = NULL;
try
{
// Create the inlying control if necessary
TryInstantiate();
if(m_oBrowser == NULL)
return E_UNEXPECTED;
// Check if this is actually an HTML document
IHTMLDocument2Ptr oDoc = m_oBrowser->Document;
// Get the document pointer
if(oDoc == NULL)
return S_OK; // Returns NULL
return oDoc->QueryInterface(__uuidof(IDispatch), (void**)pVal);
}
COM_CATCH_RETURN();
}
STDMETHODIMP CMshtmlBrowser::ExecDocumentCommand(BSTR Command, VARIANT_BOOL PromptUser)
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
try
{
// Create the inlying control if necessary
TryInstantiate();
// Get the command target pointer
IOleCommandTargetPtr oCmdTarget;
if((m_oBrowser != NULL) && (m_oBrowser->Document != NULL))
oCmdTarget = m_oBrowser->Document;
else
return E_UNEXPECTED;
DWORD nCmdId = 0;
// Choose the command id based on the command passed in
_bstr_t bsCommand = Command;
if(bsCommand == _bstr_t(L"ViewSource") )
nCmdId = HTMLID_VIEWSOURCE;
else if(bsCommand == _bstr_t(L"Find"))
nCmdId = HTMLID_FIND;
else
return Error(_T("Unknown browser command."));
TRACE(_T("[OMEA.MSHTML] Execing the browser command\n"));
// Execute the command, do not prompt user
COM_CHECK(oCmdTarget, Exec(&CGID_IWebBrowser, nCmdId, (PromptUser != VARIANT_FALSE ? OLECMDEXECOPT_PROMPTUSER : OLECMDEXECOPT_DONTPROMPTUSER), NULL, NULL));
}
COM_CATCH_RETURN();
return S_OK;
}
STDMETHODIMP CMshtmlBrowser::get_Browser(IDispatch** pVal)
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
try
{
TryInstantiate(); // Try creating the control, if not created yet
if(m_oBrowser == NULL) // Should not happen
return E_UNEXPECTED;
// Make QI on the browser object and return a reference
return m_oBrowser->QueryInterface(__uuidof(IDispatch), (void**)pVal);
}
COM_CATCH_RETURN();
}
/// Deactivates an active in-place object and discards the object's undo state.
STDMETHODIMP CMshtmlBrowser::InPlaceDeactivate()
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
TRACE(_T("InPlaceDeactivate\n"));
try
{
if(m_oBrowser == NULL)
return E_FAIL; // Must be already created if deactivating ;)
// Disconnect the events sink from the Browser
IWebBrowserEventsSinkImpl::DispEventUnadvise( m_oBrowser, &DIID_DWebBrowserEvents );
IWebBrowserEvents2SinkImpl::DispEventUnadvise( m_oBrowser, &DIID_DWebBrowserEvents2 );
// Delegate processing to the inlying control
if(m_oBrowser != NULL)
COM_CHECK(((IOleInPlaceObjectPtr)m_oBrowser), InPlaceDeactivate());
// Call the base to process deactivation on our composite control
IOleInPlaceObjectWindowlessImpl::InPlaceDeactivate();
}
COM_CATCH_RETURN();
return S_OK;
}
/// Deactivates and removes the user interface that supports in-place activation.
STDMETHODIMP CMshtmlBrowser::UIDeactivate()
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
TRACE(_T("UIDeactivate\n"));
try
{
if(m_oBrowser == NULL)
return E_FAIL; // Must be already created if deactivating ;)
// Delegate processing to the inlying control
if(m_oBrowser != NULL)
COM_CHECK(((IOleInPlaceObjectPtr)m_oBrowser), UIDeactivate());
// Call the base to process deactivation on our composite control
IOleInPlaceObjectWindowlessImpl::UIDeactivate();
}
COM_CATCH_RETURN();
return S_OK;
}
/// Reactivates a previously deactivated object, undoing the last state of the object.
STDMETHODIMP CMshtmlBrowser::ReactivateAndUndo()
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
try
{
if(m_oBrowser == NULL)
return E_FAIL; // Must be already created if deactivating ;)
// Delegate processing to the inlying control
if(m_oBrowser != NULL)
COM_CHECK(((IOleInPlaceObjectPtr)m_oBrowser), ReactivateAndUndo());
// Call the base to process deactivation on our composite control
IOleInPlaceObjectWindowlessImpl::ReactivateAndUndo();
}
COM_CATCH_RETURN();
return S_OK;
}
void CMshtmlBrowser::TryUIDeactivate()
{
//ATLTRACE2(TRACE_VERBOSE, 4, __FUNCTION__);
ASSERT(m_pSite != NULL);
if(m_pSite == NULL)
return; // Not initialized yet
// If we're not UI-Active already, then nothing to check as we cannot get more-ui-deactivated than now anyway ;)
if(!m_pSite->IsUIActive())
return;
// The window that currently has focus
HWND hWnd = ::GetFocus();
// Walk up the windows hierarchy to detect whether the focus resides somewhere beneath our window
while(hWnd != NULL)
{
if(hWnd == m_hWnd)
break;
hWnd = ::GetParent(hWnd);
}
// If we have not met our window during the walk, then someone else has focus
// Or, maybe, we just could not get who has the focus and had a NULL initially
// The control should be UI-activated no more
if(hWnd == NULL)
UIDeactivate(); // Invoke the deactivation
}
STDMETHODIMP CMshtmlBrowser::get_ParentCallback(IDispatch** pVal)
{
if(m_oParentCallback == NULL) // Not set or already released
{
*pVal = NULL;
return S_OK;
}
return m_oParentCallback->QueryInterface(__uuidof(IDispatch), (void**)pVal);
}
STDMETHODIMP CMshtmlBrowser::put_ParentCallback(IDispatch* newVal)
{
if(newVal == NULL)
return E_POINTER;
// Store for calling back upon requests
m_oParentCallback = newVal;
// Attach for checking for the control site ambient properties
if(m_pSite != NULL)
m_pSite->SetAmbientDispatch(newVal);
return S_OK;
}
STDMETHODIMP CMshtmlBrowser::get_PermissionSet(BSTR* pVal)
{
_bstr_t bsRet;
switch(m_nPermissionSet)
{
case PS_Auto:
bsRet = L"Auto";
break;
case PS_Nothing:
bsRet = L"Nothing";
break;
case PS_Everything:
bsRet = L"Everything";
break;
case PS_Zone:
{
TCHAR szBuf[0x400];
StringCchPrintf(szBuf, sizeof(szBuf) / sizeof(*szBuf), _T("#%d"), m_nSecurityZone);
bsRet = szBuf;
}
break;
default:
return Error(_T("A fatal internal error has occured: unexpected switch value."));
}
*pVal = bsRet.copy();
return S_OK;
}
STDMETHODIMP CMshtmlBrowser::put_PermissionSet(BSTR newVal)
{
_bstr_t bsVal(newVal);
if(newVal == _bstr_t(L"Auto"))
m_nPermissionSet = PS_Auto;
else if(newVal == _bstr_t(L"Nothing"))
m_nPermissionSet = PS_Nothing;
else if(newVal == _bstr_t(L"Everything"))
m_nPermissionSet = PS_Everything;
else
{
/*LPCTSTR szVal = bsVal;
if(szVal[0] != _T('#'))
return E_INVALIDARG;
scanf(*/
if(newVal[0] != L'#')
return E_INVALIDARG;
m_nSecurityZone = _wtol(newVal + 1);
m_nPermissionSet = PS_Zone;
}
return S_OK;
}
void CMshtmlBrowser::OnStatusTextChange( BSTR text )
{
if(m_oParentCallback != NULL)
{
//TRACE((LPCTSTR)(CA2T((LPCSTR)(__FUNCTION__))));
try
{
DISPID dispid;
OLECHAR FAR* szMember = L"OnStatusTextChange";
COM_CHECK(m_oParentCallback, GetIDsOfNames( IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid));
_variant_t avtParams[] = { _variant_t(text) };
DISPPARAMS dispparams = { avtParams, NULL, sizeof(avtParams) / sizeof(*avtParams), 0 };
COM_CHECK(m_oParentCallback, Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, NULL, NULL, NULL));
}
COM_CATCH();
}
}
void CMshtmlBrowser::OnTitleChange( BSTR text )
{
if(m_oParentCallback != NULL)
{
TRACE((LPCTSTR)(CA2T((LPCSTR)(__FUNCTION__))));
try
{
DISPID dispid;
OLECHAR FAR* szMember = L"OnTitleChange";
COM_CHECK(m_oParentCallback, GetIDsOfNames( IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid));
_variant_t avtParams[] = { _variant_t(text) };
DISPPARAMS dispparams = { avtParams, NULL, sizeof(avtParams) / sizeof(*avtParams), 0 };
COM_CHECK(m_oParentCallback, Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, NULL, NULL, NULL));
}
COM_CATCH();
}
}
void CMshtmlBrowser::OnBeforeNavigate1( BSTR /*URL*/, long /*Flags*/, BSTR /*TargetFrameName*/, VARIANT* /*PostData*/, BSTR /*Headers*/, VARIANT_BOOL *Cancel)
{
TRACE(_T("[OMEA.MSHTML] OnBeforeNavigate1\n"));
*Cancel = VARIANT_TRUE;
return;
}
void CMshtmlBrowser::OnBeforeNavigate2( LPDISPATCH pDisp, VARIANT *url, VARIANT * /*Flags*/, VARIANT *TargetFrameName, VARIANT *PostData, VARIANT *Headers, VARIANT_BOOL *Cancel )
{
TRACE(_T("[OMEA.MSHTML] OnBeforeNavigate2\n"));
//*Cancel = VARIANT_TRUE;
//return;
if(m_oParentCallback != NULL)
{
TRACE((LPCTSTR)(CA2T((LPCSTR)(__FUNCTION__))));
try
{
// Check if the event applies to the main browser window
SHDocVw::IWebBrowser2Ptr oSourceBrowser = pDisp;
if((SHDocVw::IWebBrowser2*)oSourceBrowser != (SHDocVw::IWebBrowser2*)m_oBrowser)
return; // The event was fired by whatever child frame
// Invoke the callback
DISPID dispid;
OLECHAR FAR* szMember = L"OnBeforeNavigate";
COM_CHECK(m_oParentCallback, GetIDsOfNames( IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid));
_variant_t avtParams[] = { Headers, PostData, TargetFrameName, url };
DISPPARAMS dispparams = { avtParams, NULL, sizeof(avtParams) / sizeof(*avtParams), 0 };
_variant_t vtRet;
COM_CHECK(m_oParentCallback, Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, &vtRet, NULL, NULL));
// Check the return value, if any
*Cancel = (bool)vtRet ? VARIANT_TRUE : VARIANT_FALSE; // If the value cannot be coerced to bool, it throws (that means the return value was not a bool, eg empty)
TRACE(_T("[OMEA.MSHTML] OnBeforeNavigate return value is %d."), (int)(short)*Cancel);
}
COM_CATCH();
}
}
void CMshtmlBrowser::OnNavigateComplete( LPDISPATCH pDisp, VARIANT* URL )
{
if(m_oParentCallback != NULL)
{
TRACE((LPCTSTR)(CA2T((LPCSTR)(__FUNCTION__))));
try
{
// Check if the event applies to the main browser window
SHDocVw::IWebBrowser2Ptr oSourceBrowser = pDisp;
if((SHDocVw::IWebBrowser2*)oSourceBrowser != (SHDocVw::IWebBrowser2*)m_oBrowser)
return; // The event was fired by whatever child frame
// Invoke the callback
DISPID dispid;
OLECHAR FAR* szMember = L"OnNavigateComplete";
COM_CHECK(m_oParentCallback, GetIDsOfNames( IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid));
_variant_t avtParams[] = { URL };
DISPPARAMS dispparams = { avtParams, NULL, sizeof(avtParams) / sizeof(*avtParams), 0 };
COM_CHECK(m_oParentCallback, Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, NULL, NULL, NULL));
}
COM_CATCH();
}
}
void CMshtmlBrowser::OnNavigateError( IDispatch* pDisp, VARIANT* URL, VARIANT* Frame, VARIANT* StatusCode, VARIANT_BOOL* Cancel )
{
if(m_oParentCallback != NULL)
{
TRACE((LPCTSTR)(CA2T((LPCSTR)(__FUNCTION__))));
try
{
// Check if the event applies to the main browser window
SHDocVw::IWebBrowser2Ptr oSourceBrowser = pDisp;
if((SHDocVw::IWebBrowser2*)oSourceBrowser != (SHDocVw::IWebBrowser2*)m_oBrowser)
return; // The event was fired by whatever child frame
// Invoke the callback
DISPID dispid;
OLECHAR FAR* szMember = L"OnNavigateError";
COM_CHECK(m_oParentCallback, GetIDsOfNames( IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid));
_variant_t avtParams[] = { StatusCode, Frame, URL };
DISPPARAMS dispparams = { avtParams, NULL, sizeof(avtParams) / sizeof(*avtParams), 0 };
_variant_t vtRet;
COM_CHECK(m_oParentCallback, Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, &vtRet, NULL, NULL));
// Check the return value, if any
*Cancel = (bool)vtRet ? VARIANT_TRUE : VARIANT_FALSE; // If the value cannot be coerced to bool, it throws (that means the return value was not a bool, eg empty)
}
COM_CATCH();
}
}
void CMshtmlBrowser::OnNewWindow( BSTR url, long /*Flags*/, BSTR TargetFrameName, VARIANT * PostData, BSTR Headers, VARIANT_BOOL * Processed)
{
if(m_oParentCallback != NULL)
{
TRACE((LPCTSTR)(CA2T((LPCSTR)(__FUNCTION__))));
try
{
// Invoke the callback
DISPID dispid;
OLECHAR FAR* szMember = L"OnNewWindow";
COM_CHECK(m_oParentCallback, GetIDsOfNames( IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid));
_variant_t avtParams[] = { Headers, PostData, TargetFrameName, url };
DISPPARAMS dispparams = { avtParams, NULL, sizeof(avtParams) / sizeof(*avtParams), 0 };
_variant_t vtRet;
COM_CHECK(m_oParentCallback, Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, &vtRet, NULL, NULL));
// Check the return value, if any
*Processed = (bool)vtRet ? VARIANT_TRUE : VARIANT_FALSE; // If the value cannot be coerced to bool, it throws (that means the return value was not a bool, eg empty)
}
COM_CATCH();
}
}
void CMshtmlBrowser::OnProgressChange( long Progress, long ProgressMax )
{
if(m_oParentCallback != NULL)
{
TRACE((LPCTSTR)(CA2T((LPCSTR)(__FUNCTION__))));
try
{
// Adjust the percentage if it's unavailable
double fProgress;
if(Progress == -1) // Complete
fProgress = 1.0;
else if((Progress == 0) && (ProgressMax == 0)) // Special case, when progress is in range [0..0] :)
fProgress = 1.0;
else
fProgress = (double)Progress / ProgressMax;
// Invoke the callback
DISPID dispid;
OLECHAR FAR* szMember = L"OnProgressChange";
COM_CHECK(m_oParentCallback, GetIDsOfNames( IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid));
_variant_t avtParams[] = { fProgress };
DISPPARAMS dispparams = { avtParams, NULL, sizeof(avtParams) / sizeof(*avtParams), 0 };
COM_CHECK(m_oParentCallback, Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, NULL, NULL, NULL));
}
COM_CATCH();
}
}
void CMshtmlBrowser::OnDocumentComplete( IDispatch* pDisp, VARIANT* URL )
{
if(m_oParentCallback != NULL)
{
TRACE((LPCTSTR)(CA2T((LPCSTR)(__FUNCTION__))));
try
{
// Check if the event applies to the main browser window
SHDocVw::IWebBrowser2Ptr oSourceBrowser = pDisp;
if((SHDocVw::IWebBrowser2*)oSourceBrowser != (SHDocVw::IWebBrowser2*)m_oBrowser)
return; // The event was fired by whatever child frame
// Invoke the callback
DISPID dispid;
OLECHAR FAR* szMember = L"OnDocumentComplete";
COM_CHECK(m_oParentCallback, GetIDsOfNames( IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid));
_variant_t avtParams[] = { URL };
DISPPARAMS dispparams = { avtParams, NULL, sizeof(avtParams) / sizeof(*avtParams), 0 };
COM_CHECK(m_oParentCallback, Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, NULL, NULL, NULL));
}
COM_CATCH();
}
}
void CMshtmlBrowser::OnQuit()
{
// TODO: If you remove the sink unadvising code from the OnDeactivate handler, put it here.
}
void CMshtmlBrowser::OnDownloadComplete()
{
if(m_oParentCallback != NULL)
{
TRACE((LPCTSTR)(CA2T((LPCSTR)(__FUNCTION__))));
try
{
// Invoke the callback
DISPID dispid;
OLECHAR FAR* szMember = L"OnDownloadComplete";
COM_CHECK(m_oParentCallback, GetIDsOfNames( IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid));
DISPPARAMS dispparams = {NULL, NULL, 0, 0};
COM_CHECK(m_oParentCallback, Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, NULL, NULL, NULL));
}
COM_CATCH();
}
}
bool CMshtmlBrowser::OnContextMenu( DWORD dwID, POINT* pptPosition, IUnknown* pCommandTarget, IDispatch* pDispatchObjectHit )
{
if(m_oParentCallback != NULL)
{
TRACE((LPCTSTR)(CA2T((LPCSTR)(__FUNCTION__))));
try
{
UIDeactivate();
// Invoke the callback
DISPID dispid;
OLECHAR FAR* szMember = L"OnContextMenu";
COM_CHECK(m_oParentCallback, GetIDsOfNames( IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid));
_variant_t avtParams[] = { pDispatchObjectHit, pCommandTarget, (long)pptPosition->y, (long)pptPosition->x, (long)dwID };
DISPPARAMS dispparams = { avtParams, NULL, sizeof(avtParams) / sizeof(*avtParams), 0 };
_variant_t vtRet;
COM_CHECK(m_oParentCallback, Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, &vtRet, NULL, NULL));
// Check the return value, if any
return (bool)vtRet; // If the value cannot be coerced to bool, it throws (that means the return value was not a bool, eg empty)
}
COM_CATCH();
}
return false; // Not handled by default
}
bool CMshtmlBrowser::OnBeforeKeyDown( long code, bool ctrl, bool alt, bool shift )
{
return OnBeforeKeyAny(L"OnBeforeKeyDown", code, ctrl, alt, shift);
}
bool CMshtmlBrowser::OnBeforeKeyUp( long code, bool ctrl, bool alt, bool shift )
{
return OnBeforeKeyAny(L"OnBeforeKeyUp", code, ctrl, alt, shift);
}
bool CMshtmlBrowser::OnBeforeKeyPress( long code, bool ctrl, bool alt, bool shift )
{
return OnBeforeKeyAny(L"OnBeforeKeyPress", code, ctrl, alt, shift);
}
bool CMshtmlBrowser::OnBeforeKeyAny( LPCWSTR szFunctionName, long code, bool ctrl, bool alt, bool shift )
{
if(m_oParentCallback != NULL)
{
TRACE((LPCTSTR)(CA2T((LPCSTR)(__FUNCTION__))));
try
{
// Invoke the callback
DISPID dispid;
OLECHAR FAR* szMember = (LPWSTR)szFunctionName;
COM_CHECK(m_oParentCallback, GetIDsOfNames( IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid));
_variant_t avtParams[] = { shift, alt, ctrl, code };
DISPPARAMS dispparams = { avtParams, NULL, sizeof(avtParams) / sizeof(*avtParams), 0 };
_variant_t vtRet;
COM_CHECK(m_oParentCallback, Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, &vtRet, NULL, NULL));
// Check the return value, if any
return (bool)vtRet; // If the value cannot be coerced to bool, it throws (that means the return value was not a bool, eg empty)
}
COM_CATCH();
}
return false; // Not handled by default
}
bool CMshtmlBrowser::OnUrlAction(LPCWSTR pwszUrl, DWORD dwAction, DWORD dwFlags, BYTE *pPolicy)
{
if(m_oParentCallback != NULL)
{
TRACE((LPCTSTR)(CA2T((LPCSTR)(__FUNCTION__))));
try
{
// Invoke the callback
DISPID dispid;
OLECHAR FAR* szMember = L"OnUrlAction";
COM_CHECK(m_oParentCallback, GetIDsOfNames( IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid));
_variant_t avtParams[] = { dwFlags, dwAction, (_bstr_t)pwszUrl };
DISPPARAMS dispparams = { avtParams, NULL, sizeof(avtParams) / sizeof(*avtParams), 0 };
_variant_t vtRet;
COM_CHECK(m_oParentCallback, Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, &vtRet, NULL, NULL));
if( ( V_VT(&vtRet) == VT_ERROR ) || ( V_VT(&vtRet) == VT_EMPTY ) ) // VT_ERROR + DISP_E_… means that the parameter is missing
return false; // Not handled
*pPolicy = (BYTE)(long)vtRet; // Extract the return value, that must be an integer in this case
return true; // we reach this point, we have succeeded.
}
COM_CATCH();
}
return false; // Not handled by default
}
bool CMshtmlBrowser::OnGetHostInfo( DWORD *pFlags )
{
if(m_oParentCallback != NULL)
{
try
{
// Declarations for the invocations
DISPID dispid;
OLECHAR FAR* szMember;
DISPPARAMS dispparams = { NULL, NULL, 0, 0 };
_variant_t vtRet;
szMember = L"AmbientHostUiInfo";
COM_CHECK(m_oParentCallback, GetIDsOfNames( IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid));
COM_CHECK(m_oParentCallback, Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispparams, &vtRet, NULL, NULL));
if( ( V_VT(&vtRet) == VT_ERROR ) || ( V_VT(&vtRet) == VT_EMPTY ) ) // VT_ERROR + DISP_E_… means that the parameter is missing
return false; // Not handled
*pFlags = (DWORD)(long)vtRet; // Extract and convert the property value
return true; // Succeeded
}
COM_CATCH();
}
return false; // Not handled by default
}
void CMshtmlBrowser::OnBrowserCreated()
{
if(m_oParentCallback != NULL)
{
try
{
// Declarations for the invocations
DISPID dispid;
OLECHAR FAR* szMember;
DISPPARAMS dispparams = { NULL, NULL, 0, 0 };
szMember = L"OnBrowserCreated";
COM_CHECK(m_oParentCallback, GetIDsOfNames( IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid));
COM_CHECK(m_oParentCallback, Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparams, NULL, NULL, NULL));
}
COM_CATCH();
}
}
IDispatchPtr CMshtmlBrowser::OnGetExternal()
{
if(m_oParentCallback != NULL)
{
try
{
// Declarations for the invocations
DISPID dispid;
OLECHAR FAR* szMember;
DISPPARAMS dispparams = { NULL, NULL, 0, 0 };
_variant_t vtRet;
szMember = L"ExternalObject";
COM_CHECK(m_oParentCallback, GetIDsOfNames( IID_NULL, &szMember, 1, LOCALE_USER_DEFAULT, &dispid));
COM_CHECK(m_oParentCallback, Invoke(dispid, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &dispparams, &vtRet, NULL, NULL));
TRACE(_T("[MSHTML] The ExternalObject VARIANT type is %#010X.\n"), V_VT(&vtRet));
if(V_VT(&vtRet) == VT_DISPATCH)
return (IDispatch*)vtRet; // Take the dispatch pointer directly
else
return (IDispatchPtr)(IUnknown*)vtRet; // Try to extract IUnknown and query it for IDispatch
}
COM_CATCH();
}
return NULL; // Not handled by default
}
STDMETHODIMP CMshtmlBrowser::QueryExternal(REFIID riid, void**pp)
{
if(m_oParentCallback == NULL)
return E_NOINTERFACE;
return m_oParentCallback->QueryInterface( riid, pp );
}
STDMETHODIMP CMshtmlBrowser::SettingsChanged(void)
{
try
{
TryInstantiate(); // Try to create the browser, if needed
COM_CHECK(((IOleControlPtr)m_oBrowser), OnAmbientPropertyChange(DISPID_AMBIENT_DLCONTROL)); // Notify the web browser that its properties have changed.
}
COM_CATCH_RETURN();
return S_OK;
}
STDMETHODIMP CMshtmlBrowser::GetShortFilePath(BSTR LongPath, BSTR* ShortPath)
{
// TODO: Add your implementation code here
TCHAR sShortPath[MAX_PATH+1];
if( !GetShortPathName( _bstr_t(LongPath), sShortPath, MAX_PATH ) )
return AtlHresultFromLastError(); // Has failed for some reason
*ShortPath = _bstr_t(sShortPath).copy();
return S_OK;
}
STDMETHODIMP CMshtmlBrowser::GetHtmlText(BSTR* Html)
{
try
{
TryInstantiate();
if(m_oBrowser->Document == NULL) // Not navigated to a page yet
return E_UNEXPECTED;
// Create a memory stream
IStreamPtr oStream;
CHECK(CreateStreamOnHGlobal(NULL, TRUE, &oStream));
// Get the content
COM_CHECK(IPersistStreamInitPtr(m_oBrowser->Document), Save(oStream, false));
// Get the size
STATSTG statstg;
ZeroMemory(&statstg, sizeof(statstg));
COM_CHECK(oStream, Stat(&statstg, STATFLAG_NONAME));
// Rewind the stream
LARGE_INTEGER li;
li.QuadPart = 0;
COM_CHECK(oStream, Seek(li, STREAM_SEEK_SET, NULL));
// Preapre the buffer
std::basic_string sHtml;
sHtml.resize((int)ceil(statstg.cbSize.QuadPart * 0.5));
// Read the data
COM_CHECK(oStream, Read(&sHtml[0], (ULONG)statstg.cbSize.QuadPart, NULL));
// Create a basic string
_bstr_t bsRet = sHtml.c_str();
// Return
*Html = bsRet.Detach();
}
COM_CATCH_RETURN();
return S_OK;
}
STDMETHODIMP CMshtmlBrowser::ResurrectWebBrowser()
{
try
{
// Invalidate the browser
m_oBrowser = NULL;
// Try re-creating
TryInstantiate();
}
COM_CATCH_RETURN();
return S_OK;
}