From: Jacek Caban Subject: [PATCH] mshtml: Hook addEventListener InvokeEx calls to allow capture default value. Message-Id: Date: Fri, 24 Nov 2017 15:09:13 +0100 Signed-off-by: Jacek Caban --- dlls/mshtml/dispex.c | 33 +++++++++++++++++++++++---------- dlls/mshtml/htmlelem.c | 7 +++++-- dlls/mshtml/htmlevent.c | 33 ++++++++++++++++++++++++++++++++- dlls/mshtml/mshtml_private.h | 10 +++++++++- dlls/mshtml/tests/events.js | 13 +++++++------ 5 files changed, 76 insertions(+), 20 deletions(-) diff --git a/dlls/mshtml/dispex.c b/dlls/mshtml/dispex.c index b04fb81023..64ad555227 100644 --- a/dlls/mshtml/dispex.c +++ b/dlls/mshtml/dispex.c @@ -52,6 +52,7 @@ typedef struct { DISPID id; BSTR name; tid_t tid; + dispex_hook_invoke_t hook; SHORT call_vtbl_off; SHORT put_vtbl_off; SHORT get_vtbl_off; @@ -230,7 +231,8 @@ static BOOL is_arg_type_supported(VARTYPE vt) return FALSE; } -static void add_func_info(dispex_data_t *data, tid_t tid, const FUNCDESC *desc, ITypeInfo *dti) +static void add_func_info(dispex_data_t *data, tid_t tid, const FUNCDESC *desc, ITypeInfo *dti, + dispex_hook_invoke_t hook) { func_info_t *info; BSTR name; @@ -262,6 +264,7 @@ static void add_func_info(dispex_data_t *data, tid_t tid, const FUNCDESC *desc, info->tid = tid; info->func_disp_idx = -1; info->prop_vt = VT_EMPTY; + info->hook = hook; }else { SysFreeString(name); } @@ -326,10 +329,9 @@ static void add_func_info(dispex_data_t *data, tid_t tid, const FUNCDESC *desc, } } -static HRESULT process_interface(dispex_data_t *data, tid_t tid, ITypeInfo *disp_typeinfo, const DISPID *blacklist_dispids) +static HRESULT process_interface(dispex_data_t *data, tid_t tid, ITypeInfo *disp_typeinfo, const dispex_hook_t *hooks) { unsigned i = 7; /* skip IDispatch functions */ - const DISPID *blacklist_iter; ITypeInfo *typeinfo; FUNCDESC *funcdesc; HRESULT hres; @@ -339,20 +341,25 @@ static HRESULT process_interface(dispex_data_t *data, tid_t tid, ITypeInfo *disp return hres; while(1) { + const dispex_hook_t *hook = NULL; + hres = ITypeInfo_GetFuncDesc(typeinfo, i++, &funcdesc); if(FAILED(hres)) break; - if(blacklist_dispids) { - for(blacklist_iter = blacklist_dispids; *blacklist_iter != DISPID_UNKNOWN; blacklist_iter++) { - if(*blacklist_iter == funcdesc->memid) + if(hooks) { + for(hook = hooks; hook->dispid != DISPID_UNKNOWN; hook++) { + if(hook->dispid == funcdesc->memid) break; } + if(hook->dispid == DISPID_UNKNOWN) + hook = NULL; } - if(!blacklist_dispids || *blacklist_iter == DISPID_UNKNOWN) { + if(!hook || hook->invoke) { TRACE("adding...\n"); - add_func_info(data, tid, funcdesc, disp_typeinfo ? disp_typeinfo : typeinfo); + add_func_info(data, tid, funcdesc, disp_typeinfo ? disp_typeinfo : typeinfo, + hook ? hook->invoke : NULL); } ITypeInfo_ReleaseFuncDesc(typeinfo, funcdesc); @@ -361,11 +368,11 @@ static HRESULT process_interface(dispex_data_t *data, tid_t tid, ITypeInfo *disp return S_OK; } -void dispex_info_add_interface(dispex_data_t *info, tid_t tid, const DISPID *blacklist_dispids) +void dispex_info_add_interface(dispex_data_t *info, tid_t tid, const dispex_hook_t *hooks) { HRESULT hres; - hres = process_interface(info, tid, NULL, blacklist_dispids); + hres = process_interface(info, tid, NULL, hooks); if(FAILED(hres)) ERR("process_interface failed: %08x\n", hres); } @@ -1239,6 +1246,12 @@ static HRESULT invoke_builtin_prop(DispatchEx *This, DISPID id, LCID lcid, WORD if(FAILED(hres)) return hres; + if(func->hook) { + hres = func->hook(This, lcid, flags, dp, res, ei, caller); + if(hres != S_FALSE) + return hres; + } + if(func->func_disp_idx != -1) return function_invoke(This, func, flags, dp, res, ei, caller); diff --git a/dlls/mshtml/htmlelem.c b/dlls/mshtml/htmlelem.c index dc712f3057..1bd4ec4899 100644 --- a/dlls/mshtml/htmlelem.c +++ b/dlls/mshtml/htmlelem.c @@ -5398,11 +5398,14 @@ static IHTMLEventObj *HTMLElement_set_current_event(DispatchEx *dispex, IHTMLEve void HTMLElement_init_dispex_info(dispex_data_t *info, compat_mode_t mode) { - static const DISPID elem2_ie11_blacklist[] = {DISPID_IHTMLELEMENT2_DOSCROLL, DISPID_UNKNOWN}; + static const dispex_hook_t elem2_ie11_hooks[] = { + {DISPID_IHTMLELEMENT2_DOSCROLL, NULL}, + {DISPID_UNKNOWN} + }; HTMLDOMNode_init_dispex_info(info, mode); - dispex_info_add_interface(info, IHTMLElement2_tid, mode >= COMPAT_MODE_IE11 ? elem2_ie11_blacklist : NULL); + dispex_info_add_interface(info, IHTMLElement2_tid, mode >= COMPAT_MODE_IE11 ? elem2_ie11_hooks : NULL); if(mode >= COMPAT_MODE_IE8) dispex_info_add_interface(info, IElementSelector_tid, NULL); diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c index 46697b30b9..d93a43ee6d 100644 --- a/dlls/mshtml/htmlevent.c +++ b/dlls/mshtml/htmlevent.c @@ -2051,6 +2051,11 @@ static inline EventTarget *impl_from_IEventTarget(IEventTarget *iface) return CONTAINING_RECORD(iface, EventTarget, IEventTarget_iface); } +static inline EventTarget *impl_from_DispatchEx(DispatchEx *iface) +{ + return CONTAINING_RECORD(iface, EventTarget, dispex); +} + static HRESULT WINAPI EventTarget_QueryInterface(IEventTarget *iface, REFIID riid, void **ppv) { EventTarget *This = impl_from_IEventTarget(iface); @@ -2162,6 +2167,27 @@ static HRESULT WINAPI EventTarget_dispatchEvent(IEventTarget *iface, IDOMEvent * return E_NOTIMPL; } +HRESULT IEventTarget_addEventListener_hook(DispatchEx *dispex, LCID lcid, WORD flags, + DISPPARAMS *dp, VARIANT *res, EXCEPINFO *ei, IServiceProvider *caller) +{ + /* If only two arguments were given, implicitly set capture to false */ + if((flags & DISPATCH_METHOD) && dp->cArgs == 2 && !dp->cNamedArgs) { + VARIANT args[3]; + DISPPARAMS new_dp = {args, NULL, 3, 0}; + V_VT(args) = VT_BOOL; + V_BOOL(args) = VARIANT_FALSE; + args[1] = dp->rgvarg[0]; + args[2] = dp->rgvarg[1]; + + TRACE("implicit capture\n"); + + return IDispatchEx_InvokeEx(&dispex->IDispatchEx_iface, DISPID_IEVENTTARGET_ADDEVENTLISTENER, + lcid, flags, &new_dp, res, ei, caller); + } + + return S_FALSE; /* fallback to default */ +} + static const IEventTargetVtbl EventTargetVtbl = { EventTarget_QueryInterface, EventTarget_AddRef, @@ -2210,8 +2236,13 @@ HRESULT EventTarget_QI(EventTarget *event_target, REFIID riid, void **ppv) void EventTarget_init_dispex_info(dispex_data_t *dispex_info, compat_mode_t compat_mode) { + static const dispex_hook_t IEventTarget_hooks[] = { + {DISPID_IEVENTTARGET_ADDEVENTLISTENER, IEventTarget_addEventListener_hook}, + {DISPID_UNKNOWN} + }; + if(compat_mode >= COMPAT_MODE_IE9) - dispex_info_add_interface(dispex_info, IEventTarget_tid, NULL); + dispex_info_add_interface(dispex_info, IEventTarget_tid, IEventTarget_hooks); } static int event_id_cmp(const void *key, const struct wine_rb_entry *entry) diff --git a/dlls/mshtml/mshtml_private.h b/dlls/mshtml/mshtml_private.h index 69fe90c810..f55f064dae 100644 --- a/dlls/mshtml/mshtml_private.h +++ b/dlls/mshtml/mshtml_private.h @@ -283,6 +283,14 @@ typedef struct { dispex_data_t *delayed_init_info; } dispex_static_data_t; +typedef HRESULT (*dispex_hook_invoke_t)(DispatchEx*,LCID,WORD,DISPPARAMS*,VARIANT*, + EXCEPINFO*,IServiceProvider*); + +typedef struct { + DISPID dispid; + dispex_hook_invoke_t invoke; +} dispex_hook_t; + struct DispatchEx { IDispatchEx IDispatchEx_iface; @@ -331,7 +339,7 @@ void dispex_unlink(DispatchEx*) DECLSPEC_HIDDEN; void release_typelib(void) DECLSPEC_HIDDEN; HRESULT get_class_typeinfo(const CLSID*,ITypeInfo**) DECLSPEC_HIDDEN; const void *dispex_get_vtbl(DispatchEx*) DECLSPEC_HIDDEN; -void dispex_info_add_interface(dispex_data_t*,tid_t,const DISPID*) DECLSPEC_HIDDEN; +void dispex_info_add_interface(dispex_data_t*,tid_t,const dispex_hook_t*) DECLSPEC_HIDDEN; compat_mode_t dispex_compat_mode(DispatchEx*) DECLSPEC_HIDDEN; static inline void init_dispex(DispatchEx *dispex, IUnknown *outer, dispex_static_data_t *desc) diff --git a/dlls/mshtml/tests/events.js b/dlls/mshtml/tests/events.js index 4c552b6508..d613c9d5bd 100644 --- a/dlls/mshtml/tests/events.js +++ b/dlls/mshtml/tests/events.js @@ -62,6 +62,7 @@ function test_listener_order() { document.body.onclick = record_call("body.onclick"); document.body.addEventListener("click", record_call("body.click(capture)"), true); document.body.addEventListener("click", record_call("body.click(bubble)"), false); + document.body.addEventListener("click", record_call("body.click(bubble2)")); document.body.attachEvent("onclick", record_call("body.click(attached)")); div.attachEvent("onclick", record_call("div.click(attached)")); @@ -74,9 +75,9 @@ function test_listener_order() { div.click(); ok(calls === "window.click(capture),document.click(capture),body.click(capture)," + "div.click(attached),div.click(bubble),div.onclick,div.click(capture1)," - + "div.click(capture2),body.onclick,body.click(bubble),body.click(attached)," - + "document.click(attached),document.click(bubble),document.onclick," - + "window.click(bubble),", "calls = " + calls); + + "div.click(capture2),body.onclick,body.click(bubble),body.click(bubble2)," + + "body.click(attached),document.click(attached),document.click(bubble)," + + "document.onclick,window.click(bubble),", "calls = " + calls); div.onclick = record_call("new div.onclick"); @@ -84,9 +85,9 @@ function test_listener_order() { div.click(); ok(calls === "window.click(capture),document.click(capture),body.click(capture)," + "div.click(attached),div.click(bubble),new div.onclick,div.click(capture1)," - + "div.click(capture2),body.onclick,body.click(bubble),body.click(attached)," - + "document.click(attached),document.click(bubble),document.onclick," - + "window.click(bubble),", "calls = " + calls); + + "div.click(capture2),body.onclick,body.click(bubble),body.click(bubble2)," + + "body.click(attached),document.click(attached),document.click(bubble)," + + "document.onclick,window.click(bubble),", "calls = " + calls); next_test(); }