diff --git a/dlls/mshtml/dispex.c b/dlls/mshtml/dispex.c index 059ed6660e3..b4cfe3fab8e 100644 --- a/dlls/mshtml/dispex.c +++ b/dlls/mshtml/dispex.c @@ -35,12 +35,14 @@ typedef struct { DISPID id; BSTR name; tid_t tid; + int func_disp_idx; } func_info_t; struct dispex_data_t { DWORD func_cnt; func_info_t *funcs; func_info_t **name_table; + DWORD func_disp_cnt; struct list entry; }; @@ -50,10 +52,20 @@ typedef struct { LPWSTR name; } dynamic_prop_t; +typedef struct { + DispatchEx dispex; + const IUnknownVtbl *lpIUnknownVtbl; + DispatchEx *obj; + func_info_t *info; +} func_disp_t; + +#define FUNCUNKNOWN(x) ((IUnknown*) &(x)->lpIUnknownVtbl) + struct dispex_dynamic_data_t { DWORD buf_size; DWORD prop_cnt; dynamic_prop_t *props; + func_disp_t **func_disps; }; #define DISPID_DYNPROP_0 0x50000000 @@ -188,22 +200,23 @@ void release_typelib(void) ITypeLib_Release(typelib); } -static void add_func_info(dispex_data_t *data, DWORD *size, tid_t tid, DISPID id, ITypeInfo *dti) +static void add_func_info(dispex_data_t *data, DWORD *size, tid_t tid, const FUNCDESC *desc, ITypeInfo *dti) { HRESULT hres; - if(data->func_cnt && data->funcs[data->func_cnt-1].id == id) + if(data->func_cnt && data->funcs[data->func_cnt-1].id == desc->memid) return; if(data->func_cnt == *size) data->funcs = heap_realloc(data->funcs, (*size <<= 1)*sizeof(func_info_t)); - hres = ITypeInfo_GetDocumentation(dti, id, &data->funcs[data->func_cnt].name, NULL, NULL, NULL); + hres = ITypeInfo_GetDocumentation(dti, desc->memid, &data->funcs[data->func_cnt].name, NULL, NULL, NULL); if(FAILED(hres)) return; - data->funcs[data->func_cnt].id = id; + data->funcs[data->func_cnt].id = desc->memid; data->funcs[data->func_cnt].tid = tid; + data->funcs[data->func_cnt].func_disp_idx = desc->invkind == INVOKE_FUNC ? data->func_disp_cnt++ : -1; data->func_cnt++; } @@ -237,6 +250,7 @@ static dispex_data_t *preprocess_dispex_data(DispatchEx *This) data = heap_alloc(sizeof(dispex_data_t)); data->func_cnt = 0; + data->func_disp_cnt = 0; data->funcs = heap_alloc(size*sizeof(func_info_t)); list_add_tail(&dispex_data_list, &data->entry); @@ -251,7 +265,7 @@ static dispex_data_t *preprocess_dispex_data(DispatchEx *This) if(FAILED(hres)) break; - add_func_info(data, &size, *tid, funcdesc->memid, dti); + add_func_info(data, &size, *tid, funcdesc, dti); ITypeInfo_ReleaseFuncDesc(ti, funcdesc); } @@ -457,6 +471,142 @@ static HRESULT typeinfo_invoke(DispatchEx *This, func_info_t *func, WORD flags, return hres; } +#define FUNCTION_THIS(iface) DEFINE_THIS(func_disp_t, IUnknown, iface) + +static HRESULT WINAPI Function_QueryInterface(IUnknown *iface, REFIID riid, void **ppv) +{ + func_disp_t *This = FUNCTION_THIS(iface); + + if(IsEqualGUID(&IID_IUnknown, riid)) { + TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); + *ppv = FUNCUNKNOWN(This); + }else if(dispex_query_interface(&This->dispex, riid, ppv)) { + return *ppv ? S_OK : E_NOINTERFACE; + }else { + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI Function_AddRef(IUnknown *iface) +{ + func_disp_t *This = FUNCTION_THIS(iface); + + TRACE("(%p)\n", This); + + return IDispatchEx_AddRef(DISPATCHEX(This->obj)); +} + +static ULONG WINAPI Function_Release(IUnknown *iface) +{ + func_disp_t *This = FUNCTION_THIS(iface); + + TRACE("(%p)\n", This); + + return IDispatchEx_Release(DISPATCHEX(This->obj)); +} + +static HRESULT function_value(IUnknown *iface, LCID lcid, WORD flags, DISPPARAMS *params, + VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) +{ + func_disp_t *This = FUNCTION_THIS(iface); + HRESULT hres; + + switch(flags) { + case DISPATCH_METHOD: + hres = typeinfo_invoke(This->obj, This->info, flags, params, res, ei); + break; + default: + FIXME("Unimplemented flags %x\n", flags); + hres = E_NOTIMPL; + } + + return hres; +} + +#undef FUNCTION_THIS + +static const IUnknownVtbl FunctionUnkVtbl = { + Function_QueryInterface, + Function_AddRef, + Function_Release +}; + +static const dispex_static_data_vtbl_t function_dispex_vtbl = { + function_value, + NULL, + NULL +}; + +static dispex_static_data_t function_dispex = { + &function_dispex_vtbl, + LAST_tid, + NULL, + NULL +}; + +static func_disp_t *create_func_disp(DispatchEx *obj, func_info_t *info) +{ + func_disp_t *ret; + + ret = heap_alloc_zero(sizeof(func_disp_t)); + if(!ret) + return NULL; + + ret->lpIUnknownVtbl = &FunctionUnkVtbl; + init_dispex(&ret->dispex, FUNCUNKNOWN(ret), &function_dispex); + ret->obj = obj; + ret->info = info; + + return ret; +} + +static HRESULT function_invoke(DispatchEx *This, func_info_t *func, WORD flags, DISPPARAMS *dp, VARIANT *res, + EXCEPINFO *ei) +{ + HRESULT hres; + + switch(flags) { + case DISPATCH_METHOD: + hres = typeinfo_invoke(This, func, flags, dp, res, ei); + break; + case DISPATCH_PROPERTYGET: { + dispex_dynamic_data_t *dynamic_data; + + dynamic_data = get_dynamic_data(This, TRUE); + if(!dynamic_data) + return E_OUTOFMEMORY; + + if(!dynamic_data->func_disps) { + dynamic_data->func_disps = heap_alloc_zero(This->data->data->func_disp_cnt * sizeof(func_disp_t*)); + if(!dynamic_data->func_disps) + return E_OUTOFMEMORY; + } + + if(!dynamic_data->func_disps[func->func_disp_idx]) { + dynamic_data->func_disps[func->func_disp_idx] = create_func_disp(This, func); + if(!dynamic_data->func_disps[func->func_disp_idx]) + return E_OUTOFMEMORY; + } + + V_VT(res) = VT_DISPATCH; + V_DISPATCH(res) = (IDispatch*)DISPATCHEX(&dynamic_data->func_disps[func->func_disp_idx]->dispex); + IDispatch_AddRef(V_DISPATCH(res)); + hres = S_OK; + break; + } + default: + FIXME("Unimplemented flags %x\n", flags); + case DISPATCH_PROPERTYPUT: + hres = E_NOTIMPL; + } + + return hres; +} + #define DISPATCHEX_THIS(iface) DEFINE_THIS(DispatchEx, IDispatchEx, iface) static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv) @@ -698,7 +848,12 @@ static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lc return DISP_E_UNKNOWNNAME; } - return typeinfo_invoke(This, data->funcs+n, wFlags, pdp, pvarRes, pei); + if(data->funcs[n].func_disp_idx == -1) + hres = typeinfo_invoke(This, data->funcs+n, wFlags, pdp, pvarRes, pei); + else + hres = function_invoke(This, data->funcs+n, wFlags, pdp, pvarRes, pei); + + return hres; } static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex)