/****************************************************************************** Copyright (c) 2000 Microsoft Corporation Module Name: Factory.cpp Abstract: This file contains the implementation of the CPCHElementBehaviorFactory class, which is used to attach binary behaviors to HTML elements. Revision History: Davide Massarenti (dmassare) 06/06/2000 created ******************************************************************************/ #include "stdafx.h" #include #include #include //////////////////////////////////////////////////////////////////////////////// typedef HRESULT (*pfnBehaviorCreator)( /*[in]*/ CPCHHelpCenterExternal* parent, /*[out]*/ IElementBehavior* *ppBehavior ); template class BehaviorCreator { public: static HRESULT CreateInstance( /*[in]*/ CPCHHelpCenterExternal* parent, /*[out]*/ IElementBehavior* *ppBehavior ) { HRESULT hr; CComObject* obj; if(SUCCEEDED(hr = obj->CreateInstance( &obj ))) { obj->AddRef(); obj->Initialize( parent ); hr = obj->QueryInterface( IID_IElementBehavior, (void**)ppBehavior ); obj->Release(); } return hr; } }; struct BehaviorDefinition { LPCWSTR szBehaviorName; LPCWSTR szTagName; pfnBehaviorCreator pfnCreator; }; static const BehaviorDefinition s_Behaviors[] = { // { NULL , L"A", BehaviorCreator ::CreateInstance }, { L"pch_body" , NULL, BehaviorCreator ::CreateInstance }, { L"pch_context" , NULL, BehaviorCreator ::CreateInstance }, { L"pch_events" , NULL, BehaviorCreator ::CreateInstance }, { L"pch_handle" , NULL, BehaviorCreator ::CreateInstance }, { L"pch_hyperlink" , NULL, BehaviorCreator ::CreateInstance }, { L"pch_state" , NULL, BehaviorCreator ::CreateInstance }, { L"pch_subsite" , NULL, BehaviorCreator ::CreateInstance }, { L"pch_tree" , NULL, BehaviorCreator ::CreateInstance }, //////////////////////////////////////////////////////////////////////////////////////// { L"pch_gradient" , NULL, BehaviorCreator::CreateInstance }, { L"pch_bitmap" , NULL, BehaviorCreator ::CreateInstance }, }; //////////////////////////////////////////////////////////////////////////////// CPCHElementBehaviorFactory::CPCHElementBehaviorFactory() { m_parent = NULL; // CPCHHelpCenterExternal* m_parent; } void CPCHElementBehaviorFactory::Initialize( /*[in]*/ CPCHHelpCenterExternal* parent ) { m_parent = parent; } ///////////////////////////////////////////////////////////////////////////// STDMETHODIMP CPCHElementBehaviorFactory::QueryService( REFGUID guidService, REFIID riid, void **ppv ) { HRESULT hr = E_NOINTERFACE; if(InlineIsEqualGUID( riid, IID_IElementBehaviorFactory )) { hr = QueryInterface( riid, ppv ); } else if(InlineIsEqualGUID( riid, IID_IPCHHelpCenterExternal ) && m_parent) { *ppv = m_parent; m_parent->AddRef(); hr = S_OK; } return hr; } STDMETHODIMP CPCHElementBehaviorFactory::FindBehavior( /*[in]*/ BSTR bstrBehavior , /*[in]*/ BSTR bstrBehaviorUrl , /*[in]*/ IElementBehaviorSite* pSite , /*[out]*/ IElementBehavior* *ppBehavior ) { __HCP_FUNC_ENTRY( "CPCHElementBehaviorFactory::FindBehavior" ); HRESULT hr; CComPtr pElement; CComBSTR bstrTagName; const BehaviorDefinition* pBehaviorDef; int i; __MPC_PARAMCHECK_BEGIN(hr) __MPC_PARAMCHECK_NOTNULL(pSite); __MPC_PARAMCHECK_POINTER_AND_SET(ppBehavior,NULL); __MPC_PARAMCHECK_END(); // // Get tag name. // if(SUCCEEDED(pSite->GetElement( &pElement )) && pElement) { (void)pElement->get_tagName( &bstrTagName ); } for(pBehaviorDef=s_Behaviors, i=0; iszBehaviorName == NULL || (bstrBehavior && !_wcsicmp( pBehaviorDef->szBehaviorName, bstrBehavior ))) && (pBehaviorDef->szTagName == NULL || (bstrTagName && !_wcsicmp( pBehaviorDef->szTagName , bstrTagName ))) ) { __MPC_EXIT_IF_METHOD_FAILS(hr, pBehaviorDef->pfnCreator( m_parent, ppBehavior )); __MPC_SET_ERROR_AND_EXIT(hr, S_OK); } } hr = E_FAIL; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// CPCHBehavior::EventSink::EventSink( CPCHBehavior* parent ) { m_lRef = 1; // long m_lRef; // m_Parent = parent; // CPCHBehavior* m_Parent; // CComPtr m_elem; // CComBSTR m_bstrName; m_pfn = NULL; // CLASS_METHOD m_pfn; m_fAttached = false; // bool m_fAttached; m_idNotifyAs = -1; // DISPID m_idNotifyAs; } CPCHBehavior::EventSink::~EventSink() { (void)Detach(); } HRESULT CPCHBehavior::EventSink::Attach() { HRESULT hr = S_FALSE; if(m_fAttached) { hr = S_OK; } else if(m_elem && m_bstrName) { CComDispatchDriver disp ( m_elem ); CComVariant vName( m_bstrName ); CComVariant vDisp( (IDispatch*)this ); CComVariant vRes; if(SUCCEEDED(hr = disp.Invoke2( DISPID_IHTMLELEMENT2_ATTACHEVENT, &vName, &vDisp, &vRes ))) { if(vRes.vt == VT_BOOL && vRes.boolVal == VARIANT_TRUE) { m_fAttached = true; } } } return hr; } HRESULT CPCHBehavior::EventSink::Detach() { HRESULT hr = S_FALSE; if(m_fAttached == false) { hr = S_OK; } else if(m_elem && m_bstrName) { CComDispatchDriver disp ( m_elem ); CComVariant vName( m_bstrName ); CComVariant vDisp( (IDispatch*)this ); // // EXTERNAL BUG: if we detach from the events, in a particular situation MSHTML crashes... // //if(SUCCEEDED(hr = disp.Invoke2( DISPID_IHTMLELEMENT2_DETACHEVENT, &vName, &vDisp ))) { m_fAttached = false; } } m_elem.Release(); return hr; } //////////////////////////////////////// STDMETHODIMP_(ULONG) CPCHBehavior::EventSink::AddRef() { return ::InterlockedIncrement( &m_lRef ); } STDMETHODIMP_(ULONG) CPCHBehavior::EventSink::Release() { ULONG l = ::InterlockedDecrement( &m_lRef ); if(l == 0) delete this; return l; } STDMETHODIMP CPCHBehavior::EventSink::QueryInterface( REFIID iid, void ** ppvObject ) { if(ppvObject == NULL) return E_POINTER; if(InlineIsEqualGUID( iid, IID_IDispatch )) { *ppvObject = this; AddRef(); return S_OK; } *ppvObject = NULL; return E_NOINTERFACE; } //////////////////////////////////////// STDMETHODIMP CPCHBehavior::EventSink::GetTypeInfoCount( UINT* pctinfo ) { return E_NOTIMPL; } STDMETHODIMP CPCHBehavior::EventSink::GetTypeInfo( UINT itinfo , LCID lcid , ITypeInfo* *pptinfo ) { return E_NOTIMPL; } STDMETHODIMP CPCHBehavior::EventSink::GetIDsOfNames( REFIID riid , LPOLESTR* rgszNames , UINT cNames , LCID lcid , DISPID* rgdispid ) { return E_NOTIMPL; } STDMETHODIMP CPCHBehavior::EventSink::Invoke( DISPID dispidMember , REFIID riid , LCID lcid , WORD wFlags , DISPPARAMS* pdispparams , VARIANT* pvarResult , EXCEPINFO* pexcepinfo , UINT* puArgErr ) { if(m_Parent && m_pfn) { return (m_Parent->*m_pfn)( m_idNotifyAs == -1 ? dispidMember : m_idNotifyAs, pdispparams, pvarResult ); } else { return S_FALSE; } } //////////////////////////////////////// HRESULT CPCHBehavior::EventSink::CreateInstance( /*[in]*/ CPCHBehavior* parent, /*[out]*/ EventSink*& pObj ) { pObj = new EventSink( parent ); return (pObj == NULL) ? E_OUTOFMEMORY : S_OK; } //////////////////////////////////////////////////////////////////////////////// CPCHBehavior::CPCHBehavior() { m_parent = NULL; // CPCHHelpCenterExternal* m_parent; // // CComPtr m_siteOM; // CComPtr m_elem; // CComPtr m_elem2; // SinkList m_lstEventSinks; m_fRTL = false; // bool m_fRTL; m_fTrusted = false; // bool m_fTrusted; m_fSystem = false; // bool m_fSystem; } void CPCHBehavior::Initialize( /*[in]*/ CPCHHelpCenterExternal* parent ) { m_parent = parent; } STDMETHODIMP CPCHBehavior::Init( /*[in]*/ IElementBehaviorSite* pBehaviorSite ) { __HCP_FUNC_ENTRY( "CPCHBehavior::Init" ); HRESULT hr; __MPC_PARAMCHECK_BEGIN(hr) __MPC_PARAMCHECK_NOTNULL(m_parent); __MPC_PARAMCHECK_NOTNULL(pBehaviorSite); __MPC_PARAMCHECK_END(); Detach(); __MPC_EXIT_IF_METHOD_FAILS(hr, pBehaviorSite->QueryInterface( IID_IElementBehaviorSiteOM, (LPVOID*)&m_siteOM )); __MPC_EXIT_IF_METHOD_FAILS(hr, pBehaviorSite->GetElement ( &m_elem )); __MPC_EXIT_IF_METHOD_FAILS(hr, m_elem .QueryInterface( &m_elem2 )); // // Look for security stuff. // { CComPtr doc; CComPtr doc3; if(SUCCEEDED(MPC::HTML::IDispatch_To_IHTMLDocument2( doc, m_elem ))) { CComBSTR bstrURL; CComBSTR bstrDir; if(SUCCEEDED(doc->get_URL( &bstrURL ))) { m_fTrusted = m_parent->SecurityManager()->IsUrlTrusted( SAFEBSTR( bstrURL ), &m_fSystem ); } __MPC_EXIT_IF_METHOD_FAILS(hr, doc.QueryInterface( &doc3 )); __MPC_EXIT_IF_METHOD_FAILS(hr, doc3->get_dir( &bstrDir )); m_fRTL = (MPC::StrICmp( bstrDir, L"RTL" ) == 0); } } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } STDMETHODIMP CPCHBehavior::Notify( /*[in]*/ LONG lEvent, /*[in/out]*/ VARIANT* pVar ) { int i = 2; return S_OK; } STDMETHODIMP CPCHBehavior::Detach() { for(SinkIter it = m_lstEventSinks.begin(); it != m_lstEventSinks.end(); it++) { EventSink* obj = *it;; if(obj) { obj->m_Parent = NULL; obj->Detach (); obj->Release(); } } m_lstEventSinks.clear(); m_siteOM.Release(); m_elem .Release(); m_elem2 .Release(); return S_OK; } //////////////////////////////////////////////////////////////////////////////// HRESULT CPCHBehavior::AttachToEvent( /*[in] */ LPCWSTR szName , /*[in] */ CLASS_METHOD pfn , /*[in] */ IDispatch* elem , /*[out]*/ IDispatch* *pVal , /*[in] */ DISPID id ) { __HCP_FUNC_ENTRY( "CPCHBehavior::AttachToEvent" ); HRESULT hr; SinkIter it; EventSink* obj = NULL; __MPC_EXIT_IF_METHOD_FAILS(hr, obj->CreateInstance( this, obj )); obj->m_elem = elem ? elem : m_elem2; obj->m_bstrName = szName; obj->m_pfn = pfn; obj->m_idNotifyAs = id; if(pVal) // Don't attach to the site, simply return the IDispatch interface. { *pVal = obj; obj->AddRef(); } else { __MPC_EXIT_IF_METHOD_FAILS(hr, obj->Attach()); } m_lstEventSinks.push_back( obj ); obj = NULL; hr = S_OK; __HCP_FUNC_CLEANUP; if(obj) obj->Release(); __HCP_FUNC_EXIT(hr); } HRESULT CPCHBehavior::AttachToEvents( /*[in] */ const EventDescription* pEvents , /*[in] */ CLASS_METHOD pfn , /*[in] */ IDispatch* elem ) { __HCP_FUNC_ENTRY( "CPCHBehavior::AttachToEvents" ); HRESULT hr; while(pEvents->szName) { __MPC_EXIT_IF_METHOD_FAILS(hr, AttachToEvent( pEvents->szName, pfn, elem, NULL, pEvents->id )); pEvents++; } hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } HRESULT CPCHBehavior::CreateEvent( /*[in]*/ LPCWSTR szName, /*[out]*/ LONG& lEventCookie ) { __HCP_FUNC_ENTRY( "CPCHBehavior::CreateEvent" ); HRESULT hr; hr = m_siteOM ? m_siteOM->RegisterEvent( CComBSTR( szName ), 0, &lEventCookie ) : E_POINTER; __HCP_FUNC_EXIT(hr); } //////////////////////////////////////// HRESULT CPCHBehavior::GetEventObject( /*[out]*/ CComPtr& ev ) { return MPC::HTML::GetEventObject( ev, m_elem ); } HRESULT CPCHBehavior::CreateEventObject( /*[out]*/ CComPtr& ev ) { __HCP_FUNC_ENTRY( "CPCHBehavior::CreateEventObject" ); HRESULT hr; ev.Release(); hr = m_siteOM ? m_siteOM->CreateEventObject( &ev ) : E_POINTER; __HCP_FUNC_EXIT(hr); } HRESULT CPCHBehavior::FireEvent( /*[in ]*/ IHTMLEventObj* ev, /*[in]*/ LONG lEventCookie ) { __HCP_FUNC_ENTRY( "CPCHBehavior::FireEvent" ); HRESULT hr; hr = m_siteOM ? m_siteOM->FireEvent( lEventCookie, ev ) : E_POINTER; __HCP_FUNC_EXIT(hr); } HRESULT CPCHBehavior::FireEvent( /*[in]*/ LONG lEventCookie ) { __HCP_FUNC_ENTRY( "CPCHBehavior::FireEvent" ); HRESULT hr; CComPtr pEvent; __MPC_EXIT_IF_METHOD_FAILS(hr, CreateEventObject( pEvent )); __MPC_EXIT_IF_METHOD_FAILS(hr, FireEvent ( pEvent, lEventCookie )); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } //////////////////////////////////////// HRESULT CPCHBehavior::CancelEvent( /*[in]*/ IHTMLEventObj* ev, /*[in]*/ VARIANT* pvReturnValue, /*[in]*/ VARIANT_BOOL fCancelBubble ) { __HCP_FUNC_ENTRY( "CPCHBehavior::CancelEvent" ); HRESULT hr; CComPtr pEvent; CComVariant vDefault; if(ev == NULL) { __MPC_EXIT_IF_METHOD_FAILS(hr, GetEventObject( pEvent )); ev = pEvent; } if(pvReturnValue == NULL) { vDefault = false; pvReturnValue = &vDefault; } __MPC_EXIT_IF_METHOD_FAILS(hr, ev->put_returnValue ( *pvReturnValue )); __MPC_EXIT_IF_METHOD_FAILS(hr, ev->put_cancelBubble( fCancelBubble )); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } //////////////////////////////////////////////////////////////////////////////// HRESULT CPCHBehavior::GetEvent_SrcElement( /*[in]*/ CComPtr& elem ) { __HCP_FUNC_ENTRY( "CPCHBehavior::GetEvent_SrcElement" ); HRESULT hr; CComPtr ev; elem.Release(); __MPC_EXIT_IF_METHOD_FAILS(hr, GetEventObject( ev )); MPC_SCRIPTHELPER_GET__DIRECT__NOTNULL(elem, ev, srcElement); hr = S_OK; __HCP_FUNC_CLEANUP; __HCP_FUNC_EXIT(hr); } //////////////////////////////////////// HRESULT CPCHBehavior::GetAsVARIANT( /*[in]*/ BSTR value, /*[out, retval]*/ VARIANT *pVal ) { if(pVal == NULL) return E_POINTER; ::VariantClear( pVal ); pVal->vt = VT_BSTR; pVal->bstrVal = ::SysAllocString( value ); return S_OK; } HRESULT CPCHBehavior::GetAsVARIANT( /*[in]*/ IDispatch* value, /*[out, retval]*/ VARIANT *pVal ) { if(pVal == NULL) return E_POINTER; ::VariantClear( pVal ); if(value) { pVal->vt = VT_DISPATCH; pVal->pdispVal = value; value->AddRef(); } return S_OK; } HRESULT CPCHBehavior::GetAsIDISPATCH( /*[in]*/ IDispatch* value, /*[out, retval]*/ IDispatch* *pVal ) { return MPC::CopyTo( value, pVal ); }