mshtml: Added IDOMEvent::removeEventListener implementation.
[wine.git] / dlls / mshtml / htmlevent.c
blob778b69297630d17f73747c2f9c8deaf1e6b55e65
1 /*
2 * Copyright 2008-2009 Jacek Caban for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include <stdarg.h>
20 #include <assert.h>
22 #define COBJMACROS
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winuser.h"
27 #include "ole2.h"
28 #include "mshtmdid.h"
30 #include "mshtml_private.h"
31 #include "htmlevent.h"
32 #include "htmlscript.h"
34 #include "wine/debug.h"
36 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
38 typedef enum {
39 LISTENER_TYPE_CAPTURE,
40 LISTENER_TYPE_BUBBLE,
41 LISTENER_TYPE_ONEVENT,
42 LISTENER_TYPE_ATTACHED
43 } listener_type_t;
45 typedef struct {
46 struct list entry;
47 listener_type_t type;
48 IDispatch *function;
49 } event_listener_t;
51 typedef struct {
52 struct wine_rb_entry entry;
53 eventid_t event_id;
54 struct list listeners;
55 } listener_container_t;
57 static const WCHAR abortW[] = {'a','b','o','r','t',0};
58 static const WCHAR beforeactivateW[] = {'b','e','f','o','r','e','a','c','t','i','v','a','t','e',0};
59 static const WCHAR beforeunloadW[] = {'b','e','f','o','r','e','u','n','l','o','a','d',0};
60 static const WCHAR blurW[] = {'b','l','u','r',0};
61 static const WCHAR changeW[] = {'c','h','a','n','g','e',0};
62 static const WCHAR clickW[] = {'c','l','i','c','k',0};
63 static const WCHAR contextmenuW[] = {'c','o','n','t','e','x','t','m','e','n','u',0};
64 static const WCHAR dataavailableW[] = {'d','a','t','a','a','v','a','i','l','a','b','l','e',0};
65 static const WCHAR dblclickW[] = {'d','b','l','c','l','i','c','k',0};
66 static const WCHAR dragW[] = {'d','r','a','g',0};
67 static const WCHAR dragstartW[] = {'d','r','a','g','s','t','a','r','t',0};
68 static const WCHAR errorW[] = {'e','r','r','o','r',0};
69 static const WCHAR focusW[] = {'f','o','c','u','s',0};
70 static const WCHAR focusinW[] = {'f','o','c','u','s','i','n',0};
71 static const WCHAR focusoutW[] = {'f','o','c','u','s','o','u','t',0};
72 static const WCHAR helpW[] = {'h','e','l','p',0};
73 static const WCHAR keydownW[] = {'k','e','y','d','o','w','n',0};
74 static const WCHAR keypressW[] = {'k','e','y','p','r','e','s','s',0};
75 static const WCHAR keyupW[] = {'k','e','y','u','p',0};
76 static const WCHAR loadW[] = {'l','o','a','d',0};
77 static const WCHAR messageW[] = {'m','e','s','s','a','g','e',0};
78 static const WCHAR mousedownW[] = {'m','o','u','s','e','d','o','w','n',0};
79 static const WCHAR mousemoveW[] = {'m','o','u','s','e','m','o','v','e',0};
80 static const WCHAR mouseoutW[] = {'m','o','u','s','e','o','u','t',0};
81 static const WCHAR mouseoverW[] = {'m','o','u','s','e','o','v','e','r',0};
82 static const WCHAR mouseupW[] = {'m','o','u','s','e','u','p',0};
83 static const WCHAR mousewheelW[] = {'m','o','u','s','e','w','h','e','e','l',0};
84 static const WCHAR pasteW[] = {'p','a','s','t','e',0};
85 static const WCHAR readystatechangeW[] = {'r','e','a','d','y','s','t','a','t','e','c','h','a','n','g','e',0};
86 static const WCHAR resizeW[] = {'r','e','s','i','z','e',0};
87 static const WCHAR scrollW[] = {'s','c','r','o','l','l',0};
88 static const WCHAR selectstartW[] = {'s','e','l','e','c','t','s','t','a','r','t',0};
89 static const WCHAR selectionchangeW[] = {'s','e','l','e','c','t','i','o','n','c','h','a','n','g','e',0};
90 static const WCHAR submitW[] = {'s','u','b','m','i','t',0};
91 static const WCHAR unloadW[] = {'u','n','l','o','a','d',0};
93 static const WCHAR EventW[] = {'E','v','e','n','t',0};
94 static const WCHAR UIEventW[] = {'U','I','E','v','e','n','t',0};
95 static const WCHAR KeyboardEventW[] = {'K','e','y','b','o','a','r','d','E','v','e','n','t',0};
96 static const WCHAR MouseEventW[] = {'M','o','u','s','e','E','v','e','n','t',0};
98 typedef enum {
99 EVENT_TYPE_EVENT,
100 EVENT_TYPE_UIEVENT,
101 EVENT_TYPE_KEYBOARD,
102 EVENT_TYPE_MOUSE,
103 EVENT_TYPE_FOCUS,
104 EVENT_TYPE_DRAG,
105 EVENT_TYPE_MESSAGE,
106 EVENT_TYPE_CLIPBOARD
107 } event_type_t;
109 static const WCHAR *event_types[] = {
110 EventW,
111 UIEventW,
112 KeyboardEventW,
113 MouseEventW,
114 EventW, /* FIXME */
115 EventW, /* FIXME */
116 EventW, /* FIXME */
117 EventW /* FIXME */
120 typedef struct {
121 const WCHAR *name;
122 event_type_t type;
123 DISPID dispid;
124 DWORD flags;
125 } event_info_t;
127 #define EVENT_DEFAULTLISTENER 0x0001
128 #define EVENT_BUBBLES 0x0002
129 #define EVENT_BIND_TO_BODY 0x0008
130 #define EVENT_CANCELABLE 0x0010
131 #define EVENT_HASDEFAULTHANDLERS 0x0020
132 #define EVENT_FIXME 0x0040
134 static const event_info_t event_info[] = {
135 {abortW, EVENT_TYPE_EVENT, DISPID_EVMETH_ONABORT,
136 EVENT_BIND_TO_BODY},
137 {beforeactivateW, EVENT_TYPE_EVENT, DISPID_EVMETH_ONBEFOREACTIVATE,
138 EVENT_FIXME | EVENT_BUBBLES | EVENT_CANCELABLE},
139 {beforeunloadW, EVENT_TYPE_EVENT, DISPID_EVMETH_ONBEFOREUNLOAD,
140 EVENT_DEFAULTLISTENER | EVENT_CANCELABLE },
141 {blurW, EVENT_TYPE_FOCUS, DISPID_EVMETH_ONBLUR,
142 EVENT_DEFAULTLISTENER},
143 {changeW, EVENT_TYPE_EVENT, DISPID_EVMETH_ONCHANGE,
144 EVENT_DEFAULTLISTENER | EVENT_BUBBLES},
145 {clickW, EVENT_TYPE_MOUSE, DISPID_EVMETH_ONCLICK,
146 EVENT_DEFAULTLISTENER | EVENT_HASDEFAULTHANDLERS | EVENT_BUBBLES | EVENT_CANCELABLE },
147 {contextmenuW, EVENT_TYPE_MOUSE, DISPID_EVMETH_ONCONTEXTMENU,
148 EVENT_BUBBLES | EVENT_CANCELABLE},
149 {dataavailableW, EVENT_TYPE_EVENT, DISPID_EVMETH_ONDATAAVAILABLE,
150 EVENT_FIXME | EVENT_BUBBLES},
151 {dblclickW, EVENT_TYPE_MOUSE, DISPID_EVMETH_ONDBLCLICK,
152 EVENT_DEFAULTLISTENER | EVENT_BUBBLES | EVENT_CANCELABLE},
153 {dragW, EVENT_TYPE_DRAG, DISPID_EVMETH_ONDRAG,
154 EVENT_FIXME | EVENT_BUBBLES | EVENT_CANCELABLE},
155 {dragstartW, EVENT_TYPE_DRAG, DISPID_EVMETH_ONDRAGSTART,
156 EVENT_FIXME | EVENT_BUBBLES | EVENT_CANCELABLE},
157 {errorW, EVENT_TYPE_EVENT, DISPID_EVMETH_ONERROR,
158 EVENT_BIND_TO_BODY},
159 {focusW, EVENT_TYPE_FOCUS, DISPID_EVMETH_ONFOCUS,
160 EVENT_DEFAULTLISTENER},
161 {focusinW, EVENT_TYPE_FOCUS, DISPID_EVMETH_ONFOCUSIN,
162 EVENT_BUBBLES},
163 {focusoutW, EVENT_TYPE_FOCUS, DISPID_EVMETH_ONFOCUSOUT,
164 EVENT_BUBBLES},
165 {helpW, EVENT_TYPE_EVENT, DISPID_EVMETH_ONHELP,
166 EVENT_BUBBLES | EVENT_CANCELABLE},
167 {keydownW, EVENT_TYPE_KEYBOARD, DISPID_EVMETH_ONKEYDOWN,
168 EVENT_DEFAULTLISTENER | EVENT_HASDEFAULTHANDLERS | EVENT_BUBBLES | EVENT_CANCELABLE },
169 {keypressW, EVENT_TYPE_KEYBOARD, DISPID_EVMETH_ONKEYPRESS,
170 EVENT_DEFAULTLISTENER | EVENT_BUBBLES | EVENT_CANCELABLE},
171 {keyupW, EVENT_TYPE_KEYBOARD, DISPID_EVMETH_ONKEYUP,
172 EVENT_DEFAULTLISTENER | EVENT_BUBBLES | EVENT_CANCELABLE},
173 {loadW, EVENT_TYPE_UIEVENT, DISPID_EVMETH_ONLOAD,
174 EVENT_BIND_TO_BODY},
175 {messageW, EVENT_TYPE_MESSAGE, DISPID_EVMETH_ONMESSAGE,
177 {mousedownW, EVENT_TYPE_MOUSE, DISPID_EVMETH_ONMOUSEDOWN,
178 EVENT_DEFAULTLISTENER | EVENT_BUBBLES | EVENT_CANCELABLE},
179 {mousemoveW, EVENT_TYPE_MOUSE, DISPID_EVMETH_ONMOUSEMOVE,
180 EVENT_DEFAULTLISTENER | EVENT_BUBBLES | EVENT_CANCELABLE},
181 {mouseoutW, EVENT_TYPE_MOUSE, DISPID_EVMETH_ONMOUSEOUT,
182 EVENT_DEFAULTLISTENER | EVENT_BUBBLES | EVENT_CANCELABLE},
183 {mouseoverW, EVENT_TYPE_MOUSE, DISPID_EVMETH_ONMOUSEOVER,
184 EVENT_DEFAULTLISTENER | EVENT_BUBBLES | EVENT_CANCELABLE},
185 {mouseupW, EVENT_TYPE_MOUSE, DISPID_EVMETH_ONMOUSEUP,
186 EVENT_DEFAULTLISTENER | EVENT_BUBBLES | EVENT_CANCELABLE},
187 {mousewheelW, EVENT_TYPE_MOUSE, DISPID_EVMETH_ONMOUSEWHEEL,
188 EVENT_FIXME},
189 {pasteW, EVENT_TYPE_CLIPBOARD, DISPID_EVMETH_ONPASTE,
190 EVENT_FIXME | EVENT_BUBBLES | EVENT_CANCELABLE},
191 {readystatechangeW, EVENT_TYPE_EVENT, DISPID_EVMETH_ONREADYSTATECHANGE,
193 {resizeW, EVENT_TYPE_UIEVENT, DISPID_EVMETH_ONRESIZE,
194 EVENT_DEFAULTLISTENER},
195 {scrollW, EVENT_TYPE_UIEVENT, DISPID_EVMETH_ONSCROLL,
196 EVENT_DEFAULTLISTENER | EVENT_BUBBLES /* FIXME: not for elements */},
197 {selectionchangeW, EVENT_TYPE_EVENT, DISPID_EVMETH_ONSELECTIONCHANGE,
198 EVENT_FIXME},
199 {selectstartW, EVENT_TYPE_EVENT, DISPID_EVMETH_ONSELECTSTART,
200 EVENT_FIXME | EVENT_BUBBLES | EVENT_CANCELABLE},
201 {submitW, EVENT_TYPE_EVENT, DISPID_EVMETH_ONSUBMIT,
202 EVENT_DEFAULTLISTENER | EVENT_HASDEFAULTHANDLERS | EVENT_BUBBLES | EVENT_CANCELABLE},
203 {unloadW, EVENT_TYPE_UIEVENT, DISPID_EVMETH_ONUNLOAD,
204 EVENT_FIXME}
207 static BOOL use_event_quirks(EventTarget*);
209 static eventid_t str_to_eid(const WCHAR *str)
211 int i;
213 for(i=0; i < sizeof(event_info)/sizeof(event_info[0]); i++) {
214 if(!strcmpW(event_info[i].name, str))
215 return i;
218 ERR("unknown type %s\n", debugstr_w(str));
219 return EVENTID_LAST;
222 static eventid_t attr_to_eid(const WCHAR *str)
224 int i;
226 if((str[0] != 'o' && str[0] != 'O') || (str[1] != 'n' && str[1] != 'N'))
227 return EVENTID_LAST;
229 for(i=0; i < sizeof(event_info)/sizeof(event_info[0]); i++) {
230 if(!strcmpW(event_info[i].name, str+2))
231 return i;
234 return EVENTID_LAST;
237 static listener_container_t *get_listener_container(EventTarget *event_target, eventid_t eid, BOOL alloc)
239 const event_target_vtbl_t *vtbl;
240 listener_container_t *container;
241 struct wine_rb_entry *entry;
243 entry = wine_rb_get(&event_target->handler_map, (const void*)eid);
244 if(entry)
245 return WINE_RB_ENTRY_VALUE(entry, listener_container_t, entry);
246 if(!alloc)
247 return NULL;
249 if(event_info[eid].flags & EVENT_FIXME)
250 FIXME("unimplemented event %s\n", debugstr_w(event_info[eid].name));
252 container = heap_alloc(sizeof(*container));
253 if(!container)
254 return NULL;
256 container->event_id = eid;
257 list_init(&container->listeners);
258 vtbl = dispex_get_vtbl(&event_target->dispex);
259 if(vtbl->bind_event)
260 vtbl->bind_event(&event_target->dispex, eid);
261 else
262 FIXME("Unsupported event binding on target %p\n", event_target);
264 wine_rb_put(&event_target->handler_map, (const void*)eid, &container->entry);
265 return container;
268 static void remove_event_listener(EventTarget *event_target, eventid_t eid, listener_type_t type, IDispatch *function)
270 listener_container_t *container;
271 event_listener_t *listener;
273 container = get_listener_container(event_target, eid, FALSE);
274 if(!container)
275 return;
277 LIST_FOR_EACH_ENTRY(listener, &container->listeners, event_listener_t, entry) {
278 if(listener->function == function && listener->type == type) {
279 IDispatch_Release(listener->function);
280 list_remove(&listener->entry);
281 heap_free(listener);
282 break;
287 typedef struct {
288 DispatchEx dispex;
289 IHTMLEventObj IHTMLEventObj_iface;
291 LONG ref;
293 const event_info_t *type;
294 DOMEvent *event;
295 VARIANT return_value;
296 } HTMLEventObj;
298 static inline HTMLEventObj *impl_from_IHTMLEventObj(IHTMLEventObj *iface)
300 return CONTAINING_RECORD(iface, HTMLEventObj, IHTMLEventObj_iface);
303 static HRESULT WINAPI HTMLEventObj_QueryInterface(IHTMLEventObj *iface, REFIID riid, void **ppv)
305 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
307 TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv);
309 if(IsEqualGUID(&IID_IUnknown, riid)) {
310 *ppv = &This->IHTMLEventObj_iface;
311 }else if(IsEqualGUID(&IID_IHTMLEventObj, riid)) {
312 *ppv = &This->IHTMLEventObj_iface;
313 }else if(dispex_query_interface(&This->dispex, riid, ppv)) {
314 return *ppv ? S_OK : E_NOINTERFACE;
315 }else {
316 *ppv = NULL;
317 WARN("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv);
318 return E_NOINTERFACE;
321 IUnknown_AddRef((IUnknown*)*ppv);
322 return S_OK;
325 static ULONG WINAPI HTMLEventObj_AddRef(IHTMLEventObj *iface)
327 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
328 LONG ref = InterlockedIncrement(&This->ref);
330 TRACE("(%p) ref=%d\n", This, ref);
332 return ref;
335 static ULONG WINAPI HTMLEventObj_Release(IHTMLEventObj *iface)
337 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
338 LONG ref = InterlockedDecrement(&This->ref);
340 TRACE("(%p) ref=%d\n", This, ref);
342 if(!ref) {
343 if(This->event)
344 IDOMEvent_Release(&This->event->IDOMEvent_iface);
345 release_dispex(&This->dispex);
346 heap_free(This);
349 return ref;
352 static HRESULT WINAPI HTMLEventObj_GetTypeInfoCount(IHTMLEventObj *iface, UINT *pctinfo)
354 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
355 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
358 static HRESULT WINAPI HTMLEventObj_GetTypeInfo(IHTMLEventObj *iface, UINT iTInfo,
359 LCID lcid, ITypeInfo **ppTInfo)
361 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
362 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo);
365 static HRESULT WINAPI HTMLEventObj_GetIDsOfNames(IHTMLEventObj *iface, REFIID riid,
366 LPOLESTR *rgszNames, UINT cNames,
367 LCID lcid, DISPID *rgDispId)
369 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
370 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface, riid, rgszNames, cNames,
371 lcid, rgDispId);
374 static HRESULT WINAPI HTMLEventObj_Invoke(IHTMLEventObj *iface, DISPID dispIdMember,
375 REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
376 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
378 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
379 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface, dispIdMember, riid, lcid,
380 wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
383 static HRESULT WINAPI HTMLEventObj_get_srcElement(IHTMLEventObj *iface, IHTMLElement **p)
385 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
387 TRACE("(%p)->(%p)\n", This, p);
389 *p = NULL;
390 if(This->event && This->event->target)
391 IDispatchEx_QueryInterface(&This->event->target->dispex.IDispatchEx_iface, &IID_IHTMLElement, (void**)p);
392 return S_OK;
395 static HRESULT WINAPI HTMLEventObj_get_altKey(IHTMLEventObj *iface, VARIANT_BOOL *p)
397 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
398 cpp_bool ret = FALSE;
400 TRACE("(%p)->(%p)\n", This, p);
402 if(This->event) {
403 nsIDOMKeyEvent *key_event;
404 nsresult nsres;
406 nsres = nsIDOMEvent_QueryInterface(This->event->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
407 if(NS_SUCCEEDED(nsres)) {
408 nsIDOMKeyEvent_GetAltKey(key_event, &ret);
409 nsIDOMKeyEvent_Release(key_event);
410 }else {
411 nsIDOMMouseEvent *mouse_event;
413 nsres = nsIDOMEvent_QueryInterface(This->event->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
414 if(NS_SUCCEEDED(nsres)) {
415 nsIDOMMouseEvent_GetAltKey(mouse_event, &ret);
416 nsIDOMMouseEvent_Release(mouse_event);
421 *p = ret ? VARIANT_TRUE : VARIANT_FALSE;
422 return S_OK;
425 static HRESULT WINAPI HTMLEventObj_get_ctrlKey(IHTMLEventObj *iface, VARIANT_BOOL *p)
427 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
428 cpp_bool ret = FALSE;
430 TRACE("(%p)->(%p)\n", This, p);
432 if(This->event) {
433 nsIDOMKeyEvent *key_event;
434 nsresult nsres;
436 nsres = nsIDOMEvent_QueryInterface(This->event->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
437 if(NS_SUCCEEDED(nsres)) {
438 nsIDOMKeyEvent_GetCtrlKey(key_event, &ret);
439 nsIDOMKeyEvent_Release(key_event);
440 }else {
441 nsIDOMMouseEvent *mouse_event;
443 nsres = nsIDOMEvent_QueryInterface(This->event->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
444 if(NS_SUCCEEDED(nsres)) {
445 nsIDOMMouseEvent_GetCtrlKey(mouse_event, &ret);
446 nsIDOMMouseEvent_Release(mouse_event);
451 *p = ret ? VARIANT_TRUE : VARIANT_FALSE;
452 return S_OK;
455 static HRESULT WINAPI HTMLEventObj_get_shiftKey(IHTMLEventObj *iface, VARIANT_BOOL *p)
457 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
458 cpp_bool ret = FALSE;
460 TRACE("(%p)->(%p)\n", This, p);
462 if(This->event) {
463 nsIDOMKeyEvent *key_event;
464 nsresult nsres;
466 nsres = nsIDOMEvent_QueryInterface(This->event->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
467 if(NS_SUCCEEDED(nsres)) {
468 nsIDOMKeyEvent_GetShiftKey(key_event, &ret);
469 nsIDOMKeyEvent_Release(key_event);
470 }else {
471 nsIDOMMouseEvent *mouse_event;
473 nsres = nsIDOMEvent_QueryInterface(This->event->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
474 if(NS_SUCCEEDED(nsres)) {
475 nsIDOMMouseEvent_GetShiftKey(mouse_event, &ret);
476 nsIDOMMouseEvent_Release(mouse_event);
481 *p = ret ? VARIANT_TRUE : VARIANT_FALSE;
482 return S_OK;
485 static HRESULT WINAPI HTMLEventObj_put_returnValue(IHTMLEventObj *iface, VARIANT v)
487 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
489 TRACE("(%p)->(%s)\n", This, debugstr_variant(&v));
491 if(V_VT(&v) != VT_BOOL) {
492 FIXME("unsupported value %s\n", debugstr_variant(&v));
493 return DISP_E_BADVARTYPE;
496 This->return_value = v;
497 if(!V_BOOL(&v) && This->event)
498 IDOMEvent_preventDefault(&This->event->IDOMEvent_iface);
499 return S_OK;
502 static HRESULT WINAPI HTMLEventObj_get_returnValue(IHTMLEventObj *iface, VARIANT *p)
504 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
506 TRACE("(%p)->(%p)\n", This, p);
508 V_VT(p) = VT_EMPTY;
509 return VariantCopy(p, &This->return_value);
512 static HRESULT WINAPI HTMLEventObj_put_cancelBubble(IHTMLEventObj *iface, VARIANT_BOOL v)
514 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
516 TRACE("(%p)->(%x)\n", This, v);
518 if(This->event)
519 IDOMEvent_stopPropagation(&This->event->IDOMEvent_iface);
520 return S_OK;
523 static HRESULT WINAPI HTMLEventObj_get_cancelBubble(IHTMLEventObj *iface, VARIANT_BOOL *p)
525 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
527 TRACE("(%p)->(%p)\n", This, p);
529 *p = This->event && This->event->stop_propagation ? VARIANT_TRUE : VARIANT_FALSE;
530 return S_OK;
533 static HRESULT WINAPI HTMLEventObj_get_fromElement(IHTMLEventObj *iface, IHTMLElement **p)
535 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
537 FIXME("(%p)->(%p)\n", This, p);
539 *p = NULL;
540 return S_OK;
543 static HRESULT WINAPI HTMLEventObj_get_toElement(IHTMLEventObj *iface, IHTMLElement **p)
545 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
547 FIXME("(%p)->(%p)\n", This, p);
549 *p = NULL;
550 return S_OK;
553 static HRESULT WINAPI HTMLEventObj_put_keyCode(IHTMLEventObj *iface, LONG v)
555 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
556 FIXME("(%p)->(%d)\n", This, v);
557 return E_NOTIMPL;
560 static HRESULT WINAPI HTMLEventObj_get_keyCode(IHTMLEventObj *iface, LONG *p)
562 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
563 UINT32 key_code = 0;
565 TRACE("(%p)->(%p)\n", This, p);
567 if(This->event) {
568 nsIDOMKeyEvent *key_event;
569 nsresult nsres;
571 nsres = nsIDOMEvent_QueryInterface(This->event->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
572 if(NS_SUCCEEDED(nsres)) {
573 nsIDOMKeyEvent_GetKeyCode(key_event, &key_code);
574 nsIDOMKeyEvent_Release(key_event);
578 *p = key_code;
579 return S_OK;
582 static HRESULT WINAPI HTMLEventObj_get_button(IHTMLEventObj *iface, LONG *p)
584 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
585 INT16 button = 0;
587 TRACE("(%p)->(%p)\n", This, p);
589 if(This->event) {
590 nsIDOMMouseEvent *mouse_event;
591 nsresult nsres;
593 nsres = nsIDOMEvent_QueryInterface(This->event->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
594 if(NS_SUCCEEDED(nsres)) {
595 nsIDOMMouseEvent_GetButton(mouse_event, &button);
596 nsIDOMMouseEvent_Release(mouse_event);
600 *p = button;
601 return S_OK;
604 static HRESULT WINAPI HTMLEventObj_get_type(IHTMLEventObj *iface, BSTR *p)
606 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
608 TRACE("(%p)->(%p)\n", This, p);
610 if(!This->type) {
611 *p = NULL;
612 return S_OK;
615 *p = SysAllocString(This->type->name);
616 return *p ? S_OK : E_OUTOFMEMORY;
619 static HRESULT WINAPI HTMLEventObj_get_qualifier(IHTMLEventObj *iface, BSTR *p)
621 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
623 FIXME("(%p)->(%p)\n", This, p);
625 *p = NULL;
626 return S_OK;
629 static HRESULT WINAPI HTMLEventObj_get_reason(IHTMLEventObj *iface, LONG *p)
631 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
633 FIXME("(%p)->(%p)\n", This, p);
635 *p = 0;
636 return S_OK;
639 static HRESULT WINAPI HTMLEventObj_get_x(IHTMLEventObj *iface, LONG *p)
641 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
642 LONG x = 0;
644 TRACE("(%p)->(%p)\n", This, p);
646 if(This->event) {
647 nsIDOMUIEvent *ui_event;
648 nsresult nsres;
650 nsres = nsIDOMEvent_QueryInterface(This->event->nsevent, &IID_nsIDOMUIEvent, (void**)&ui_event);
651 if(NS_SUCCEEDED(nsres)) {
652 /* NOTE: pageX is not exactly right here. */
653 nsres = nsIDOMUIEvent_GetPageX(ui_event, &x);
654 assert(nsres == NS_OK);
655 nsIDOMUIEvent_Release(ui_event);
659 *p = x;
660 return S_OK;
663 static HRESULT WINAPI HTMLEventObj_get_y(IHTMLEventObj *iface, LONG *p)
665 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
666 LONG y = 0;
668 TRACE("(%p)->(%p)\n", This, p);
670 if(This->event) {
671 nsIDOMUIEvent *ui_event;
672 nsresult nsres;
674 nsres = nsIDOMEvent_QueryInterface(This->event->nsevent, &IID_nsIDOMUIEvent, (void**)&ui_event);
675 if(NS_SUCCEEDED(nsres)) {
676 /* NOTE: pageY is not exactly right here. */
677 nsres = nsIDOMUIEvent_GetPageY(ui_event, &y);
678 assert(nsres == NS_OK);
679 nsIDOMUIEvent_Release(ui_event);
683 *p = y;
684 return S_OK;
687 static HRESULT WINAPI HTMLEventObj_get_clientX(IHTMLEventObj *iface, LONG *p)
689 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
690 LONG x = 0;
692 TRACE("(%p)->(%p)\n", This, p);
694 if(This->event) {
695 nsIDOMMouseEvent *mouse_event;
696 nsresult nsres;
698 nsres = nsIDOMEvent_QueryInterface(This->event->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
699 if(NS_SUCCEEDED(nsres)) {
700 nsIDOMMouseEvent_GetClientX(mouse_event, &x);
701 nsIDOMMouseEvent_Release(mouse_event);
705 *p = x;
706 return S_OK;
709 static HRESULT WINAPI HTMLEventObj_get_clientY(IHTMLEventObj *iface, LONG *p)
711 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
712 LONG y = 0;
714 TRACE("(%p)->(%p)\n", This, p);
716 if(This->event) {
717 nsIDOMMouseEvent *mouse_event;
718 nsresult nsres;
720 nsres = nsIDOMEvent_QueryInterface(This->event->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
721 if(NS_SUCCEEDED(nsres)) {
722 nsIDOMMouseEvent_GetClientY(mouse_event, &y);
723 nsIDOMMouseEvent_Release(mouse_event);
727 *p = y;
728 return S_OK;
731 static HRESULT WINAPI HTMLEventObj_get_offsetX(IHTMLEventObj *iface, LONG *p)
733 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
735 FIXME("(%p)->(%p)\n", This, p);
737 *p = 0;
738 return S_OK;
741 static HRESULT WINAPI HTMLEventObj_get_offsetY(IHTMLEventObj *iface, LONG *p)
743 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
745 FIXME("(%p)->(%p)\n", This, p);
747 *p = 0;
748 return S_OK;
751 static HRESULT WINAPI HTMLEventObj_get_screenX(IHTMLEventObj *iface, LONG *p)
753 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
754 LONG x = 0;
756 TRACE("(%p)->(%p)\n", This, p);
758 if(This->event) {
759 nsIDOMMouseEvent *mouse_event;
760 nsresult nsres;
762 nsres = nsIDOMEvent_QueryInterface(This->event->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
763 if(NS_SUCCEEDED(nsres)) {
764 nsIDOMMouseEvent_GetScreenX(mouse_event, &x);
765 nsIDOMMouseEvent_Release(mouse_event);
769 *p = x;
770 return S_OK;
773 static HRESULT WINAPI HTMLEventObj_get_screenY(IHTMLEventObj *iface, LONG *p)
775 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
776 LONG y = 0;
778 TRACE("(%p)->(%p)\n", This, p);
780 if(This->event) {
781 nsIDOMMouseEvent *mouse_event;
782 nsresult nsres;
784 nsres = nsIDOMEvent_QueryInterface(This->event->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
785 if(NS_SUCCEEDED(nsres)) {
786 nsIDOMMouseEvent_GetScreenY(mouse_event, &y);
787 nsIDOMMouseEvent_Release(mouse_event);
791 *p = y;
792 return S_OK;
795 static HRESULT WINAPI HTMLEventObj_get_srcFilter(IHTMLEventObj *iface, IDispatch **p)
797 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
799 FIXME("(%p)->(%p)\n", This, p);
801 *p = NULL;
802 return S_OK;
805 static const IHTMLEventObjVtbl HTMLEventObjVtbl = {
806 HTMLEventObj_QueryInterface,
807 HTMLEventObj_AddRef,
808 HTMLEventObj_Release,
809 HTMLEventObj_GetTypeInfoCount,
810 HTMLEventObj_GetTypeInfo,
811 HTMLEventObj_GetIDsOfNames,
812 HTMLEventObj_Invoke,
813 HTMLEventObj_get_srcElement,
814 HTMLEventObj_get_altKey,
815 HTMLEventObj_get_ctrlKey,
816 HTMLEventObj_get_shiftKey,
817 HTMLEventObj_put_returnValue,
818 HTMLEventObj_get_returnValue,
819 HTMLEventObj_put_cancelBubble,
820 HTMLEventObj_get_cancelBubble,
821 HTMLEventObj_get_fromElement,
822 HTMLEventObj_get_toElement,
823 HTMLEventObj_put_keyCode,
824 HTMLEventObj_get_keyCode,
825 HTMLEventObj_get_button,
826 HTMLEventObj_get_type,
827 HTMLEventObj_get_qualifier,
828 HTMLEventObj_get_reason,
829 HTMLEventObj_get_x,
830 HTMLEventObj_get_y,
831 HTMLEventObj_get_clientX,
832 HTMLEventObj_get_clientY,
833 HTMLEventObj_get_offsetX,
834 HTMLEventObj_get_offsetY,
835 HTMLEventObj_get_screenX,
836 HTMLEventObj_get_screenY,
837 HTMLEventObj_get_srcFilter
840 static inline HTMLEventObj *unsafe_impl_from_IHTMLEventObj(IHTMLEventObj *iface)
842 return iface->lpVtbl == &HTMLEventObjVtbl ? impl_from_IHTMLEventObj(iface) : NULL;
845 static const tid_t HTMLEventObj_iface_tids[] = {
846 IHTMLEventObj_tid,
850 static dispex_static_data_t HTMLEventObj_dispex = {
851 NULL,
852 DispCEventObj_tid,
853 HTMLEventObj_iface_tids
856 static HTMLEventObj *alloc_event_obj(DOMEvent *event)
858 HTMLEventObj *event_obj;
860 event_obj = heap_alloc_zero(sizeof(*event_obj));
861 if(!event_obj)
862 return NULL;
864 event_obj->IHTMLEventObj_iface.lpVtbl = &HTMLEventObjVtbl;
865 event_obj->ref = 1;
866 event_obj->event = event;
867 if(event)
868 IDOMEvent_AddRef(&event->IDOMEvent_iface);
870 init_dispex(&event_obj->dispex, (IUnknown*)&event_obj->IHTMLEventObj_iface, &HTMLEventObj_dispex);
871 return event_obj;
874 HRESULT create_event_obj(IHTMLEventObj **ret)
876 HTMLEventObj *event_obj;
878 event_obj = alloc_event_obj(NULL);
879 if(!event_obj)
880 return E_OUTOFMEMORY;
882 *ret = &event_obj->IHTMLEventObj_iface;
883 return S_OK;
886 static inline DOMEvent *impl_from_IDOMEvent(IDOMEvent *iface)
888 return CONTAINING_RECORD(iface, DOMEvent, IDOMEvent_iface);
891 static HRESULT WINAPI DOMEvent_QueryInterface(IDOMEvent *iface, REFIID riid, void **ppv)
893 DOMEvent *This = impl_from_IDOMEvent(iface);
895 TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv);
897 if(IsEqualGUID(&IID_IUnknown, riid))
898 *ppv = &This->IDOMEvent_iface;
899 else if(IsEqualGUID(&IID_IDOMEvent, riid))
900 *ppv = &This->IDOMEvent_iface;
901 else if(dispex_query_interface(&This->dispex, riid, ppv))
902 return *ppv ? S_OK : E_NOINTERFACE;
903 else {
904 *ppv = NULL;
905 WARN("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv);
906 return E_NOINTERFACE;
909 IUnknown_AddRef((IUnknown*)*ppv);
910 return S_OK;
913 static ULONG WINAPI DOMEvent_AddRef(IDOMEvent *iface)
915 DOMEvent *This = impl_from_IDOMEvent(iface);
916 LONG ref = InterlockedIncrement(&This->ref);
918 TRACE("(%p) ref=%u\n", This, ref);
920 return ref;
923 static ULONG WINAPI DOMEvent_Release(IDOMEvent *iface)
925 DOMEvent *This = impl_from_IDOMEvent(iface);
926 LONG ref = InterlockedDecrement(&This->ref);
928 TRACE("(%p) ref=%u\n", This, ref);
930 if(!ref) {
931 if(This->target)
932 IDispatchEx_Release(&This->target->dispex.IDispatchEx_iface);
933 nsIDOMEvent_Release(This->nsevent);
934 release_dispex(&This->dispex);
935 heap_free(This);
938 return ref;
941 static HRESULT WINAPI DOMEvent_GetTypeInfoCount(IDOMEvent *iface, UINT *pctinfo)
943 DOMEvent *This = impl_from_IDOMEvent(iface);
944 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
947 static HRESULT WINAPI DOMEvent_GetTypeInfo(IDOMEvent *iface, UINT iTInfo,
948 LCID lcid, ITypeInfo **ppTInfo)
950 DOMEvent *This = impl_from_IDOMEvent(iface);
951 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo);
954 static HRESULT WINAPI DOMEvent_GetIDsOfNames(IDOMEvent *iface, REFIID riid,
955 LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId)
957 DOMEvent *This = impl_from_IDOMEvent(iface);
958 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface, riid, rgszNames, cNames,
959 lcid, rgDispId);
962 static HRESULT WINAPI DOMEvent_Invoke(IDOMEvent *iface, DISPID dispIdMember,
963 REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult,
964 EXCEPINFO *pExcepInfo, UINT *puArgErr)
966 DOMEvent *This = impl_from_IDOMEvent(iface);
967 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface, dispIdMember, riid, lcid,
968 wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
971 static HRESULT WINAPI DOMEvent_get_bubbles(IDOMEvent *iface, VARIANT_BOOL *p)
973 DOMEvent *This = impl_from_IDOMEvent(iface);
974 FIXME("(%p)->(%p)\n", This, p);
975 return E_NOTIMPL;
978 static HRESULT WINAPI DOMEvent_get_cancelable(IDOMEvent *iface, VARIANT_BOOL *p)
980 DOMEvent *This = impl_from_IDOMEvent(iface);
981 FIXME("(%p)->(%p)\n", This, p);
982 return E_NOTIMPL;
985 static HRESULT WINAPI DOMEvent_get_currentTarget(IDOMEvent *iface, IEventTarget **p)
987 DOMEvent *This = impl_from_IDOMEvent(iface);
988 FIXME("(%p)->(%p)\n", This, p);
989 return E_NOTIMPL;
992 static HRESULT WINAPI DOMEvent_get_defaultPrevented(IDOMEvent *iface, VARIANT_BOOL *p)
994 DOMEvent *This = impl_from_IDOMEvent(iface);
995 FIXME("(%p)->(%p)\n", This, p);
996 return E_NOTIMPL;
999 static HRESULT WINAPI DOMEvent_get_eventPhase(IDOMEvent *iface, USHORT *p)
1001 DOMEvent *This = impl_from_IDOMEvent(iface);
1003 TRACE("(%p)->(%p)\n", This, p);
1005 *p = This->phase;
1006 return S_OK;
1009 static HRESULT WINAPI DOMEvent_get_target(IDOMEvent *iface, IEventTarget **p)
1011 DOMEvent *This = impl_from_IDOMEvent(iface);
1012 FIXME("(%p)->(%p)\n", This, p);
1013 return E_NOTIMPL;
1016 static HRESULT WINAPI DOMEvent_get_timeStamp(IDOMEvent *iface, ULONGLONG *p)
1018 DOMEvent *This = impl_from_IDOMEvent(iface);
1019 FIXME("(%p)->(%p)\n", This, p);
1020 return E_NOTIMPL;
1023 static HRESULT WINAPI DOMEvent_get_type(IDOMEvent *iface, BSTR *p)
1025 DOMEvent *This = impl_from_IDOMEvent(iface);
1026 FIXME("(%p)->(%p)\n", This, p);
1027 return E_NOTIMPL;
1030 static HRESULT WINAPI DOMEvent_initEvent(IDOMEvent *iface, BSTR type, VARIANT_BOOL can_bubble, VARIANT_BOOL cancelable)
1032 DOMEvent *This = impl_from_IDOMEvent(iface);
1033 FIXME("(%p)->()\n", This);
1034 return E_NOTIMPL;
1037 static HRESULT WINAPI DOMEvent_preventDefault(IDOMEvent *iface)
1039 DOMEvent *This = impl_from_IDOMEvent(iface);
1041 TRACE("(%p)\n", This);
1043 This->prevent_default = TRUE;
1044 nsIDOMEvent_PreventDefault(This->nsevent);
1045 return S_OK;
1048 static HRESULT WINAPI DOMEvent_stopPropagation(IDOMEvent *iface)
1050 DOMEvent *This = impl_from_IDOMEvent(iface);
1052 TRACE("(%p)\n", This);
1054 This->stop_propagation = TRUE;
1055 nsIDOMEvent_StopPropagation(This->nsevent);
1056 IDOMEvent_preventDefault(&This->IDOMEvent_iface);
1057 return S_OK;
1060 static HRESULT WINAPI DOMEvent_stopImmediatePropagation(IDOMEvent *iface)
1062 DOMEvent *This = impl_from_IDOMEvent(iface);
1063 FIXME("(%p)\n", This);
1064 return E_NOTIMPL;
1067 static HRESULT WINAPI DOMEvent_get_isTrusted(IDOMEvent *iface, VARIANT_BOOL *p)
1069 DOMEvent *This = impl_from_IDOMEvent(iface);
1070 FIXME("(%p)->(%p)\n", This, p);
1071 return E_NOTIMPL;
1074 static HRESULT WINAPI DOMEvent_put_cancelBubble(IDOMEvent *iface, VARIANT_BOOL v)
1076 DOMEvent *This = impl_from_IDOMEvent(iface);
1077 FIXME("(%p)->(%x)\n", This, v);
1078 return E_NOTIMPL;
1081 static HRESULT WINAPI DOMEvent_get_cancelBubble(IDOMEvent *iface, VARIANT_BOOL *p)
1083 DOMEvent *This = impl_from_IDOMEvent(iface);
1084 FIXME("(%p)->(%p)\n", This, p);
1085 return E_NOTIMPL;
1088 static HRESULT WINAPI DOMEvent_get_srcElement(IDOMEvent *iface, IHTMLElement **p)
1090 DOMEvent *This = impl_from_IDOMEvent(iface);
1091 FIXME("(%p)->(%p)\n", This, p);
1092 return E_NOTIMPL;
1095 static const IDOMEventVtbl DOMEventVtbl = {
1096 DOMEvent_QueryInterface,
1097 DOMEvent_AddRef,
1098 DOMEvent_Release,
1099 DOMEvent_GetTypeInfoCount,
1100 DOMEvent_GetTypeInfo,
1101 DOMEvent_GetIDsOfNames,
1102 DOMEvent_Invoke,
1103 DOMEvent_get_bubbles,
1104 DOMEvent_get_cancelable,
1105 DOMEvent_get_currentTarget,
1106 DOMEvent_get_defaultPrevented,
1107 DOMEvent_get_eventPhase,
1108 DOMEvent_get_target,
1109 DOMEvent_get_timeStamp,
1110 DOMEvent_get_type,
1111 DOMEvent_initEvent,
1112 DOMEvent_preventDefault,
1113 DOMEvent_stopPropagation,
1114 DOMEvent_stopImmediatePropagation,
1115 DOMEvent_get_isTrusted,
1116 DOMEvent_put_cancelBubble,
1117 DOMEvent_get_cancelBubble,
1118 DOMEvent_get_srcElement
1121 static const tid_t DOMEvent_iface_tids[] = {
1122 IDOMEvent_tid,
1126 static dispex_static_data_t DOMEvent_dispex = {
1127 NULL,
1128 IDOMEvent_tid,
1129 DOMEvent_iface_tids
1132 static DOMEvent *alloc_event(nsIDOMEvent *nsevent)
1134 DOMEvent *event;
1136 event = heap_alloc_zero(sizeof(*event));
1137 if(!event)
1138 return NULL;
1140 init_dispex(&event->dispex, (IUnknown*)&event->IDOMEvent_iface, &DOMEvent_dispex);
1141 event->IDOMEvent_iface.lpVtbl = &DOMEventVtbl;
1142 event->ref = 1;
1143 nsIDOMEvent_AddRef(event->nsevent = nsevent);
1144 event->event_id = EVENTID_LAST;
1145 return event;
1148 HRESULT create_event_from_nsevent(nsIDOMEvent *nsevent, DOMEvent **ret_event)
1150 DOMEvent *event;
1151 nsAString nsstr;
1152 nsresult nsres;
1154 event = alloc_event(nsevent);
1155 if(!event)
1156 return E_OUTOFMEMORY;
1158 nsAString_Init(&nsstr, NULL);
1159 nsres = nsIDOMEvent_GetType(event->nsevent, &nsstr);
1160 if(NS_SUCCEEDED(nsres)) {
1161 const WCHAR *type;
1162 nsAString_GetData(&nsstr, &type);
1163 event->event_id = str_to_eid(type);
1164 if(event->event_id == EVENTID_LAST)
1165 FIXME("unknown event type %s\n", debugstr_w(type));
1166 }else {
1167 ERR("GetType failed: %08x\n", nsres);
1169 nsAString_Finish(&nsstr);
1171 *ret_event = event;
1172 return S_OK;
1175 HRESULT create_document_event_str(HTMLDocumentNode *doc, const WCHAR *type, IDOMEvent **ret_event)
1177 nsIDOMEvent *nsevent;
1178 DOMEvent *event;
1179 nsAString nsstr;
1180 nsresult nsres;
1182 nsAString_InitDepend(&nsstr, type);
1183 nsres = nsIDOMHTMLDocument_CreateEvent(doc->nsdoc, &nsstr, &nsevent);
1184 nsAString_Finish(&nsstr);
1185 if(NS_FAILED(nsres)) {
1186 FIXME("CreateEvent(%s) failed: %08x\n", debugstr_w(type), nsres);
1187 return E_FAIL;
1190 event = alloc_event(nsevent);
1191 nsIDOMEvent_Release(nsevent);
1192 if(!event)
1193 return E_OUTOFMEMORY;
1195 *ret_event = &event->IDOMEvent_iface;
1196 return S_OK;
1199 HRESULT create_document_event(HTMLDocumentNode *doc, eventid_t event_id, DOMEvent **ret_event)
1201 nsIDOMEvent *nsevent;
1202 DOMEvent *event;
1203 nsAString nsstr;
1204 nsresult nsres;
1206 nsAString_InitDepend(&nsstr, event_types[event_info[event_id].type]);
1207 nsres = nsIDOMHTMLDocument_CreateEvent(doc->nsdoc, &nsstr, &nsevent);
1208 nsAString_Finish(&nsstr);
1209 if(NS_FAILED(nsres)) {
1210 FIXME("CreateEvent(%s) failed: %08x\n", debugstr_w(event_types[event_info[event_id].type]), nsres);
1211 return E_FAIL;
1214 event = alloc_event(nsevent);
1215 if(!event)
1216 return E_OUTOFMEMORY;
1218 event->event_id = event_id;
1219 *ret_event = event;
1220 return S_OK;
1223 static HRESULT call_disp_func(IDispatch *disp, DISPPARAMS *dp, VARIANT *retv)
1225 IDispatchEx *dispex;
1226 EXCEPINFO ei;
1227 HRESULT hres;
1229 memset(&ei, 0, sizeof(ei));
1231 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
1232 if(SUCCEEDED(hres)) {
1233 hres = IDispatchEx_InvokeEx(dispex, 0, GetUserDefaultLCID(), DISPATCH_METHOD, dp, retv, &ei, NULL);
1234 IDispatchEx_Release(dispex);
1235 }else {
1236 TRACE("Could not get IDispatchEx interface: %08x\n", hres);
1237 hres = IDispatch_Invoke(disp, 0, &IID_NULL, GetUserDefaultLCID(), DISPATCH_METHOD,
1238 dp, retv, &ei, NULL);
1241 return hres;
1244 static HRESULT call_cp_func(IDispatch *disp, DISPID dispid, IHTMLEventObj *event_obj, VARIANT *retv)
1246 DISPPARAMS dp = {NULL,NULL,0,0};
1247 VARIANT event_arg;
1248 ULONG argerr;
1249 EXCEPINFO ei;
1251 if(event_obj) {
1252 V_VT(&event_arg) = VT_DISPATCH;
1253 V_DISPATCH(&event_arg) = (IDispatch*)event_obj;
1254 dp.rgvarg = &event_arg;
1255 dp.cArgs = 1;
1258 memset(&ei, 0, sizeof(ei));
1259 return IDispatch_Invoke(disp, dispid, &IID_NULL, 0, DISPATCH_METHOD, &dp, retv, &ei, &argerr);
1262 static BOOL is_cp_event(cp_static_data_t *data, DISPID dispid)
1264 int min, max, i;
1265 HRESULT hres;
1267 if(!data)
1268 return FALSE;
1270 if(!data->ids) {
1271 hres = get_dispids(data->tid, &data->id_cnt, &data->ids);
1272 if(FAILED(hres))
1273 return FALSE;
1276 min = 0;
1277 max = data->id_cnt-1;
1278 while(min <= max) {
1279 i = (min+max)/2;
1280 if(data->ids[i] == dispid)
1281 return TRUE;
1283 if(data->ids[i] < dispid)
1284 min = i+1;
1285 else
1286 max = i-1;
1289 return FALSE;
1292 static void call_event_handlers(EventTarget *event_target, DOMEvent *event)
1294 const eventid_t eid = event->event_id;
1295 const listener_container_t *container = get_listener_container(event_target, eid, FALSE);
1296 const BOOL cancelable = event_info[eid].flags & EVENT_CANCELABLE;
1297 const BOOL use_quirks = use_event_quirks(event_target);
1298 event_listener_t *listener, listeners_buf[8], *listeners = listeners_buf;
1299 unsigned listeners_cnt, listeners_size;
1300 ConnectionPointContainer *cp_container = NULL;
1301 const event_target_vtbl_t *vtbl;
1302 VARIANT v;
1303 HRESULT hres;
1305 if(use_quirks && container && !list_empty(&container->listeners)
1306 && event->phase != DEP_CAPTURING_PHASE) {
1307 listener = LIST_ENTRY(list_tail(&container->listeners), event_listener_t, entry);
1308 if(listener && listener->function && listener->type == LISTENER_TYPE_ONEVENT) {
1309 DISPID named_arg = DISPID_THIS;
1310 VARIANTARG arg;
1311 DISPPARAMS dp = {&arg, &named_arg, 1, 1};
1313 V_VT(&arg) = VT_DISPATCH;
1314 V_DISPATCH(&arg) = (IDispatch*)&event_target->dispex.IDispatchEx_iface;
1315 V_VT(&v) = VT_EMPTY;
1317 TRACE("%s >>>\n", debugstr_w(event_info[eid].name));
1318 hres = call_disp_func(listener->function, &dp, &v);
1319 if(hres == S_OK) {
1320 TRACE("%s <<< %s\n", debugstr_w(event_info[eid].name), debugstr_variant(&v));
1322 if(cancelable) {
1323 if(V_VT(&v) == VT_BOOL) {
1324 if(!V_BOOL(&v))
1325 IDOMEvent_preventDefault(&event->IDOMEvent_iface);
1326 }else if(V_VT(&v) != VT_EMPTY) {
1327 FIXME("unhandled result %s\n", debugstr_variant(&v));
1330 VariantClear(&v);
1331 }else {
1332 WARN("%s <<< %08x\n", debugstr_w(event_info[eid].name), hres);
1337 listeners_cnt = 0;
1338 listeners_size = sizeof(listeners_buf)/sizeof(*listeners_buf);
1340 if(container) {
1341 LIST_FOR_EACH_ENTRY(listener, &container->listeners, event_listener_t, entry) {
1342 if(!listener->function)
1343 continue;
1344 switch(listener->type) {
1345 case LISTENER_TYPE_ONEVENT:
1346 if(use_quirks || event->phase == DEP_CAPTURING_PHASE)
1347 continue;
1348 break;
1349 case LISTENER_TYPE_CAPTURE:
1350 if(event->phase == DEP_BUBBLING_PHASE || event->in_fire_event)
1351 continue;
1352 break;
1353 case LISTENER_TYPE_BUBBLE:
1354 if(event->in_fire_event)
1355 continue;
1356 /* fallthrough */
1357 case LISTENER_TYPE_ATTACHED:
1358 if(event->phase == DEP_CAPTURING_PHASE)
1359 continue;
1360 break;
1363 if(listeners_cnt == listeners_size) {
1364 event_listener_t *new_listeners;
1365 if(listeners == listeners_buf) {
1366 new_listeners = heap_alloc(listeners_size * 2 * sizeof(*new_listeners));
1367 if(!new_listeners)
1368 break;
1369 memcpy(new_listeners, listeners, listeners_cnt * sizeof(*listeners));
1370 }else {
1371 new_listeners = heap_realloc(listeners, listeners_size * 2 * sizeof(*new_listeners));
1373 listeners = new_listeners;
1374 listeners_size *= 2;
1377 listeners[listeners_cnt].type = listener->type;
1378 IDispatch_AddRef(listeners[listeners_cnt].function = listener->function);
1379 listeners_cnt++;
1383 for(listener = listeners; listener < listeners + listeners_cnt; listener++) {
1384 if(listener->type != LISTENER_TYPE_ATTACHED) {
1385 DISPID named_arg = DISPID_THIS;
1386 VARIANTARG args[2];
1387 DISPPARAMS dp = {args, &named_arg, 2, 1};
1389 V_VT(args) = VT_DISPATCH;
1390 V_DISPATCH(args) = (IDispatch*)&event_target->dispex.IDispatchEx_iface;
1391 V_VT(args+1) = VT_DISPATCH;
1392 V_DISPATCH(args+1) = event->in_fire_event
1393 ? (IDispatch*)event->event_obj : (IDispatch*)&event->IDOMEvent_iface;
1394 V_VT(&v) = VT_EMPTY;
1396 TRACE("%s >>>\n", debugstr_w(event_info[event->event_id].name));
1397 hres = call_disp_func(listener->function, &dp, &v);
1398 if(hres == S_OK) {
1399 TRACE("%s <<< %s\n", debugstr_w(event_info[event->event_id].name),
1400 debugstr_variant(&v));
1402 if(cancelable) {
1403 if(V_VT(&v) == VT_BOOL) {
1404 if(!V_BOOL(&v))
1405 IDOMEvent_preventDefault(&event->IDOMEvent_iface);
1406 }else if(V_VT(&v) != VT_EMPTY) {
1407 FIXME("unhandled result %s\n", debugstr_variant(&v));
1410 VariantClear(&v);
1411 }else {
1412 WARN("%s <<< %08x\n", debugstr_w(event_info[event->event_id].name), hres);
1414 }else {
1415 VARIANTARG arg;
1416 DISPPARAMS dp = {&arg, NULL, 1, 0};
1418 V_VT(&arg) = VT_DISPATCH;
1419 V_DISPATCH(&arg) = (IDispatch*)event->event_obj;
1420 V_VT(&v) = VT_EMPTY;
1422 TRACE("%s attached >>>\n", debugstr_w(event_info[eid].name));
1423 hres = call_disp_func(listener->function, &dp, &v);
1424 if(hres == S_OK) {
1425 TRACE("%s attached <<<\n", debugstr_w(event_info[eid].name));
1427 if(cancelable) {
1428 if(V_VT(&v) == VT_BOOL) {
1429 if(!V_BOOL(&v))
1430 IDOMEvent_preventDefault(&event->IDOMEvent_iface);
1431 }else if(V_VT(&v) != VT_EMPTY) {
1432 FIXME("unhandled result %s\n", debugstr_variant(&v));
1435 VariantClear(&v);
1436 }else {
1437 WARN("%s attached <<< %08x\n", debugstr_w(event_info[eid].name), hres);
1442 if(listeners != listeners_buf)
1443 heap_free(listeners);
1444 if(event->phase == DEP_CAPTURING_PHASE)
1445 return;
1447 if((vtbl = dispex_get_vtbl(&event_target->dispex)) && vtbl->get_cp_container)
1448 cp_container = vtbl->get_cp_container(&event_target->dispex);
1449 if(cp_container) {
1450 if(cp_container->cps) {
1451 ConnectionPoint *cp;
1452 unsigned i, j;
1454 for(j=0; cp_container->cp_entries[j].riid; j++) {
1455 cp = cp_container->cps + j;
1456 if(!cp->sinks_size || !is_cp_event(cp->data, event_info[eid].dispid))
1457 continue;
1459 for(i=0; i < cp->sinks_size; i++) {
1460 if(!cp->sinks[i].disp)
1461 continue;
1463 V_VT(&v) = VT_EMPTY;
1465 TRACE("cp %s [%u] >>>\n", debugstr_w(event_info[eid].name), i);
1466 hres = call_cp_func(cp->sinks[i].disp, event_info[eid].dispid,
1467 cp->data->pass_event_arg ? event->event_obj : NULL, &v);
1468 if(hres == S_OK) {
1469 TRACE("cp %s [%u] <<<\n", debugstr_w(event_info[eid].name), i);
1471 if(cancelable) {
1472 if(V_VT(&v) == VT_BOOL) {
1473 if(!V_BOOL(&v))
1474 IDOMEvent_preventDefault(&event->IDOMEvent_iface);
1475 }else if(V_VT(&v) != VT_EMPTY) {
1476 FIXME("unhandled result %s\n", debugstr_variant(&v));
1479 VariantClear(&v);
1480 }else {
1481 WARN("cp %s [%u] <<< %08x\n", debugstr_w(event_info[eid].name), i, hres);
1486 IConnectionPointContainer_Release(&cp_container->IConnectionPointContainer_iface);
1490 void dispatch_event(EventTarget *event_target, DOMEvent *event)
1492 EventTarget *target_chain_buf[8], **target_chain = target_chain_buf;
1493 unsigned chain_cnt, chain_buf_size, i;
1494 const event_target_vtbl_t *vtbl, *target_vtbl;
1495 HTMLEventObj *event_obj_ref = NULL;
1496 IHTMLEventObj *prev_event = NULL;
1497 EventTarget *iter;
1498 DWORD event_flags;
1499 HRESULT hres;
1501 if(event->event_id == EVENTID_LAST) {
1502 FIXME("Unsupported on unknown events\n");
1503 return;
1506 TRACE("(%p) %s\n", event_target, debugstr_w(event_info[event->event_id].name));
1508 event_flags = event_info[event->event_id].flags;
1509 iter = event_target;
1510 IDispatchEx_AddRef(&event_target->dispex.IDispatchEx_iface);
1512 chain_cnt = 0;
1513 chain_buf_size = sizeof(target_chain_buf)/sizeof(*target_chain_buf);
1515 do {
1516 if(chain_cnt == chain_buf_size) {
1517 EventTarget **new_chain;
1518 if(target_chain == target_chain_buf) {
1519 new_chain = heap_alloc(chain_buf_size * 2 * sizeof(*new_chain));
1520 if(!new_chain)
1521 break;
1522 memcpy(new_chain, target_chain, chain_buf_size * sizeof(*new_chain));
1523 }else {
1524 new_chain = heap_realloc(target_chain, chain_buf_size * 2 * sizeof(*new_chain));
1525 if(!new_chain)
1526 break;
1528 chain_buf_size *= 2;
1529 target_chain = new_chain;
1532 target_chain[chain_cnt++] = iter;
1534 if(!(vtbl = dispex_get_vtbl(&iter->dispex)) || !vtbl->get_parent_event_target)
1535 break;
1536 iter = vtbl->get_parent_event_target(&iter->dispex);
1537 } while(iter);
1539 if(!event->event_obj && !event->no_event_obj) {
1540 event_obj_ref = alloc_event_obj(event);
1541 if(event_obj_ref) {
1542 event_obj_ref->type = event_info + event->event_id;
1543 event->event_obj = &event_obj_ref->IHTMLEventObj_iface;
1547 target_vtbl = dispex_get_vtbl(&event_target->dispex);
1548 if(target_vtbl && target_vtbl->set_current_event)
1549 prev_event = target_vtbl->set_current_event(&event_target->dispex, event->event_obj);
1551 event->target = event_target;
1552 IDispatchEx_AddRef(&event_target->dispex.IDispatchEx_iface);
1554 event->phase = DEP_AT_TARGET;
1555 call_event_handlers(target_chain[0], event);
1557 if(event_flags & EVENT_BUBBLES) {
1558 event->phase = DEP_BUBBLING_PHASE;
1559 for(i = 1; !event->stop_propagation && i < chain_cnt; i++)
1560 call_event_handlers(target_chain[i], event);
1563 if(target_vtbl && target_vtbl->set_current_event) {
1564 prev_event = target_vtbl->set_current_event(&event_target->dispex, prev_event);
1565 if(prev_event)
1566 IHTMLEventObj_Release(prev_event);
1569 if(event_flags & EVENT_HASDEFAULTHANDLERS) {
1570 for(i = 0; !event->prevent_default && i < chain_cnt; i++) {
1571 BOOL prevent_default = FALSE;
1572 vtbl = dispex_get_vtbl(&target_chain[i]->dispex);
1573 if(!vtbl || !vtbl->handle_event_default)
1574 continue;
1575 hres = vtbl->handle_event_default(&event_target->dispex, event->event_id,
1576 event->nsevent, &prevent_default);
1577 if(FAILED(hres) || event->stop_propagation)
1578 break;
1579 if(prevent_default)
1580 IDOMEvent_preventDefault(&event->IDOMEvent_iface);
1584 if(event_obj_ref) {
1585 event->event_obj = NULL;
1586 IHTMLEventObj_Release(&event_obj_ref->IHTMLEventObj_iface);
1589 for(i = 0; i < chain_cnt; i++)
1590 IDispatchEx_Release(&target_chain[i]->dispex.IDispatchEx_iface);
1591 if(target_chain != target_chain_buf)
1592 heap_free(target_chain);
1595 HRESULT fire_event(HTMLDOMNode *node, const WCHAR *event_name, VARIANT *event_var, VARIANT_BOOL *cancelled)
1597 HTMLEventObj *event_obj = NULL;
1598 eventid_t eid;
1599 HRESULT hres = S_OK;
1601 eid = attr_to_eid(event_name);
1602 if(eid == EVENTID_LAST) {
1603 WARN("unknown event %s\n", debugstr_w(event_name));
1604 return E_INVALIDARG;
1607 if(event_var && V_VT(event_var) != VT_EMPTY && V_VT(event_var) != VT_ERROR) {
1608 if(V_VT(event_var) != VT_DISPATCH) {
1609 FIXME("event_var %s not supported\n", debugstr_variant(event_var));
1610 return E_NOTIMPL;
1613 if(V_DISPATCH(event_var)) {
1614 IHTMLEventObj *event_iface;
1616 hres = IDispatch_QueryInterface(V_DISPATCH(event_var), &IID_IHTMLEventObj, (void**)&event_iface);
1617 if(FAILED(hres)) {
1618 FIXME("No IHTMLEventObj iface\n");
1619 return hres;
1622 event_obj = unsafe_impl_from_IHTMLEventObj(event_iface);
1623 if(!event_obj) {
1624 ERR("Not our IHTMLEventObj?\n");
1625 IHTMLEventObj_Release(event_iface);
1626 return E_FAIL;
1631 if(!event_obj) {
1632 event_obj = alloc_event_obj(NULL);
1633 if(!event_obj)
1634 return E_OUTOFMEMORY;
1637 event_obj->type = event_info + eid;
1638 if(!event_obj->event)
1639 hres = create_document_event(node->doc, eid, &event_obj->event);
1641 if(SUCCEEDED(hres)) {
1642 event_obj->event->event_obj = &event_obj->IHTMLEventObj_iface;
1643 event_obj->event->in_fire_event++;
1644 dispatch_event(&node->event_target, event_obj->event);
1645 event_obj->event->in_fire_event--;
1646 event_obj->event->event_obj = NULL;
1649 IHTMLEventObj_Release(&event_obj->IHTMLEventObj_iface);
1650 if(FAILED(hres))
1651 return hres;
1653 *cancelled = VARIANT_TRUE; /* FIXME */
1654 return S_OK;
1657 HRESULT ensure_doc_nsevent_handler(HTMLDocumentNode *doc, eventid_t eid)
1659 nsIDOMNode *nsnode = NULL;
1661 TRACE("%s\n", debugstr_w(event_info[eid].name));
1663 if(!doc->nsdoc)
1664 return S_OK;
1666 switch(eid) {
1667 case EVENTID_FOCUSIN:
1668 doc->event_vector[eid] = TRUE;
1669 eid = EVENTID_FOCUS;
1670 break;
1671 case EVENTID_FOCUSOUT:
1672 doc->event_vector[eid] = TRUE;
1673 eid = EVENTID_BLUR;
1674 break;
1675 default:
1676 break;
1679 if(doc->event_vector[eid] || !(event_info[eid].flags & (EVENT_DEFAULTLISTENER|EVENT_BIND_TO_BODY)))
1680 return S_OK;
1682 if(event_info[eid].flags & EVENT_BIND_TO_BODY) {
1683 nsnode = doc->node.nsnode;
1684 nsIDOMNode_AddRef(nsnode);
1687 doc->event_vector[eid] = TRUE;
1688 add_nsevent_listener(doc, nsnode, event_info[eid].name);
1690 if(nsnode)
1691 nsIDOMNode_Release(nsnode);
1692 return S_OK;
1695 void detach_events(HTMLDocumentNode *doc)
1697 if(doc->event_vector) {
1698 int i;
1700 for(i=0; i < EVENTID_LAST; i++) {
1701 if(doc->event_vector[i]) {
1702 detach_nsevent(doc, event_info[i].name);
1703 doc->event_vector[i] = FALSE;
1708 release_nsevents(doc);
1711 static HRESULT get_event_dispex_ref(EventTarget *event_target, eventid_t eid, BOOL alloc, VARIANT **ret)
1713 WCHAR buf[64];
1714 buf[0] = 'o';
1715 buf[1] = 'n';
1716 strcpyW(buf+2, event_info[eid].name);
1717 return dispex_get_dprop_ref(&event_target->dispex, buf, alloc, ret);
1720 static event_listener_t *get_onevent_listener(EventTarget *event_target, eventid_t eid, BOOL alloc)
1722 listener_container_t *container;
1723 event_listener_t *listener;
1725 container = get_listener_container(event_target, eid, alloc);
1726 if(!container)
1727 return NULL;
1729 LIST_FOR_EACH_ENTRY_REV(listener, &container->listeners, event_listener_t, entry) {
1730 if(listener->type == LISTENER_TYPE_ONEVENT)
1731 return listener;
1734 if(!alloc)
1735 return NULL;
1737 listener = heap_alloc(sizeof(*listener));
1738 if(!listener)
1739 return NULL;
1741 listener->type = LISTENER_TYPE_ONEVENT;
1742 listener->function = NULL;
1743 list_add_tail(&container->listeners, &listener->entry);
1744 return listener;
1747 static void remove_event_handler(EventTarget *event_target, eventid_t eid)
1749 event_listener_t *listener;
1750 VARIANT *store;
1751 HRESULT hres;
1753 hres = get_event_dispex_ref(event_target, eid, FALSE, &store);
1754 if(SUCCEEDED(hres))
1755 VariantClear(store);
1757 listener = get_onevent_listener(event_target, eid, FALSE);
1758 if(listener && listener->function) {
1759 IDispatch_Release(listener->function);
1760 listener->function = NULL;
1764 static HRESULT set_event_handler_disp(EventTarget *event_target, eventid_t eid, IDispatch *disp)
1766 event_listener_t *listener;
1768 if(event_info[eid].flags & EVENT_FIXME)
1769 FIXME("unimplemented event %s\n", debugstr_w(event_info[eid].name));
1771 remove_event_handler(event_target, eid);
1772 if(!disp)
1773 return S_OK;
1775 listener = get_onevent_listener(event_target, eid, TRUE);
1776 if(!listener)
1777 return E_OUTOFMEMORY;
1779 if(listener->function)
1780 IDispatch_Release(listener->function);
1782 IDispatch_AddRef(listener->function = disp);
1783 return S_OK;
1786 HRESULT set_event_handler(EventTarget *event_target, eventid_t eid, VARIANT *var)
1788 switch(V_VT(var)) {
1789 case VT_EMPTY:
1790 if(use_event_quirks(event_target)) {
1791 WARN("attempt to set to VT_EMPTY in quirks mode\n");
1792 return E_NOTIMPL;
1794 /* fall through */
1795 case VT_NULL:
1796 remove_event_handler(event_target, eid);
1797 return S_OK;
1799 case VT_DISPATCH:
1800 return set_event_handler_disp(event_target, eid, V_DISPATCH(var));
1802 case VT_BSTR: {
1803 VARIANT *v;
1804 HRESULT hres;
1806 if(!use_event_quirks(event_target))
1807 FIXME("Setting to string %s not supported\n", debugstr_w(V_BSTR(var)));
1810 * Setting event handler to string is a rare case and we don't want to
1811 * complicate nor increase memory of listener_container_t for that. Instead,
1812 * we store the value in DispatchEx, which can already handle custom
1813 * properties.
1815 remove_event_handler(event_target, eid);
1817 hres = get_event_dispex_ref(event_target, eid, TRUE, &v);
1818 if(FAILED(hres))
1819 return hres;
1821 V_BSTR(v) = SysAllocString(V_BSTR(var));
1822 if(!V_BSTR(v))
1823 return E_OUTOFMEMORY;
1824 V_VT(v) = VT_BSTR;
1825 return S_OK;
1828 default:
1829 FIXME("not handler %s\n", debugstr_variant(var));
1830 return E_NOTIMPL;
1833 return S_OK;
1836 HRESULT get_event_handler(EventTarget *event_target, eventid_t eid, VARIANT *var)
1838 event_listener_t *listener;
1839 VARIANT *v;
1840 HRESULT hres;
1842 hres = get_event_dispex_ref(event_target, eid, FALSE, &v);
1843 if(SUCCEEDED(hres) && V_VT(v) != VT_EMPTY) {
1844 V_VT(var) = VT_EMPTY;
1845 return VariantCopy(var, v);
1848 listener = get_onevent_listener(event_target, eid, FALSE);
1849 if(listener && listener->function) {
1850 V_VT(var) = VT_DISPATCH;
1851 V_DISPATCH(var) = listener->function;
1852 IDispatch_AddRef(V_DISPATCH(var));
1853 }else {
1854 V_VT(var) = VT_NULL;
1857 return S_OK;
1860 HRESULT attach_event(EventTarget *event_target, BSTR name, IDispatch *disp, VARIANT_BOOL *res)
1862 listener_container_t *container;
1863 event_listener_t *listener;
1864 eventid_t eid;
1866 eid = attr_to_eid(name);
1867 if(eid == EVENTID_LAST) {
1868 WARN("Unknown event\n");
1869 *res = VARIANT_TRUE;
1870 return S_OK;
1873 container = get_listener_container(event_target, eid, TRUE);
1874 if(!container)
1875 return E_OUTOFMEMORY;
1877 listener = heap_alloc(sizeof(*listener));
1878 if(!listener)
1879 return E_OUTOFMEMORY;
1881 listener->type = LISTENER_TYPE_ATTACHED;
1882 IDispatch_AddRef(listener->function = disp);
1883 if(use_event_quirks(event_target))
1884 list_add_head(&container->listeners, &listener->entry);
1885 else
1886 list_add_tail(&container->listeners, &listener->entry);
1888 *res = VARIANT_TRUE;
1889 return S_OK;
1892 HRESULT detach_event(EventTarget *event_target, BSTR name, IDispatch *disp)
1894 eventid_t eid;
1896 eid = attr_to_eid(name);
1897 if(eid == EVENTID_LAST) {
1898 WARN("Unknown event\n");
1899 return S_OK;
1902 remove_event_listener(event_target, eid, LISTENER_TYPE_ATTACHED, disp);
1903 return S_OK;
1906 void bind_target_event(HTMLDocumentNode *doc, EventTarget *event_target, const WCHAR *event, IDispatch *disp)
1908 eventid_t eid;
1910 TRACE("(%p %p %s %p)\n", doc, event_target, debugstr_w(event), disp);
1912 eid = attr_to_eid(event);
1913 if(eid == EVENTID_LAST) {
1914 WARN("Unsupported event %s\n", debugstr_w(event));
1915 return;
1918 set_event_handler_disp(event_target, eid, disp);
1921 void update_doc_cp_events(HTMLDocumentNode *doc, cp_static_data_t *cp)
1923 int i;
1925 for(i=0; i < EVENTID_LAST; i++) {
1926 if((event_info[i].flags & EVENT_DEFAULTLISTENER) && is_cp_event(cp, event_info[i].dispid))
1927 ensure_doc_nsevent_handler(doc, i);
1931 void check_event_attr(HTMLDocumentNode *doc, nsIDOMHTMLElement *nselem)
1933 nsIDOMMozNamedAttrMap *attr_map;
1934 const PRUnichar *name, *value;
1935 nsAString name_str, value_str;
1936 HTMLDOMNode *node = NULL;
1937 cpp_bool has_attrs;
1938 nsIDOMAttr *attr;
1939 IDispatch *disp;
1940 UINT32 length, i;
1941 eventid_t eid;
1942 nsresult nsres;
1943 HRESULT hres;
1945 nsres = nsIDOMHTMLElement_HasAttributes(nselem, &has_attrs);
1946 if(NS_FAILED(nsres) || !has_attrs)
1947 return;
1949 nsres = nsIDOMHTMLElement_GetAttributes(nselem, &attr_map);
1950 if(NS_FAILED(nsres))
1951 return;
1953 nsres = nsIDOMMozNamedAttrMap_GetLength(attr_map, &length);
1954 assert(nsres == NS_OK);
1956 nsAString_Init(&name_str, NULL);
1957 nsAString_Init(&value_str, NULL);
1959 for(i = 0; i < length; i++) {
1960 nsres = nsIDOMMozNamedAttrMap_Item(attr_map, i, &attr);
1961 if(NS_FAILED(nsres))
1962 continue;
1964 nsres = nsIDOMAttr_GetName(attr, &name_str);
1965 if(NS_FAILED(nsres)) {
1966 nsIDOMAttr_Release(attr);
1967 continue;
1970 nsAString_GetData(&name_str, &name);
1971 eid = attr_to_eid(name);
1972 if(eid == EVENTID_LAST) {
1973 nsIDOMAttr_Release(attr);
1974 continue;
1977 nsres = nsIDOMAttr_GetValue(attr, &value_str);
1978 nsIDOMAttr_Release(attr);
1979 if(NS_FAILED(nsres))
1980 continue;
1982 nsAString_GetData(&value_str, &value);
1983 if(!*value)
1984 continue;
1986 TRACE("%p.%s = %s\n", nselem, debugstr_w(name), debugstr_w(value));
1988 disp = script_parse_event(doc->window, value);
1989 if(!disp)
1990 continue;
1992 if(!node) {
1993 hres = get_node(doc, (nsIDOMNode*)nselem, TRUE, &node);
1994 if(FAILED(hres)) {
1995 IDispatch_Release(disp);
1996 break;
2000 set_event_handler_disp(get_node_event_prop_target(node, eid), eid, disp);
2001 IDispatch_Release(disp);
2004 if(node)
2005 node_release(node);
2006 nsAString_Finish(&name_str);
2007 nsAString_Finish(&value_str);
2008 nsIDOMMozNamedAttrMap_Release(attr_map);
2011 HRESULT doc_init_events(HTMLDocumentNode *doc)
2013 unsigned i;
2014 HRESULT hres;
2016 doc->event_vector = heap_alloc_zero(EVENTID_LAST*sizeof(BOOL));
2017 if(!doc->event_vector)
2018 return E_OUTOFMEMORY;
2020 init_nsevents(doc);
2022 for(i=0; i < EVENTID_LAST; i++) {
2023 if(event_info[i].flags & EVENT_HASDEFAULTHANDLERS) {
2024 hres = ensure_doc_nsevent_handler(doc, i);
2025 if(FAILED(hres))
2026 return hres;
2030 return S_OK;
2033 static inline EventTarget *impl_from_IEventTarget(IEventTarget *iface)
2035 return CONTAINING_RECORD(iface, EventTarget, IEventTarget_iface);
2038 static HRESULT WINAPI EventTarget_QueryInterface(IEventTarget *iface, REFIID riid, void **ppv)
2040 EventTarget *This = impl_from_IEventTarget(iface);
2041 return IDispatchEx_QueryInterface(&This->dispex.IDispatchEx_iface, riid, ppv);
2044 static ULONG WINAPI EventTarget_AddRef(IEventTarget *iface)
2046 EventTarget *This = impl_from_IEventTarget(iface);
2047 return IDispatchEx_AddRef(&This->dispex.IDispatchEx_iface);
2050 static ULONG WINAPI EventTarget_Release(IEventTarget *iface)
2052 EventTarget *This = impl_from_IEventTarget(iface);
2053 return IDispatchEx_Release(&This->dispex.IDispatchEx_iface);
2056 static HRESULT WINAPI EventTarget_GetTypeInfoCount(IEventTarget *iface, UINT *pctinfo)
2058 EventTarget *This = impl_from_IEventTarget(iface);
2059 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
2062 static HRESULT WINAPI EventTarget_GetTypeInfo(IEventTarget *iface, UINT iTInfo,
2063 LCID lcid, ITypeInfo **ppTInfo)
2065 EventTarget *This = impl_from_IEventTarget(iface);
2066 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo);
2069 static HRESULT WINAPI EventTarget_GetIDsOfNames(IEventTarget *iface, REFIID riid, LPOLESTR *rgszNames,
2070 UINT cNames, LCID lcid, DISPID *rgDispId)
2072 EventTarget *This = impl_from_IEventTarget(iface);
2073 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface, riid,
2074 rgszNames, cNames, lcid, rgDispId);
2077 static HRESULT WINAPI EventTarget_Invoke(IEventTarget *iface, DISPID dispIdMember,
2078 REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
2079 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
2081 EventTarget *This = impl_from_IEventTarget(iface);
2082 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface, dispIdMember,
2083 riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
2086 static HRESULT WINAPI EventTarget_addEventListener(IEventTarget *iface, BSTR type,
2087 IDispatch *function, VARIANT_BOOL capture)
2089 EventTarget *This = impl_from_IEventTarget(iface);
2090 listener_type_t listener_type = capture ? LISTENER_TYPE_CAPTURE : LISTENER_TYPE_BUBBLE;
2091 listener_container_t *container;
2092 event_listener_t *listener;
2093 eventid_t eid;
2095 TRACE("(%p)->(%s %p %x)\n", This, debugstr_w(type), function, capture);
2097 eid = str_to_eid(type);
2098 if(eid == EVENTID_LAST) {
2099 FIXME("Unsupported on event %s\n", debugstr_w(type));
2100 return E_NOTIMPL;
2104 container = get_listener_container(This, eid, TRUE);
2105 if(!container)
2106 return E_OUTOFMEMORY;
2108 /* check for duplicates */
2109 LIST_FOR_EACH_ENTRY(listener, &container->listeners, event_listener_t, entry) {
2110 if(listener->type == listener_type && listener->function == function)
2111 return S_OK;
2114 listener = heap_alloc(sizeof(*listener));
2115 if(!listener)
2116 return E_OUTOFMEMORY;
2118 listener->type = listener_type;
2119 IDispatch_AddRef(listener->function = function);
2120 list_add_tail(&container->listeners, &listener->entry);
2121 return S_OK;
2124 static HRESULT WINAPI EventTarget_removeEventListener(IEventTarget *iface, BSTR type,
2125 IDispatch *listener, VARIANT_BOOL capture)
2127 EventTarget *This = impl_from_IEventTarget(iface);
2128 eventid_t eid;
2130 TRACE("(%p)->(%s %p %x)\n", This, debugstr_w(type), listener, capture);
2132 eid = str_to_eid(type);
2133 if(eid == EVENTID_LAST) {
2134 FIXME("Unsupported on event %s\n", debugstr_w(type));
2135 return E_NOTIMPL;
2138 remove_event_listener(This, eid, capture ? LISTENER_TYPE_CAPTURE : LISTENER_TYPE_BUBBLE, listener);
2139 return S_OK;
2142 static HRESULT WINAPI EventTarget_dispatchEvent(IEventTarget *iface, IDOMEvent *event, VARIANT_BOOL *result)
2144 EventTarget *This = impl_from_IEventTarget(iface);
2145 FIXME("(%p)->(%p %p)\n", This, event, result);
2146 return E_NOTIMPL;
2149 static const IEventTargetVtbl EventTargetVtbl = {
2150 EventTarget_QueryInterface,
2151 EventTarget_AddRef,
2152 EventTarget_Release,
2153 EventTarget_GetTypeInfoCount,
2154 EventTarget_GetTypeInfo,
2155 EventTarget_GetIDsOfNames,
2156 EventTarget_Invoke,
2157 EventTarget_addEventListener,
2158 EventTarget_removeEventListener,
2159 EventTarget_dispatchEvent
2162 #define DELAY_INIT_VTBL ((const IEventTargetVtbl*)1)
2164 static BOOL use_event_quirks(EventTarget *event_target)
2166 if(event_target->IEventTarget_iface.lpVtbl == DELAY_INIT_VTBL) {
2167 event_target->IEventTarget_iface.lpVtbl =
2168 dispex_compat_mode(&event_target->dispex) >= COMPAT_MODE_IE9
2169 ? &EventTargetVtbl : NULL;
2171 return !event_target->IEventTarget_iface.lpVtbl;
2174 HRESULT EventTarget_QI(EventTarget *event_target, REFIID riid, void **ppv)
2176 if(IsEqualGUID(riid, &IID_IEventTarget)) {
2177 if(use_event_quirks(event_target)) {
2178 WARN("IEventTarget queried, but not supported by in document mode\n");
2179 *ppv = NULL;
2180 return E_NOINTERFACE;
2182 IEventTarget_AddRef(&event_target->IEventTarget_iface);
2183 *ppv = &event_target->IEventTarget_iface;
2184 return S_OK;
2187 if(dispex_query_interface(&event_target->dispex, riid, ppv))
2188 return *ppv ? S_OK : E_NOINTERFACE;
2190 WARN("(%p)->(%s %p)\n", event_target, debugstr_mshtml_guid(riid), ppv);
2191 *ppv = NULL;
2192 return E_NOINTERFACE;
2195 static int event_id_cmp(const void *key, const struct wine_rb_entry *entry)
2197 return (INT_PTR)key - WINE_RB_ENTRY_VALUE(entry, listener_container_t, entry)->event_id;
2200 void EventTarget_Init(EventTarget *event_target, IUnknown *outer, dispex_static_data_t *dispex_data,
2201 compat_mode_t compat_mode)
2203 init_dispex_with_compat_mode(&event_target->dispex, outer, dispex_data, compat_mode);
2204 wine_rb_init(&event_target->handler_map, event_id_cmp);
2207 * IEventTarget is supported by the object or not depending on compatibility mode.
2208 * We use NULL vtbl for objects in compatibility mode not supporting the interface.
2209 * For targets that don't know compatibility mode at creation time, we set vtbl
2210 * to special DELAY_INIT_VTBL value so that vtbl will be set to proper value
2211 * when it's needed.
2213 if(compat_mode == COMPAT_MODE_QUIRKS && dispex_data->vtbl && dispex_data->vtbl->get_compat_mode)
2214 event_target->IEventTarget_iface.lpVtbl = DELAY_INIT_VTBL;
2215 else if(compat_mode < COMPAT_MODE_IE9)
2216 event_target->IEventTarget_iface.lpVtbl = NULL;
2217 else
2218 event_target->IEventTarget_iface.lpVtbl = &EventTargetVtbl;
2221 void release_event_target(EventTarget *event_target)
2223 listener_container_t *iter, *iter2;
2225 WINE_RB_FOR_EACH_ENTRY_DESTRUCTOR(iter, iter2, &event_target->handler_map, listener_container_t, entry) {
2226 while(!list_empty(&iter->listeners)) {
2227 event_listener_t *listener = LIST_ENTRY(list_head(&iter->listeners), event_listener_t, entry);
2228 if(listener->function)
2229 IDispatch_Release(listener->function);
2230 list_remove(&listener->entry);
2231 heap_free(listener);
2233 heap_free(iter);