Commit 87d4a5a2 authored by Jacek Caban's avatar Jacek Caban Committed by Alexandre Julliard

mshtml: Store all event listeners in a list.

parent 231d1d3c
...@@ -41,6 +41,7 @@ typedef enum { ...@@ -41,6 +41,7 @@ typedef enum {
} listener_type_t; } listener_type_t;
typedef struct { typedef struct {
struct list entry;
listener_type_t type; listener_type_t type;
IDispatch *function; IDispatch *function;
} event_listener_t; } event_listener_t;
...@@ -48,10 +49,8 @@ typedef struct { ...@@ -48,10 +49,8 @@ typedef struct {
typedef struct { typedef struct {
struct wine_rb_entry entry; struct wine_rb_entry entry;
eventid_t event_id; eventid_t event_id;
IDispatch *handler_prop; struct list listeners;
DWORD handler_cnt; } listener_container_t;
IDispatch **handlers;
} handler_vector_t;
static const WCHAR abortW[] = {'a','b','o','r','t',0}; static const WCHAR abortW[] = {'a','b','o','r','t',0};
static const WCHAR beforeactivateW[] = {'b','e','f','o','r','e','a','c','t','i','v','a','t','e',0}; static const WCHAR beforeactivateW[] = {'b','e','f','o','r','e','a','c','t','i','v','a','t','e',0};
...@@ -233,6 +232,56 @@ static eventid_t attr_to_eid(const WCHAR *str) ...@@ -233,6 +232,56 @@ static eventid_t attr_to_eid(const WCHAR *str)
return EVENTID_LAST; return EVENTID_LAST;
} }
static listener_container_t *get_listener_container(EventTarget *event_target, eventid_t eid, BOOL alloc)
{
const event_target_vtbl_t *vtbl;
listener_container_t *container;
struct wine_rb_entry *entry;
entry = wine_rb_get(&event_target->handler_map, (const void*)eid);
if(entry)
return WINE_RB_ENTRY_VALUE(entry, listener_container_t, entry);
if(!alloc)
return NULL;
if(event_info[eid].flags & EVENT_FIXME)
FIXME("unimplemented event %s\n", debugstr_w(event_info[eid].name));
container = heap_alloc(sizeof(*container));
if(!container)
return NULL;
container->event_id = eid;
list_init(&container->listeners);
vtbl = dispex_get_vtbl(&event_target->dispex);
if(vtbl->bind_event)
vtbl->bind_event(&event_target->dispex, eid);
else
FIXME("Unsupported event binding on target %p\n", event_target);
wine_rb_put(&event_target->handler_map, (const void*)eid, &container->entry);
return container;
}
static void remove_event_listener(EventTarget *event_target, eventid_t eid, listener_type_t type, IDispatch *function)
{
listener_container_t *container;
event_listener_t *listener;
container = get_listener_container(event_target, eid, FALSE);
if(!container)
return;
LIST_FOR_EACH_ENTRY(listener, &container->listeners, event_listener_t, entry) {
if(listener->function == function && listener->type == type) {
IDispatch_Release(listener->function);
list_remove(&listener->entry);
heap_free(listener);
break;
}
}
}
typedef struct { typedef struct {
DispatchEx dispex; DispatchEx dispex;
IHTMLEventObj IHTMLEventObj_iface; IHTMLEventObj IHTMLEventObj_iface;
...@@ -1169,33 +1218,6 @@ HRESULT create_document_event(HTMLDocumentNode *doc, eventid_t event_id, DOMEven ...@@ -1169,33 +1218,6 @@ HRESULT create_document_event(HTMLDocumentNode *doc, eventid_t event_id, DOMEven
return S_OK; return S_OK;
} }
static handler_vector_t *get_handler_vector(EventTarget *event_target, eventid_t eid, BOOL alloc)
{
const event_target_vtbl_t *vtbl;
handler_vector_t *handler_vector;
struct wine_rb_entry *entry;
entry = wine_rb_get(&event_target->handler_map, (const void*)eid);
if(entry)
return WINE_RB_ENTRY_VALUE(entry, handler_vector_t, entry);
if(!alloc)
return NULL;
handler_vector = heap_alloc_zero(sizeof(*handler_vector));
if(!handler_vector)
return NULL;
handler_vector->event_id = eid;
vtbl = dispex_get_vtbl(&event_target->dispex);
if(vtbl->bind_event)
vtbl->bind_event(&event_target->dispex, eid);
else
FIXME("Unsupported event binding on target %p\n", event_target);
wine_rb_put(&event_target->handler_map, (const void*)eid, &handler_vector->entry);
return handler_vector;
}
static HRESULT call_disp_func(IDispatch *disp, DISPPARAMS *dp, VARIANT *retv) static HRESULT call_disp_func(IDispatch *disp, DISPPARAMS *dp, VARIANT *retv)
{ {
IDispatchEx *dispex; IDispatchEx *dispex;
...@@ -1268,7 +1290,7 @@ static BOOL is_cp_event(cp_static_data_t *data, DISPID dispid) ...@@ -1268,7 +1290,7 @@ static BOOL is_cp_event(cp_static_data_t *data, DISPID dispid)
static void call_event_handlers(EventTarget *event_target, DOMEvent *event) static void call_event_handlers(EventTarget *event_target, DOMEvent *event)
{ {
const eventid_t eid = event->event_id; const eventid_t eid = event->event_id;
handler_vector_t *container = get_handler_vector(event_target, eid, FALSE); const listener_container_t *container = get_listener_container(event_target, eid, FALSE);
const BOOL cancelable = event_info[eid].flags & EVENT_CANCELABLE; const BOOL cancelable = event_info[eid].flags & EVENT_CANCELABLE;
event_listener_t *listener, listeners_buf[8], *listeners = listeners_buf; event_listener_t *listener, listeners_buf[8], *listeners = listeners_buf;
unsigned listeners_cnt, listeners_size; unsigned listeners_cnt, listeners_size;
...@@ -1277,20 +1299,19 @@ static void call_event_handlers(EventTarget *event_target, DOMEvent *event) ...@@ -1277,20 +1299,19 @@ static void call_event_handlers(EventTarget *event_target, DOMEvent *event)
VARIANT v; VARIANT v;
HRESULT hres; HRESULT hres;
if(container && container->handler_prop) { if(container && !list_empty(&container->listeners)) {
listener = LIST_ENTRY(list_tail(&container->listeners), event_listener_t, entry);
if(listener->function && listener->type == LISTENER_TYPE_ONEVENT) {
DISPID named_arg = DISPID_THIS; DISPID named_arg = DISPID_THIS;
VARIANTARG arg; VARIANTARG arg;
DISPPARAMS dp = {&arg, &named_arg, 1, 1}; DISPPARAMS dp = {&arg, &named_arg, 1, 1};
if(!use_event_quirks(event_target))
FIXME("Event argument not supported\n");
V_VT(&arg) = VT_DISPATCH; V_VT(&arg) = VT_DISPATCH;
V_DISPATCH(&arg) = (IDispatch*)&event_target->dispex.IDispatchEx_iface; V_DISPATCH(&arg) = (IDispatch*)&event_target->dispex.IDispatchEx_iface;
V_VT(&v) = VT_EMPTY; V_VT(&v) = VT_EMPTY;
TRACE("%s >>>\n", debugstr_w(event_info[eid].name)); TRACE("%s >>>\n", debugstr_w(event_info[eid].name));
hres = call_disp_func(container->handler_prop, &dp, &v); hres = call_disp_func(listener->function, &dp, &v);
if(hres == S_OK) { if(hres == S_OK) {
TRACE("%s <<< %s\n", debugstr_w(event_info[eid].name), debugstr_variant(&v)); TRACE("%s <<< %s\n", debugstr_w(event_info[eid].name), debugstr_variant(&v));
...@@ -1307,14 +1328,14 @@ static void call_event_handlers(EventTarget *event_target, DOMEvent *event) ...@@ -1307,14 +1328,14 @@ static void call_event_handlers(EventTarget *event_target, DOMEvent *event)
WARN("%s <<< %08x\n", debugstr_w(event_info[eid].name), hres); WARN("%s <<< %08x\n", debugstr_w(event_info[eid].name), hres);
} }
} }
}
listeners_cnt = 0; listeners_cnt = 0;
listeners_size = sizeof(listeners_buf)/sizeof(*listeners_buf); listeners_size = sizeof(listeners_buf)/sizeof(*listeners_buf);
if(container) { if(container) {
unsigned i = container->handler_cnt; LIST_FOR_EACH_ENTRY(listener, &container->listeners, event_listener_t, entry) {
while(i--) { if(!listener->function)
if(!container->handlers[i])
continue; continue;
if(listeners_cnt == listeners_size) { if(listeners_cnt == listeners_size) {
...@@ -1331,8 +1352,8 @@ static void call_event_handlers(EventTarget *event_target, DOMEvent *event) ...@@ -1331,8 +1352,8 @@ static void call_event_handlers(EventTarget *event_target, DOMEvent *event)
listeners_size *= 2; listeners_size *= 2;
} }
listeners[listeners_cnt].type = LISTENER_TYPE_ATTACHED; listeners[listeners_cnt].type = listener->type;
IDispatch_AddRef(listeners[listeners_cnt].function = container->handlers[i]); IDispatch_AddRef(listeners[listeners_cnt].function = listener->function);
listeners_cnt++; listeners_cnt++;
} }
} }
...@@ -1642,9 +1663,36 @@ static HRESULT get_event_dispex_ref(EventTarget *event_target, eventid_t eid, BO ...@@ -1642,9 +1663,36 @@ static HRESULT get_event_dispex_ref(EventTarget *event_target, eventid_t eid, BO
return dispex_get_dprop_ref(&event_target->dispex, buf, alloc, ret); return dispex_get_dprop_ref(&event_target->dispex, buf, alloc, ret);
} }
static event_listener_t *get_onevent_listener(EventTarget *event_target, eventid_t eid, BOOL alloc)
{
listener_container_t *container;
event_listener_t *listener;
container = get_listener_container(event_target, eid, alloc);
if(!container)
return NULL;
LIST_FOR_EACH_ENTRY_REV(listener, &container->listeners, event_listener_t, entry) {
if(listener->type == LISTENER_TYPE_ONEVENT)
return listener;
}
if(!alloc)
return NULL;
listener = heap_alloc(sizeof(*listener));
if(!listener)
return NULL;
listener->type = LISTENER_TYPE_ONEVENT;
listener->function = NULL;
list_add_tail(&container->listeners, &listener->entry);
return listener;
}
static void remove_event_handler(EventTarget *event_target, eventid_t eid) static void remove_event_handler(EventTarget *event_target, eventid_t eid)
{ {
handler_vector_t *handler_vector; event_listener_t *listener;
VARIANT *store; VARIANT *store;
HRESULT hres; HRESULT hres;
...@@ -1652,16 +1700,16 @@ static void remove_event_handler(EventTarget *event_target, eventid_t eid) ...@@ -1652,16 +1700,16 @@ static void remove_event_handler(EventTarget *event_target, eventid_t eid)
if(SUCCEEDED(hres)) if(SUCCEEDED(hres))
VariantClear(store); VariantClear(store);
handler_vector = get_handler_vector(event_target, eid, FALSE); listener = get_onevent_listener(event_target, eid, FALSE);
if(handler_vector && handler_vector->handler_prop) { if(listener && listener->function) {
IDispatch_Release(handler_vector->handler_prop); IDispatch_Release(listener->function);
handler_vector->handler_prop = NULL; listener->function = NULL;
} }
} }
static HRESULT set_event_handler_disp(EventTarget *event_target, eventid_t eid, IDispatch *disp) static HRESULT set_event_handler_disp(EventTarget *event_target, eventid_t eid, IDispatch *disp)
{ {
handler_vector_t *handler_vector; event_listener_t *listener;
if(event_info[eid].flags & EVENT_FIXME) if(event_info[eid].flags & EVENT_FIXME)
FIXME("unimplemented event %s\n", debugstr_w(event_info[eid].name)); FIXME("unimplemented event %s\n", debugstr_w(event_info[eid].name));
...@@ -1670,15 +1718,14 @@ static HRESULT set_event_handler_disp(EventTarget *event_target, eventid_t eid, ...@@ -1670,15 +1718,14 @@ static HRESULT set_event_handler_disp(EventTarget *event_target, eventid_t eid,
if(!disp) if(!disp)
return S_OK; return S_OK;
handler_vector = get_handler_vector(event_target, eid, TRUE); listener = get_onevent_listener(event_target, eid, TRUE);
if(!handler_vector) if(!listener)
return E_OUTOFMEMORY; return E_OUTOFMEMORY;
if(handler_vector->handler_prop) if(listener->function)
IDispatch_Release(handler_vector->handler_prop); IDispatch_Release(listener->function);
handler_vector->handler_prop = disp; IDispatch_AddRef(listener->function = disp);
IDispatch_AddRef(disp);
return S_OK; return S_OK;
} }
...@@ -1707,7 +1754,7 @@ HRESULT set_event_handler(EventTarget *event_target, eventid_t eid, VARIANT *var ...@@ -1707,7 +1754,7 @@ HRESULT set_event_handler(EventTarget *event_target, eventid_t eid, VARIANT *var
/* /*
* Setting event handler to string is a rare case and we don't want to * Setting event handler to string is a rare case and we don't want to
* complicate nor increase memory of handler_vector_t for that. Instead, * complicate nor increase memory of listener_container_t for that. Instead,
* we store the value in DispatchEx, which can already handle custom * we store the value in DispatchEx, which can already handle custom
* properties. * properties.
*/ */
...@@ -1734,7 +1781,7 @@ HRESULT set_event_handler(EventTarget *event_target, eventid_t eid, VARIANT *var ...@@ -1734,7 +1781,7 @@ HRESULT set_event_handler(EventTarget *event_target, eventid_t eid, VARIANT *var
HRESULT get_event_handler(EventTarget *event_target, eventid_t eid, VARIANT *var) HRESULT get_event_handler(EventTarget *event_target, eventid_t eid, VARIANT *var)
{ {
handler_vector_t *handler_vector; event_listener_t *listener;
VARIANT *v; VARIANT *v;
HRESULT hres; HRESULT hres;
...@@ -1744,10 +1791,10 @@ HRESULT get_event_handler(EventTarget *event_target, eventid_t eid, VARIANT *var ...@@ -1744,10 +1791,10 @@ HRESULT get_event_handler(EventTarget *event_target, eventid_t eid, VARIANT *var
return VariantCopy(var, v); return VariantCopy(var, v);
} }
handler_vector = get_handler_vector(event_target, eid, FALSE); listener = get_onevent_listener(event_target, eid, FALSE);
if(handler_vector && handler_vector->handler_prop) { if(listener && listener->function) {
V_VT(var) = VT_DISPATCH; V_VT(var) = VT_DISPATCH;
V_DISPATCH(var) = handler_vector->handler_prop; V_DISPATCH(var) = listener->function;
IDispatch_AddRef(V_DISPATCH(var)); IDispatch_AddRef(V_DISPATCH(var));
}else { }else {
V_VT(var) = VT_NULL; V_VT(var) = VT_NULL;
...@@ -1758,9 +1805,9 @@ HRESULT get_event_handler(EventTarget *event_target, eventid_t eid, VARIANT *var ...@@ -1758,9 +1805,9 @@ HRESULT get_event_handler(EventTarget *event_target, eventid_t eid, VARIANT *var
HRESULT attach_event(EventTarget *event_target, BSTR name, IDispatch *disp, VARIANT_BOOL *res) HRESULT attach_event(EventTarget *event_target, BSTR name, IDispatch *disp, VARIANT_BOOL *res)
{ {
handler_vector_t *handler_vector; listener_container_t *container;
event_listener_t *listener;
eventid_t eid; eventid_t eid;
DWORD i = 0;
eid = attr_to_eid(name); eid = attr_to_eid(name);
if(eid == EVENTID_LAST) { if(eid == EVENTID_LAST) {
...@@ -1769,28 +1816,17 @@ HRESULT attach_event(EventTarget *event_target, BSTR name, IDispatch *disp, VARI ...@@ -1769,28 +1816,17 @@ HRESULT attach_event(EventTarget *event_target, BSTR name, IDispatch *disp, VARI
return S_OK; return S_OK;
} }
if(event_info[eid].flags & EVENT_FIXME) container = get_listener_container(event_target, eid, TRUE);
FIXME("unimplemented event %s\n", debugstr_w(event_info[eid].name)); if(!container)
handler_vector = get_handler_vector(event_target, eid, TRUE);
if(!handler_vector)
return E_OUTOFMEMORY; return E_OUTOFMEMORY;
while(i < handler_vector->handler_cnt && handler_vector->handlers[i]) listener = heap_alloc(sizeof(*listener));
i++; if(!listener)
if(i == handler_vector->handler_cnt) {
if(i)
handler_vector->handlers = heap_realloc_zero(handler_vector->handlers,
(i + 1) * sizeof(*handler_vector->handlers));
else
handler_vector->handlers = heap_alloc_zero(sizeof(*handler_vector->handlers));
if(!handler_vector->handlers)
return E_OUTOFMEMORY; return E_OUTOFMEMORY;
handler_vector->handler_cnt++;
}
IDispatch_AddRef(disp); listener->type = LISTENER_TYPE_ATTACHED;
handler_vector->handlers[i] = disp; IDispatch_AddRef(listener->function = disp);
list_add_head(&container->listeners, &listener->entry);
*res = VARIANT_TRUE; *res = VARIANT_TRUE;
return S_OK; return S_OK;
...@@ -1798,9 +1834,7 @@ HRESULT attach_event(EventTarget *event_target, BSTR name, IDispatch *disp, VARI ...@@ -1798,9 +1834,7 @@ HRESULT attach_event(EventTarget *event_target, BSTR name, IDispatch *disp, VARI
HRESULT detach_event(EventTarget *event_target, BSTR name, IDispatch *disp) HRESULT detach_event(EventTarget *event_target, BSTR name, IDispatch *disp)
{ {
handler_vector_t *handler_vector;
eventid_t eid; eventid_t eid;
unsigned i;
eid = attr_to_eid(name); eid = attr_to_eid(name);
if(eid == EVENTID_LAST) { if(eid == EVENTID_LAST) {
...@@ -1808,17 +1842,7 @@ HRESULT detach_event(EventTarget *event_target, BSTR name, IDispatch *disp) ...@@ -1808,17 +1842,7 @@ HRESULT detach_event(EventTarget *event_target, BSTR name, IDispatch *disp)
return S_OK; return S_OK;
} }
handler_vector = get_handler_vector(event_target, eid, FALSE); remove_event_listener(event_target, eid, LISTENER_TYPE_ATTACHED, disp);
if(!handler_vector)
return S_OK;
for(i = 0; i < handler_vector->handler_cnt; i++) {
if(handler_vector->handlers[i] == disp) {
IDispatch_Release(handler_vector->handlers[i]);
handler_vector->handlers[i] = NULL;
}
}
return S_OK; return S_OK;
} }
...@@ -2073,7 +2097,7 @@ HRESULT EventTarget_QI(EventTarget *event_target, REFIID riid, void **ppv) ...@@ -2073,7 +2097,7 @@ HRESULT EventTarget_QI(EventTarget *event_target, REFIID riid, void **ppv)
static int event_id_cmp(const void *key, const struct wine_rb_entry *entry) static int event_id_cmp(const void *key, const struct wine_rb_entry *entry)
{ {
return (INT_PTR)key - WINE_RB_ENTRY_VALUE(entry, handler_vector_t, entry)->event_id; return (INT_PTR)key - WINE_RB_ENTRY_VALUE(entry, listener_container_t, entry)->event_id;
} }
void EventTarget_Init(EventTarget *event_target, IUnknown *outer, dispex_static_data_t *dispex_data, void EventTarget_Init(EventTarget *event_target, IUnknown *outer, dispex_static_data_t *dispex_data,
...@@ -2099,16 +2123,16 @@ void EventTarget_Init(EventTarget *event_target, IUnknown *outer, dispex_static_ ...@@ -2099,16 +2123,16 @@ void EventTarget_Init(EventTarget *event_target, IUnknown *outer, dispex_static_
void release_event_target(EventTarget *event_target) void release_event_target(EventTarget *event_target)
{ {
handler_vector_t *iter, *iter2; listener_container_t *iter, *iter2;
unsigned i;
WINE_RB_FOR_EACH_ENTRY_DESTRUCTOR(iter, iter2, &event_target->handler_map, handler_vector_t, entry) { WINE_RB_FOR_EACH_ENTRY_DESTRUCTOR(iter, iter2, &event_target->handler_map, listener_container_t, entry) {
if(iter->handler_prop) while(!list_empty(&iter->listeners)) {
IDispatch_Release(iter->handler_prop); event_listener_t *listener = LIST_ENTRY(list_head(&iter->listeners), event_listener_t, entry);
for(i = 0; i < iter->handler_cnt; i++) if(listener->function)
if(iter->handlers[i]) IDispatch_Release(listener->function);
IDispatch_Release(iter->handlers[i]); list_remove(&listener->entry);
heap_free(iter->handlers); heap_free(listener);
}
heap_free(iter); heap_free(iter);
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment