From eda756dbb54d44b09441b1d53549a062c54d8822 Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Wed, 3 Oct 2012 17:26:28 +0200 Subject: [PATCH] mshtml: Added support for binding ActiveX events with script event attribute. --- dlls/mshtml/pluginhost.c | 370 +++++++++++++++++++++++++++++++++++++++ dlls/mshtml/pluginhost.h | 3 + dlls/mshtml/script.c | 7 +- 3 files changed, 378 insertions(+), 2 deletions(-) diff --git a/dlls/mshtml/pluginhost.c b/dlls/mshtml/pluginhost.c index e4b85a87e9e..0f68c25972b 100644 --- a/dlls/mshtml/pluginhost.c +++ b/dlls/mshtml/pluginhost.c @@ -19,6 +19,7 @@ #include "config.h" #include +#include #define COBJMACROS @@ -470,6 +471,349 @@ HRESULT invoke_plugin_prop(HTMLPluginContainer *plugin_container, DISPID id, LCI lcid, flags, params, res, ei, NULL); } +typedef struct { + DISPID id; + IDispatch *disp; +} sink_entry_t; + +struct PHEventSink { + IDispatch IDispatch_iface; + + LONG ref; + + PluginHost *host; + ITypeInfo *typeinfo; + GUID iid; + DWORD cookie; + + sink_entry_t *handlers; + DWORD handlers_cnt; + DWORD handlers_size; +}; + +static sink_entry_t *find_sink_entry(PHEventSink *sink, DISPID id) +{ + sink_entry_t *iter; + + for(iter = sink->handlers; iter < sink->handlers+sink->handlers_cnt; iter++) { + if(iter->id == id) + return iter; + } + + return NULL; +} + +static void add_sink_handler(PHEventSink *sink, DISPID id, IDispatch *disp) +{ + sink_entry_t *entry = find_sink_entry(sink, id); + + if(entry) { + if(entry->disp) + IDispatch_Release(entry->disp); + }else { + if(!sink->handlers_size) { + sink->handlers = heap_alloc(4*sizeof(*sink->handlers)); + if(!sink->handlers) + return; + sink->handlers_size = 4; + }else if(sink->handlers_cnt == sink->handlers_size) { + sink_entry_t *new_handlers; + + new_handlers = heap_realloc(sink->handlers, 2*sink->handlers_size*sizeof(*sink->handlers)); + if(!new_handlers) + return; + sink->handlers = new_handlers; + sink->handlers_size *= 2; + } + entry = sink->handlers + sink->handlers_cnt++; + entry->id = id; + } + + IDispatch_AddRef(disp); + entry->disp = disp; +} + +static inline PHEventSink *PHEventSink_from_IDispatch(IDispatch *iface) +{ + return CONTAINING_RECORD(iface, PHEventSink, IDispatch_iface); +} + +static HRESULT WINAPI PHEventSink_QueryInterface(IDispatch *iface, REFIID riid, void **ppv) +{ + PHEventSink *This = PHEventSink_from_IDispatch(iface); + + if(IsEqualGUID(riid, &IID_IUnknown)) { + TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); + *ppv = &This->IDispatch_iface; + }else if(IsEqualGUID(riid, &IID_IDispatch)) { + TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv); + *ppv = &This->IDispatch_iface; + }else { + WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI PHEventSink_AddRef(IDispatch *iface) +{ + PHEventSink *This = PHEventSink_from_IDispatch(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p)\n", This); + + return ref; +} + +static ULONG WINAPI PHEventSink_Release(IDispatch *iface) +{ + PHEventSink *This = PHEventSink_from_IDispatch(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p)\n", This); + + if(!ref) { + unsigned i; + + assert(!This->host); + + for(i=0; i < This->handlers_cnt; i++) { + if(This->handlers[i].disp) + IDispatch_Release(This->handlers[i].disp); + } + heap_free(This->handlers); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI PHEventSink_GetTypeInfoCount(IDispatch *iface, UINT *pctinfo) +{ + PHEventSink *This = PHEventSink_from_IDispatch(iface); + FIXME("(%p)->(%p)\n", This, pctinfo); + return E_NOTIMPL; +} + +static HRESULT WINAPI PHEventSink_GetTypeInfo(IDispatch *iface, UINT iTInfo, + LCID lcid, ITypeInfo **ppTInfo) +{ + PHEventSink *This = PHEventSink_from_IDispatch(iface); + FIXME("(%p)->(%d %d %p)\n", This, iTInfo, lcid, ppTInfo); + return E_NOTIMPL; +} + +static HRESULT WINAPI PHEventSink_GetIDsOfNames(IDispatch *iface, REFIID riid, LPOLESTR *rgszNames, + UINT cNames, LCID lcid, DISPID *rgDispId) +{ + PHEventSink *This = PHEventSink_from_IDispatch(iface); + FIXME("(%p)->(%s %p %u %d %p)\n", This, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId); + return E_NOTIMPL; +} + +static HRESULT WINAPI PHEventSink_Invoke(IDispatch *iface, DISPID dispIdMember, REFIID riid, LCID lcid, + WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + PHEventSink *This = PHEventSink_from_IDispatch(iface); + IDispatchEx *dispex; + sink_entry_t *entry; + HRESULT hres; + + TRACE("(%p)->(%d %s %d %x %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), lcid, wFlags, + pDispParams, pVarResult, pExcepInfo, puArgErr); + + if(!This->host) { + WARN("No host\n"); + return E_UNEXPECTED; + } + + entry = find_sink_entry(This, dispIdMember); + if(!entry || !entry->disp) { + WARN("No handler %d\n", dispIdMember); + if(pVarResult) + V_VT(pVarResult) = VT_EMPTY; + return S_OK; + } + + hres = IDispatch_QueryInterface(entry->disp, &IID_IDispatchEx, (void**)&dispex); + + TRACE("(%p) %d >>>\n", This, entry->id); + if(SUCCEEDED(hres)) { + hres = IDispatchEx_InvokeEx(dispex, DISPID_VALUE, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, NULL); + IDispatchEx_Release(dispex); + }else { + hres = IDispatch_Invoke(entry->disp, DISPID_VALUE, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + } + if(SUCCEEDED(hres)) + TRACE("(%p) %d <<<\n", This, entry->id); + else + WARN("(%p) %d <<< %08x\n", This, entry->id, hres); + return hres; +} + +static const IDispatchVtbl PHCPDispatchVtbl = { + PHEventSink_QueryInterface, + PHEventSink_AddRef, + PHEventSink_Release, + PHEventSink_GetTypeInfoCount, + PHEventSink_GetTypeInfo, + PHEventSink_GetIDsOfNames, + PHEventSink_Invoke +}; + +static PHEventSink *create_event_sink(PluginHost *plugin_host, ITypeInfo *typeinfo) +{ + IConnectionPointContainer *cp_container; + PHEventSink *ret; + IConnectionPoint *cp; + TYPEATTR *typeattr; + GUID guid; + HRESULT hres; + + hres = ITypeInfo_GetTypeAttr(typeinfo, &typeattr); + if(FAILED(hres)) + return NULL; + + guid = typeattr->guid; + ITypeInfo_ReleaseTypeAttr(typeinfo, typeattr); + + hres = IUnknown_QueryInterface(plugin_host->plugin_unk, &IID_IConnectionPointContainer, (void**)&cp_container); + if(FAILED(hres)) { + WARN("Could not get IConnectionPointContainer iface: %08x\n", hres); + return NULL; + } + + hres = IConnectionPointContainer_FindConnectionPoint(cp_container, &guid, &cp); + IConnectionPointContainer_Release(cp_container); + if(FAILED(hres)) { + WARN("Could not find %s connection point\n", debugstr_guid(&guid)); + return NULL; + } + + ret = heap_alloc_zero(sizeof(*ret)); + if(ret) { + ret->IDispatch_iface.lpVtbl = &PHCPDispatchVtbl; + ret->ref = 1; + ret->host = plugin_host; + ret->iid = guid; + + ITypeInfo_AddRef(typeinfo); + ret->typeinfo = typeinfo; + + hres = IConnectionPoint_Advise(cp, (IUnknown*)&ret->IDispatch_iface, &ret->cookie); + }else { + hres = E_OUTOFMEMORY; + } + + IConnectionPoint_Release(cp); + if(FAILED(hres)) { + WARN("Advise failed: %08x\n", hres); + return NULL; + } + + return ret; +} + +static ITypeInfo *get_eventiface_info(HTMLPluginContainer *plugin_container, ITypeInfo *class_info) +{ + int impl_types, i, impl_flags; + ITypeInfo *ret = NULL; + TYPEATTR *typeattr; + HREFTYPE ref; + HRESULT hres; + + hres = ITypeInfo_GetTypeAttr(class_info, &typeattr); + if(FAILED(hres)) + return NULL; + + if(typeattr->typekind != TKIND_COCLASS) { + WARN("not coclass\n"); + ITypeInfo_ReleaseTypeAttr(class_info, typeattr); + return NULL; + } + + impl_types = typeattr->cImplTypes; + ITypeInfo_ReleaseTypeAttr(class_info, typeattr); + + for(i=0; iplugin_host; + ITypeInfo *class_info, *source_info; + DISPID id; + HRESULT hres; + + TRACE("(%p %p %s %p)\n", doc, plugin_host, debugstr_w(event), disp); + + if(!plugin_host || !plugin_host->plugin_unk) { + WARN("detached element %p\n", plugin_host); + return; + } + + if(plugin_host->sink) { + source_info = plugin_host->sink->typeinfo; + ITypeInfo_AddRef(source_info); + }else { + IProvideClassInfo *provide_ci; + + hres = IUnknown_QueryInterface(plugin_host->plugin_unk, &IID_IProvideClassInfo, (void**)&provide_ci); + if(FAILED(hres)) { + FIXME("No IProvideClassInfo, try GetTypeInfo?\n"); + return; + } + + hres = IProvideClassInfo_GetClassInfo(provide_ci, &class_info); + IProvideClassInfo_Release(provide_ci); + if(FAILED(hres) || !class_info) { + WARN("GetClassInfo failed: %08x\n", hres); + return; + } + + source_info = get_eventiface_info(plugin_container, class_info); + ITypeInfo_Release(class_info); + if(!source_info) + return; + } + + hres = ITypeInfo_GetIDsOfNames(source_info, &event, 1, &id); + if(FAILED(hres)) + WARN("Could not get disp id: %08x\n", hres); + else if(!plugin_host->sink) + plugin_host->sink = create_event_sink(plugin_host, source_info); + + ITypeInfo_Release(source_info); + if(FAILED(hres) || !plugin_host->sink) + return; + + add_sink_handler(plugin_host->sink, id, disp); +} + static inline PluginHost *impl_from_IOleClientSite(IOleClientSite *iface) { return CONTAINING_RECORD(iface, PluginHost, IOleClientSite_iface); @@ -547,6 +891,11 @@ static ULONG WINAPI PHClientSite_Release(IOleClientSite *iface) IDispatch_Release(This->disp); if(This->ip_object) IOleInPlaceObject_Release(This->ip_object); + if(This->sink) { + This->sink->host = NULL; + IDispatch_Release(&This->sink->IDispatch_iface); + This->sink = NULL; + } list_remove(&This->entry); if(This->element) This->element->plugin_host = NULL; @@ -1301,6 +1650,27 @@ void detach_plugin_host(PluginHost *host) } } + if(host->sink) { + IConnectionPointContainer *cp_container; + IConnectionPoint *cp; + + assert(host->plugin_unk != NULL); + + hres = IUnknown_QueryInterface(host->plugin_unk, &IID_IConnectionPointContainer, (void**)&cp_container); + if(SUCCEEDED(hres)) { + hres = IConnectionPointContainer_FindConnectionPoint(cp_container, &host->sink->iid, &cp); + IConnectionPointContainer_Release(cp_container); + if(SUCCEEDED(hres)) { + IConnectionPoint_Unadvise(cp, host->sink->cookie); + IConnectionPoint_Release(cp); + } + } + + host->sink->host = NULL; + IDispatch_Release(&host->sink->IDispatch_iface); + host->sink = NULL; + } + if(host->element) { host->element->plugin_host = NULL; host->element = NULL; diff --git a/dlls/mshtml/pluginhost.h b/dlls/mshtml/pluginhost.h index d776885f073..5cc5ad0f553 100644 --- a/dlls/mshtml/pluginhost.h +++ b/dlls/mshtml/pluginhost.h @@ -17,6 +17,7 @@ */ typedef struct HTMLPluginContainer HTMLPluginContainer; +typedef struct PHEventSink PHEventSink; typedef struct { IOleClientSite IOleClientSite_iface; @@ -43,6 +44,7 @@ typedef struct { HTMLDocumentNode *doc; struct list entry; + PHEventSink *sink; HTMLPluginContainer *element; } PluginHost; @@ -71,3 +73,4 @@ HRESULT get_plugin_disp(HTMLPluginContainer*,IDispatch**) DECLSPEC_HIDDEN; HRESULT get_plugin_dispid(HTMLPluginContainer*,WCHAR*,DISPID*) DECLSPEC_HIDDEN; HRESULT invoke_plugin_prop(HTMLPluginContainer*,DISPID,LCID,WORD,DISPPARAMS*,VARIANT*,EXCEPINFO*) DECLSPEC_HIDDEN; void notif_container_change(HTMLPluginContainer*,DISPID); +void bind_activex_event(HTMLDocumentNode*,HTMLPluginContainer*,WCHAR*,IDispatch*) DECLSPEC_HIDDEN; diff --git a/dlls/mshtml/script.c b/dlls/mshtml/script.c index 467464a753a..efde2a63e16 100644 --- a/dlls/mshtml/script.c +++ b/dlls/mshtml/script.c @@ -1065,6 +1065,8 @@ static BOOL parse_event_str(WCHAR *event, const WCHAR **args) { WCHAR *ptr; + TRACE("%s\n", debugstr_w(event)); + for(ptr = event; isalnumW(*ptr); ptr++); if(!*ptr) { *args = NULL; @@ -1079,8 +1081,9 @@ static BOOL parse_event_str(WCHAR *event, const WCHAR **args) while(isalnumW(*ptr) || isspaceW(*ptr) || *ptr == ',') ptr++; - if(*ptr++ != ')') + if(*ptr != ')') return FALSE; + *ptr++ = 0; return !*ptr; } @@ -1203,7 +1206,7 @@ void bind_event_scripts(HTMLDocumentNode *doc) IHTMLElement_QueryInterface(&event_target->IHTMLElement_iface, &IID_HTMLPluginContainer, (void**)&plugin_container); if(plugin_container) - FIXME("ActiveX events not supported\n"); + bind_activex_event(doc, plugin_container, event, event_disp); else bind_elem_event(doc, event_target, event, event_disp);