Release 2.16.
[wine.git] / dlls / mshtml / htmlevent.c
blob40f26de563df16673ad7cfbff90be64f7a2b7b17
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 struct {
39 struct wine_rb_entry entry;
40 eventid_t event_id;
41 IDispatch *handler_prop;
42 DWORD handler_cnt;
43 IDispatch **handlers;
44 } handler_vector_t;
46 static const WCHAR abortW[] = {'a','b','o','r','t',0};
47 static const WCHAR beforeactivateW[] = {'b','e','f','o','r','e','a','c','t','i','v','a','t','e',0};
48 static const WCHAR beforeunloadW[] = {'b','e','f','o','r','e','u','n','l','o','a','d',0};
49 static const WCHAR blurW[] = {'b','l','u','r',0};
50 static const WCHAR changeW[] = {'c','h','a','n','g','e',0};
51 static const WCHAR clickW[] = {'c','l','i','c','k',0};
52 static const WCHAR contextmenuW[] = {'c','o','n','t','e','x','t','m','e','n','u',0};
53 static const WCHAR dataavailableW[] = {'d','a','t','a','a','v','a','i','l','a','b','l','e',0};
54 static const WCHAR dblclickW[] = {'d','b','l','c','l','i','c','k',0};
55 static const WCHAR dragW[] = {'d','r','a','g',0};
56 static const WCHAR dragstartW[] = {'d','r','a','g','s','t','a','r','t',0};
57 static const WCHAR errorW[] = {'e','r','r','o','r',0};
58 static const WCHAR focusW[] = {'f','o','c','u','s',0};
59 static const WCHAR focusinW[] = {'f','o','c','u','s','i','n',0};
60 static const WCHAR focusoutW[] = {'f','o','c','u','s','o','u','t',0};
61 static const WCHAR helpW[] = {'h','e','l','p',0};
62 static const WCHAR keydownW[] = {'k','e','y','d','o','w','n',0};
63 static const WCHAR keypressW[] = {'k','e','y','p','r','e','s','s',0};
64 static const WCHAR keyupW[] = {'k','e','y','u','p',0};
65 static const WCHAR loadW[] = {'l','o','a','d',0};
66 static const WCHAR messageW[] = {'m','e','s','s','a','g','e',0};
67 static const WCHAR mousedownW[] = {'m','o','u','s','e','d','o','w','n',0};
68 static const WCHAR mousemoveW[] = {'m','o','u','s','e','m','o','v','e',0};
69 static const WCHAR mouseoutW[] = {'m','o','u','s','e','o','u','t',0};
70 static const WCHAR mouseoverW[] = {'m','o','u','s','e','o','v','e','r',0};
71 static const WCHAR mouseupW[] = {'m','o','u','s','e','u','p',0};
72 static const WCHAR mousewheelW[] = {'m','o','u','s','e','w','h','e','e','l',0};
73 static const WCHAR pasteW[] = {'p','a','s','t','e',0};
74 static const WCHAR readystatechangeW[] = {'r','e','a','d','y','s','t','a','t','e','c','h','a','n','g','e',0};
75 static const WCHAR resizeW[] = {'r','e','s','i','z','e',0};
76 static const WCHAR scrollW[] = {'s','c','r','o','l','l',0};
77 static const WCHAR selectstartW[] = {'s','e','l','e','c','t','s','t','a','r','t',0};
78 static const WCHAR selectionchangeW[] = {'s','e','l','e','c','t','i','o','n','c','h','a','n','g','e',0};
79 static const WCHAR submitW[] = {'s','u','b','m','i','t',0};
80 static const WCHAR unloadW[] = {'u','n','l','o','a','d',0};
82 static const WCHAR HTMLEventsW[] = {'H','T','M','L','E','v','e','n','t','s',0};
83 static const WCHAR KeyboardEventW[] = {'K','e','y','b','o','a','r','d','E','v','e','n','t',0};
84 static const WCHAR MouseEventW[] = {'M','o','u','s','e','E','v','e','n','t',0};
86 enum {
87 EVENTT_NONE,
88 EVENTT_HTML,
89 EVENTT_KEY,
90 EVENTT_MOUSE
93 static const WCHAR *event_types[] = {
94 NULL,
95 HTMLEventsW,
96 KeyboardEventW,
97 MouseEventW
100 typedef struct {
101 const WCHAR *name;
102 DWORD type;
103 DISPID dispid;
104 DWORD flags;
105 } event_info_t;
107 #define EVENT_DEFAULTLISTENER 0x0001
108 #define EVENT_BUBBLE 0x0002
109 #define EVENT_FORWARDBODY 0x0004
110 #define EVENT_BIND_TO_BODY 0x0008
111 #define EVENT_CANCELABLE 0x0010
112 #define EVENT_HASDEFAULTHANDLERS 0x0020
113 #define EVENT_FIXME 0x0040
115 static const event_info_t event_info[] = {
116 {abortW, EVENTT_NONE, DISPID_EVMETH_ONABORT,
117 EVENT_BIND_TO_BODY},
118 {beforeactivateW, EVENTT_NONE, DISPID_EVMETH_ONBEFOREACTIVATE,
119 EVENT_FIXME},
120 {beforeunloadW, EVENTT_NONE, DISPID_EVMETH_ONBEFOREUNLOAD,
121 EVENT_DEFAULTLISTENER|EVENT_FORWARDBODY},
122 {blurW, EVENTT_HTML, DISPID_EVMETH_ONBLUR,
123 EVENT_DEFAULTLISTENER},
124 {changeW, EVENTT_HTML, DISPID_EVMETH_ONCHANGE,
125 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
126 {clickW, EVENTT_MOUSE, DISPID_EVMETH_ONCLICK,
127 EVENT_DEFAULTLISTENER|EVENT_BUBBLE|EVENT_CANCELABLE|EVENT_HASDEFAULTHANDLERS},
128 {contextmenuW, EVENTT_MOUSE, DISPID_EVMETH_ONCONTEXTMENU,
129 EVENT_BUBBLE|EVENT_CANCELABLE},
130 {dataavailableW, EVENTT_NONE, DISPID_EVMETH_ONDATAAVAILABLE,
131 EVENT_BUBBLE},
132 {dblclickW, EVENTT_MOUSE, DISPID_EVMETH_ONDBLCLICK,
133 EVENT_DEFAULTLISTENER|EVENT_BUBBLE|EVENT_CANCELABLE},
134 {dragW, EVENTT_MOUSE, DISPID_EVMETH_ONDRAG,
135 EVENT_FIXME|EVENT_CANCELABLE},
136 {dragstartW, EVENTT_MOUSE, DISPID_EVMETH_ONDRAGSTART,
137 EVENT_FIXME|EVENT_CANCELABLE},
138 {errorW, EVENTT_NONE, DISPID_EVMETH_ONERROR,
139 EVENT_BIND_TO_BODY},
140 {focusW, EVENTT_HTML, DISPID_EVMETH_ONFOCUS,
141 EVENT_DEFAULTLISTENER},
142 {focusinW, EVENTT_HTML, DISPID_EVMETH_ONFOCUSIN,
143 EVENT_BUBBLE},
144 {focusoutW, EVENTT_HTML, DISPID_EVMETH_ONFOCUSOUT,
145 EVENT_BUBBLE},
146 {helpW, EVENTT_KEY, DISPID_EVMETH_ONHELP,
147 EVENT_BUBBLE|EVENT_CANCELABLE},
148 {keydownW, EVENTT_KEY, DISPID_EVMETH_ONKEYDOWN,
149 EVENT_DEFAULTLISTENER|EVENT_BUBBLE|EVENT_HASDEFAULTHANDLERS},
150 {keypressW, EVENTT_KEY, DISPID_EVMETH_ONKEYPRESS,
151 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
152 {keyupW, EVENTT_KEY, DISPID_EVMETH_ONKEYUP,
153 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
154 {loadW, EVENTT_HTML, DISPID_EVMETH_ONLOAD,
155 EVENT_BIND_TO_BODY},
156 {messageW, EVENTT_NONE, DISPID_EVMETH_ONMESSAGE,
157 EVENT_FORWARDBODY /* FIXME: remove when we get the target right */ },
158 {mousedownW, EVENTT_MOUSE, DISPID_EVMETH_ONMOUSEDOWN,
159 EVENT_DEFAULTLISTENER|EVENT_BUBBLE|EVENT_CANCELABLE},
160 {mousemoveW, EVENTT_MOUSE, DISPID_EVMETH_ONMOUSEMOVE,
161 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
162 {mouseoutW, EVENTT_MOUSE, DISPID_EVMETH_ONMOUSEOUT,
163 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
164 {mouseoverW, EVENTT_MOUSE, DISPID_EVMETH_ONMOUSEOVER,
165 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
166 {mouseupW, EVENTT_MOUSE, DISPID_EVMETH_ONMOUSEUP,
167 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
168 {mousewheelW, EVENTT_MOUSE, DISPID_EVMETH_ONMOUSEWHEEL,
169 EVENT_FIXME},
170 {pasteW, EVENTT_NONE, DISPID_EVMETH_ONPASTE,
171 EVENT_CANCELABLE},
172 {readystatechangeW, EVENTT_NONE, DISPID_EVMETH_ONREADYSTATECHANGE,
174 {resizeW, EVENTT_NONE, DISPID_EVMETH_ONRESIZE,
175 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
176 {scrollW, EVENTT_HTML, DISPID_EVMETH_ONSCROLL,
177 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
178 {selectionchangeW, EVENTT_NONE, DISPID_EVMETH_ONSELECTIONCHANGE,
179 EVENT_FIXME},
180 {selectstartW, EVENTT_MOUSE, DISPID_EVMETH_ONSELECTSTART,
181 EVENT_CANCELABLE},
182 {submitW, EVENTT_HTML, DISPID_EVMETH_ONSUBMIT,
183 EVENT_DEFAULTLISTENER|EVENT_BUBBLE|EVENT_CANCELABLE|EVENT_HASDEFAULTHANDLERS},
184 {unloadW, EVENTT_HTML, DISPID_EVMETH_ONUNLOAD,
185 EVENT_FIXME}
188 eventid_t str_to_eid(LPCWSTR str)
190 int i;
192 for(i=0; i < sizeof(event_info)/sizeof(event_info[0]); i++) {
193 if(!strcmpW(event_info[i].name, str))
194 return i;
197 ERR("unknown type %s\n", debugstr_w(str));
198 return EVENTID_LAST;
201 static eventid_t attr_to_eid(const WCHAR *str)
203 int i;
205 if((str[0] != 'o' && str[0] != 'O') || (str[1] != 'n' && str[1] != 'N'))
206 return EVENTID_LAST;
208 for(i=0; i < sizeof(event_info)/sizeof(event_info[0]); i++) {
209 if(!strcmpW(event_info[i].name, str+2))
210 return i;
213 return EVENTID_LAST;
216 struct HTMLEventObj {
217 DispatchEx dispex;
218 IHTMLEventObj IHTMLEventObj_iface;
220 LONG ref;
222 HTMLDOMNode *target;
223 const event_info_t *type;
224 nsIDOMEvent *nsevent;
225 VARIANT return_value;
226 BOOL prevent_default;
227 BOOL cancel_bubble;
230 static inline HTMLEventObj *impl_from_IHTMLEventObj(IHTMLEventObj *iface)
232 return CONTAINING_RECORD(iface, HTMLEventObj, IHTMLEventObj_iface);
235 static HRESULT WINAPI HTMLEventObj_QueryInterface(IHTMLEventObj *iface, REFIID riid, void **ppv)
237 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
239 TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv);
241 if(IsEqualGUID(&IID_IUnknown, riid)) {
242 *ppv = &This->IHTMLEventObj_iface;
243 }else if(IsEqualGUID(&IID_IHTMLEventObj, riid)) {
244 *ppv = &This->IHTMLEventObj_iface;
245 }else if(dispex_query_interface(&This->dispex, riid, ppv)) {
246 return *ppv ? S_OK : E_NOINTERFACE;
247 }else {
248 *ppv = NULL;
249 WARN("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv);
250 return E_NOINTERFACE;
253 IUnknown_AddRef((IUnknown*)*ppv);
254 return S_OK;
257 static ULONG WINAPI HTMLEventObj_AddRef(IHTMLEventObj *iface)
259 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
260 LONG ref = InterlockedIncrement(&This->ref);
262 TRACE("(%p) ref=%d\n", This, ref);
264 return ref;
267 static ULONG WINAPI HTMLEventObj_Release(IHTMLEventObj *iface)
269 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
270 LONG ref = InterlockedDecrement(&This->ref);
272 TRACE("(%p) ref=%d\n", This, ref);
274 if(!ref) {
275 if(This->target)
276 IHTMLDOMNode_Release(&This->target->IHTMLDOMNode_iface);
277 if(This->nsevent)
278 nsIDOMEvent_Release(This->nsevent);
279 release_dispex(&This->dispex);
280 heap_free(This);
283 return ref;
286 static HRESULT WINAPI HTMLEventObj_GetTypeInfoCount(IHTMLEventObj *iface, UINT *pctinfo)
288 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
289 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
292 static HRESULT WINAPI HTMLEventObj_GetTypeInfo(IHTMLEventObj *iface, UINT iTInfo,
293 LCID lcid, ITypeInfo **ppTInfo)
295 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
296 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo);
299 static HRESULT WINAPI HTMLEventObj_GetIDsOfNames(IHTMLEventObj *iface, REFIID riid,
300 LPOLESTR *rgszNames, UINT cNames,
301 LCID lcid, DISPID *rgDispId)
303 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
304 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface, riid, rgszNames, cNames,
305 lcid, rgDispId);
308 static HRESULT WINAPI HTMLEventObj_Invoke(IHTMLEventObj *iface, DISPID dispIdMember,
309 REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
310 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
312 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
313 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface, dispIdMember, riid, lcid,
314 wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
317 static HRESULT WINAPI HTMLEventObj_get_srcElement(IHTMLEventObj *iface, IHTMLElement **p)
319 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
321 TRACE("(%p)->(%p)\n", This, p);
323 *p = NULL;
324 if(This->target)
325 IHTMLDOMNode_QueryInterface(&This->target->IHTMLDOMNode_iface, &IID_IHTMLElement, (void**)p);
326 return S_OK;
329 static HRESULT WINAPI HTMLEventObj_get_altKey(IHTMLEventObj *iface, VARIANT_BOOL *p)
331 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
332 cpp_bool ret = FALSE;
334 TRACE("(%p)->(%p)\n", This, p);
336 if(This->nsevent) {
337 nsIDOMKeyEvent *key_event;
338 nsresult nsres;
340 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
341 if(NS_SUCCEEDED(nsres)) {
342 nsIDOMKeyEvent_GetAltKey(key_event, &ret);
343 nsIDOMKeyEvent_Release(key_event);
344 }else {
345 nsIDOMMouseEvent *mouse_event;
347 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
348 if(NS_SUCCEEDED(nsres)) {
349 nsIDOMMouseEvent_GetAltKey(mouse_event, &ret);
350 nsIDOMMouseEvent_Release(mouse_event);
355 *p = ret ? VARIANT_TRUE : VARIANT_FALSE;
356 return S_OK;
359 static HRESULT WINAPI HTMLEventObj_get_ctrlKey(IHTMLEventObj *iface, VARIANT_BOOL *p)
361 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
362 cpp_bool ret = FALSE;
364 TRACE("(%p)->(%p)\n", This, p);
366 if(This->nsevent) {
367 nsIDOMKeyEvent *key_event;
368 nsresult nsres;
370 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
371 if(NS_SUCCEEDED(nsres)) {
372 nsIDOMKeyEvent_GetCtrlKey(key_event, &ret);
373 nsIDOMKeyEvent_Release(key_event);
374 }else {
375 nsIDOMMouseEvent *mouse_event;
377 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
378 if(NS_SUCCEEDED(nsres)) {
379 nsIDOMMouseEvent_GetCtrlKey(mouse_event, &ret);
380 nsIDOMMouseEvent_Release(mouse_event);
385 *p = ret ? VARIANT_TRUE : VARIANT_FALSE;
386 return S_OK;
389 static HRESULT WINAPI HTMLEventObj_get_shiftKey(IHTMLEventObj *iface, VARIANT_BOOL *p)
391 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
392 cpp_bool ret = FALSE;
394 TRACE("(%p)->(%p)\n", This, p);
396 if(This->nsevent) {
397 nsIDOMKeyEvent *key_event;
398 nsresult nsres;
400 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
401 if(NS_SUCCEEDED(nsres)) {
402 nsIDOMKeyEvent_GetShiftKey(key_event, &ret);
403 nsIDOMKeyEvent_Release(key_event);
404 }else {
405 nsIDOMMouseEvent *mouse_event;
407 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
408 if(NS_SUCCEEDED(nsres)) {
409 nsIDOMMouseEvent_GetShiftKey(mouse_event, &ret);
410 nsIDOMMouseEvent_Release(mouse_event);
415 *p = ret ? VARIANT_TRUE : VARIANT_FALSE;
416 return S_OK;
419 static HRESULT WINAPI HTMLEventObj_put_returnValue(IHTMLEventObj *iface, VARIANT v)
421 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
423 TRACE("(%p)->(%s)\n", This, debugstr_variant(&v));
425 if(V_VT(&v) != VT_BOOL) {
426 FIXME("unsupported value %s\n", debugstr_variant(&v));
427 return DISP_E_BADVARTYPE;
430 This->return_value = v;
431 if(!V_BOOL(&v))
432 This->prevent_default = TRUE;
433 return S_OK;
436 static HRESULT WINAPI HTMLEventObj_get_returnValue(IHTMLEventObj *iface, VARIANT *p)
438 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
440 TRACE("(%p)->(%p)\n", This, p);
442 V_VT(p) = VT_EMPTY;
443 return VariantCopy(p, &This->return_value);
446 static HRESULT WINAPI HTMLEventObj_put_cancelBubble(IHTMLEventObj *iface, VARIANT_BOOL v)
448 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
450 TRACE("(%p)->(%x)\n", This, v);
452 This->cancel_bubble = !!v;
453 return S_OK;
456 static HRESULT WINAPI HTMLEventObj_get_cancelBubble(IHTMLEventObj *iface, VARIANT_BOOL *p)
458 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
460 TRACE("(%p)->(%p)\n", This, p);
462 *p = This->cancel_bubble ? VARIANT_TRUE : VARIANT_FALSE;
463 return S_OK;
466 static HRESULT WINAPI HTMLEventObj_get_fromElement(IHTMLEventObj *iface, IHTMLElement **p)
468 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
470 FIXME("(%p)->(%p)\n", This, p);
472 *p = NULL;
473 return S_OK;
476 static HRESULT WINAPI HTMLEventObj_get_toElement(IHTMLEventObj *iface, IHTMLElement **p)
478 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
480 FIXME("(%p)->(%p)\n", This, p);
482 *p = NULL;
483 return S_OK;
486 static HRESULT WINAPI HTMLEventObj_put_keyCode(IHTMLEventObj *iface, LONG v)
488 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
489 FIXME("(%p)->(%d)\n", This, v);
490 return E_NOTIMPL;
493 static HRESULT WINAPI HTMLEventObj_get_keyCode(IHTMLEventObj *iface, LONG *p)
495 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
496 UINT32 key_code = 0;
498 TRACE("(%p)->(%p)\n", This, p);
500 if(This->nsevent) {
501 nsIDOMKeyEvent *key_event;
502 nsresult nsres;
504 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
505 if(NS_SUCCEEDED(nsres)) {
506 nsIDOMKeyEvent_GetKeyCode(key_event, &key_code);
507 nsIDOMKeyEvent_Release(key_event);
511 *p = key_code;
512 return S_OK;
515 static HRESULT WINAPI HTMLEventObj_get_button(IHTMLEventObj *iface, LONG *p)
517 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
518 INT16 button = 0;
520 TRACE("(%p)->(%p)\n", This, p);
522 if(This->nsevent) {
523 nsIDOMMouseEvent *mouse_event;
524 nsresult nsres;
526 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
527 if(NS_SUCCEEDED(nsres)) {
528 nsIDOMMouseEvent_GetButton(mouse_event, &button);
529 nsIDOMMouseEvent_Release(mouse_event);
533 *p = button;
534 return S_OK;
537 static HRESULT WINAPI HTMLEventObj_get_type(IHTMLEventObj *iface, BSTR *p)
539 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
541 TRACE("(%p)->(%p)\n", This, p);
543 if(!This->type) {
544 *p = NULL;
545 return S_OK;
548 *p = SysAllocString(This->type->name);
549 return *p ? S_OK : E_OUTOFMEMORY;
552 static HRESULT WINAPI HTMLEventObj_get_qualifier(IHTMLEventObj *iface, BSTR *p)
554 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
556 FIXME("(%p)->(%p)\n", This, p);
558 *p = NULL;
559 return S_OK;
562 static HRESULT WINAPI HTMLEventObj_get_reason(IHTMLEventObj *iface, LONG *p)
564 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
566 FIXME("(%p)->(%p)\n", This, p);
568 *p = 0;
569 return S_OK;
572 static HRESULT WINAPI HTMLEventObj_get_x(IHTMLEventObj *iface, LONG *p)
574 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
575 LONG x = 0;
577 TRACE("(%p)->(%p)\n", This, p);
579 if(This->nsevent) {
580 nsIDOMUIEvent *ui_event;
581 nsresult nsres;
583 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMUIEvent, (void**)&ui_event);
584 if(NS_SUCCEEDED(nsres)) {
585 /* NOTE: pageX is not exactly right here. */
586 nsres = nsIDOMUIEvent_GetPageX(ui_event, &x);
587 assert(nsres == NS_OK);
588 nsIDOMUIEvent_Release(ui_event);
592 *p = x;
593 return S_OK;
596 static HRESULT WINAPI HTMLEventObj_get_y(IHTMLEventObj *iface, LONG *p)
598 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
599 LONG y = 0;
601 TRACE("(%p)->(%p)\n", This, p);
603 if(This->nsevent) {
604 nsIDOMUIEvent *ui_event;
605 nsresult nsres;
607 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMUIEvent, (void**)&ui_event);
608 if(NS_SUCCEEDED(nsres)) {
609 /* NOTE: pageY is not exactly right here. */
610 nsres = nsIDOMUIEvent_GetPageY(ui_event, &y);
611 assert(nsres == NS_OK);
612 nsIDOMUIEvent_Release(ui_event);
616 *p = y;
617 return S_OK;
620 static HRESULT WINAPI HTMLEventObj_get_clientX(IHTMLEventObj *iface, LONG *p)
622 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
623 LONG x = 0;
625 TRACE("(%p)->(%p)\n", This, p);
627 if(This->nsevent) {
628 nsIDOMMouseEvent *mouse_event;
629 nsresult nsres;
631 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
632 if(NS_SUCCEEDED(nsres)) {
633 nsIDOMMouseEvent_GetClientX(mouse_event, &x);
634 nsIDOMMouseEvent_Release(mouse_event);
638 *p = x;
639 return S_OK;
642 static HRESULT WINAPI HTMLEventObj_get_clientY(IHTMLEventObj *iface, LONG *p)
644 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
645 LONG y = 0;
647 TRACE("(%p)->(%p)\n", This, p);
649 if(This->nsevent) {
650 nsIDOMMouseEvent *mouse_event;
651 nsresult nsres;
653 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
654 if(NS_SUCCEEDED(nsres)) {
655 nsIDOMMouseEvent_GetClientY(mouse_event, &y);
656 nsIDOMMouseEvent_Release(mouse_event);
660 *p = y;
661 return S_OK;
664 static HRESULT WINAPI HTMLEventObj_get_offsetX(IHTMLEventObj *iface, LONG *p)
666 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
668 FIXME("(%p)->(%p)\n", This, p);
670 *p = 0;
671 return S_OK;
674 static HRESULT WINAPI HTMLEventObj_get_offsetY(IHTMLEventObj *iface, LONG *p)
676 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
678 FIXME("(%p)->(%p)\n", This, p);
680 *p = 0;
681 return S_OK;
684 static HRESULT WINAPI HTMLEventObj_get_screenX(IHTMLEventObj *iface, LONG *p)
686 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
687 LONG x = 0;
689 TRACE("(%p)->(%p)\n", This, p);
691 if(This->nsevent) {
692 nsIDOMMouseEvent *mouse_event;
693 nsresult nsres;
695 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
696 if(NS_SUCCEEDED(nsres)) {
697 nsIDOMMouseEvent_GetScreenX(mouse_event, &x);
698 nsIDOMMouseEvent_Release(mouse_event);
702 *p = x;
703 return S_OK;
706 static HRESULT WINAPI HTMLEventObj_get_screenY(IHTMLEventObj *iface, LONG *p)
708 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
709 LONG y = 0;
711 TRACE("(%p)->(%p)\n", This, p);
713 if(This->nsevent) {
714 nsIDOMMouseEvent *mouse_event;
715 nsresult nsres;
717 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
718 if(NS_SUCCEEDED(nsres)) {
719 nsIDOMMouseEvent_GetScreenY(mouse_event, &y);
720 nsIDOMMouseEvent_Release(mouse_event);
724 *p = y;
725 return S_OK;
728 static HRESULT WINAPI HTMLEventObj_get_srcFilter(IHTMLEventObj *iface, IDispatch **p)
730 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
732 FIXME("(%p)->(%p)\n", This, p);
734 *p = NULL;
735 return S_OK;
738 static const IHTMLEventObjVtbl HTMLEventObjVtbl = {
739 HTMLEventObj_QueryInterface,
740 HTMLEventObj_AddRef,
741 HTMLEventObj_Release,
742 HTMLEventObj_GetTypeInfoCount,
743 HTMLEventObj_GetTypeInfo,
744 HTMLEventObj_GetIDsOfNames,
745 HTMLEventObj_Invoke,
746 HTMLEventObj_get_srcElement,
747 HTMLEventObj_get_altKey,
748 HTMLEventObj_get_ctrlKey,
749 HTMLEventObj_get_shiftKey,
750 HTMLEventObj_put_returnValue,
751 HTMLEventObj_get_returnValue,
752 HTMLEventObj_put_cancelBubble,
753 HTMLEventObj_get_cancelBubble,
754 HTMLEventObj_get_fromElement,
755 HTMLEventObj_get_toElement,
756 HTMLEventObj_put_keyCode,
757 HTMLEventObj_get_keyCode,
758 HTMLEventObj_get_button,
759 HTMLEventObj_get_type,
760 HTMLEventObj_get_qualifier,
761 HTMLEventObj_get_reason,
762 HTMLEventObj_get_x,
763 HTMLEventObj_get_y,
764 HTMLEventObj_get_clientX,
765 HTMLEventObj_get_clientY,
766 HTMLEventObj_get_offsetX,
767 HTMLEventObj_get_offsetY,
768 HTMLEventObj_get_screenX,
769 HTMLEventObj_get_screenY,
770 HTMLEventObj_get_srcFilter
773 static inline HTMLEventObj *unsafe_impl_from_IHTMLEventObj(IHTMLEventObj *iface)
775 return iface->lpVtbl == &HTMLEventObjVtbl ? impl_from_IHTMLEventObj(iface) : NULL;
778 static const tid_t HTMLEventObj_iface_tids[] = {
779 IHTMLEventObj_tid,
783 static dispex_static_data_t HTMLEventObj_dispex = {
784 NULL,
785 DispCEventObj_tid,
786 HTMLEventObj_iface_tids
789 static HTMLEventObj *create_event(void)
791 HTMLEventObj *ret;
793 ret = heap_alloc_zero(sizeof(*ret));
794 if(!ret)
795 return NULL;
797 ret->IHTMLEventObj_iface.lpVtbl = &HTMLEventObjVtbl;
798 ret->ref = 1;
800 init_dispex(&ret->dispex, (IUnknown*)&ret->IHTMLEventObj_iface, &HTMLEventObj_dispex);
802 return ret;
805 static HRESULT set_event_info(HTMLEventObj *event, HTMLDOMNode *target, eventid_t eid, nsIDOMEvent *nsevent)
807 event->type = event_info+eid;
808 event->nsevent = nsevent;
810 if(nsevent) {
811 nsIDOMEvent_AddRef(nsevent);
812 }else if(event_types[event_info[eid].type]) {
813 nsAString type_str;
814 nsresult nsres;
816 nsAString_InitDepend(&type_str, event_types[event_info[eid].type]);
817 nsres = nsIDOMHTMLDocument_CreateEvent(target->doc->nsdoc, &type_str, &event->nsevent);
818 nsAString_Finish(&type_str);
819 if(NS_FAILED(nsres)) {
820 ERR("Could not create event: %08x\n", nsres);
821 return E_FAIL;
825 event->target = target;
826 if(target)
827 IHTMLDOMNode_AddRef(&target->IHTMLDOMNode_iface);
828 return S_OK;
831 HRESULT create_event_obj(IHTMLEventObj **ret)
833 HTMLEventObj *event;
835 event = create_event();
836 if(!event)
837 return E_OUTOFMEMORY;
839 *ret = &event->IHTMLEventObj_iface;
840 return S_OK;
843 static handler_vector_t *get_handler_vector(EventTarget *event_target, eventid_t eid, BOOL alloc)
845 const dispex_static_data_vtbl_t *vtbl;
846 handler_vector_t *handler_vector;
847 struct wine_rb_entry *entry;
849 vtbl = dispex_get_vtbl(&event_target->dispex);
850 if(vtbl->get_event_target)
851 event_target = vtbl->get_event_target(&event_target->dispex);
853 entry = wine_rb_get(&event_target->handler_map, (const void*)eid);
854 if(entry)
855 return WINE_RB_ENTRY_VALUE(entry, handler_vector_t, entry);
856 if(!alloc)
857 return NULL;
859 handler_vector = heap_alloc_zero(sizeof(*handler_vector));
860 if(!handler_vector)
861 return NULL;
863 handler_vector->event_id = eid;
864 vtbl = dispex_get_vtbl(&event_target->dispex);
865 if(vtbl->bind_event)
866 vtbl->bind_event(&event_target->dispex, eid);
867 else
868 FIXME("Unsupported event binding on target %p\n", event_target);
870 wine_rb_put(&event_target->handler_map, (const void*)eid, &handler_vector->entry);
871 return handler_vector;
874 static HRESULT call_disp_func(IDispatch *disp, DISPPARAMS *dp, VARIANT *retv)
876 IDispatchEx *dispex;
877 EXCEPINFO ei;
878 HRESULT hres;
880 memset(&ei, 0, sizeof(ei));
882 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
883 if(SUCCEEDED(hres)) {
884 hres = IDispatchEx_InvokeEx(dispex, 0, GetUserDefaultLCID(), DISPATCH_METHOD, dp, retv, &ei, NULL);
885 IDispatchEx_Release(dispex);
886 }else {
887 TRACE("Could not get IDispatchEx interface: %08x\n", hres);
888 hres = IDispatch_Invoke(disp, 0, &IID_NULL, GetUserDefaultLCID(), DISPATCH_METHOD,
889 dp, retv, &ei, NULL);
892 return hres;
895 static HRESULT call_cp_func(IDispatch *disp, DISPID dispid, HTMLEventObj *event_obj, VARIANT *retv)
897 DISPPARAMS dp = {NULL,NULL,0,0};
898 VARIANT event_arg;
899 ULONG argerr;
900 EXCEPINFO ei;
902 if(event_obj) {
903 V_VT(&event_arg) = VT_DISPATCH;
904 V_DISPATCH(&event_arg) = (IDispatch*)&event_obj->IHTMLEventObj_iface;
905 dp.rgvarg = &event_arg;
906 dp.cArgs = 1;
909 memset(&ei, 0, sizeof(ei));
910 return IDispatch_Invoke(disp, dispid, &IID_NULL, 0, DISPATCH_METHOD, &dp, retv, &ei, &argerr);
913 static BOOL is_cp_event(cp_static_data_t *data, DISPID dispid)
915 int min, max, i;
916 HRESULT hres;
918 if(!data)
919 return FALSE;
921 if(!data->ids) {
922 hres = get_dispids(data->tid, &data->id_cnt, &data->ids);
923 if(FAILED(hres))
924 return FALSE;
927 min = 0;
928 max = data->id_cnt-1;
929 while(min <= max) {
930 i = (min+max)/2;
931 if(data->ids[i] == dispid)
932 return TRUE;
934 if(data->ids[i] < dispid)
935 min = i+1;
936 else
937 max = i-1;
940 return FALSE;
943 void call_event_handlers(HTMLDocumentNode *doc, HTMLEventObj *event_obj, EventTarget *event_target,
944 ConnectionPointContainer *cp_container, eventid_t eid, IDispatch *this_obj)
946 handler_vector_t *handler_vector = get_handler_vector(event_target, eid, FALSE);
947 const BOOL cancelable = event_info[eid].flags & EVENT_CANCELABLE;
948 VARIANT v;
949 HRESULT hres;
951 if(handler_vector && handler_vector->handler_prop) {
952 DISPID named_arg = DISPID_THIS;
953 VARIANTARG arg;
954 DISPPARAMS dp = {&arg, &named_arg, 1, 1};
956 V_VT(&arg) = VT_DISPATCH;
957 V_DISPATCH(&arg) = this_obj;
958 V_VT(&v) = VT_EMPTY;
960 TRACE("%s >>>\n", debugstr_w(event_info[eid].name));
961 hres = call_disp_func(handler_vector->handler_prop, &dp, &v);
962 if(hres == S_OK) {
963 TRACE("%s <<< %s\n", debugstr_w(event_info[eid].name), debugstr_variant(&v));
965 if(cancelable) {
966 if(V_VT(&v) == VT_BOOL) {
967 if(!V_BOOL(&v))
968 event_obj->prevent_default = TRUE;
969 }else if(V_VT(&v) != VT_EMPTY) {
970 FIXME("unhandled result %s\n", debugstr_variant(&v));
973 VariantClear(&v);
974 }else {
975 WARN("%s <<< %08x\n", debugstr_w(event_info[eid].name), hres);
979 if(handler_vector && handler_vector->handler_cnt) {
980 VARIANTARG arg;
981 DISPPARAMS dp = {&arg, NULL, 1, 0};
982 int i;
984 V_VT(&arg) = VT_DISPATCH;
985 V_DISPATCH(&arg) = (IDispatch*)&event_obj->dispex.IDispatchEx_iface;
987 i = handler_vector->handler_cnt;
988 while(i--) {
989 if(handler_vector->handlers[i]) {
990 V_VT(&v) = VT_EMPTY;
992 TRACE("%s [%d] >>>\n", debugstr_w(event_info[eid].name), i);
993 hres = call_disp_func(handler_vector->handlers[i], &dp, &v);
994 if(hres == S_OK) {
995 TRACE("%s [%d] <<<\n", debugstr_w(event_info[eid].name), i);
997 if(cancelable) {
998 if(V_VT(&v) == VT_BOOL) {
999 if(!V_BOOL(&v))
1000 event_obj->prevent_default = TRUE;
1001 }else if(V_VT(&v) != VT_EMPTY) {
1002 FIXME("unhandled result %s\n", debugstr_variant(&v));
1005 VariantClear(&v);
1006 }else {
1007 WARN("%s [%d] <<< %08x\n", debugstr_w(event_info[eid].name), i, hres);
1014 * NOTE: CP events may require doc_obj reference, which we don't own. We make sure that
1015 * it's safe to call event handler by checking nsevent_listener, which is NULL for
1016 * detached documents.
1018 if(cp_container && cp_container->forward_container)
1019 cp_container = cp_container->forward_container;
1020 if(cp_container && cp_container->cps && doc->nsevent_listener) {
1021 ConnectionPoint *cp;
1022 unsigned i, j;
1024 for(j=0; cp_container->cp_entries[j].riid; j++) {
1025 cp = cp_container->cps + j;
1026 if(!cp->sinks_size || !is_cp_event(cp->data, event_info[eid].dispid))
1027 continue;
1029 for(i=0; doc->nsevent_listener && i < cp->sinks_size; i++) {
1030 if(!cp->sinks[i].disp)
1031 continue;
1033 V_VT(&v) = VT_EMPTY;
1035 TRACE("cp %s [%u] >>>\n", debugstr_w(event_info[eid].name), i);
1036 hres = call_cp_func(cp->sinks[i].disp, event_info[eid].dispid,
1037 cp->data->pass_event_arg ? event_obj : NULL, &v);
1038 if(hres == S_OK) {
1039 TRACE("cp %s [%u] <<<\n", debugstr_w(event_info[eid].name), i);
1041 if(cancelable) {
1042 if(V_VT(&v) == VT_BOOL) {
1043 if(!V_BOOL(&v))
1044 event_obj->prevent_default = TRUE;
1045 }else if(V_VT(&v) != VT_EMPTY) {
1046 FIXME("unhandled result %s\n", debugstr_variant(&v));
1049 VariantClear(&v);
1050 }else {
1051 WARN("cp %s [%u] <<< %08x\n", debugstr_w(event_info[eid].name), i, hres);
1055 if(!doc->nsevent_listener)
1056 break;
1061 static void fire_event_obj(HTMLDocumentNode *doc, eventid_t eid, HTMLEventObj *event_obj,
1062 HTMLDOMNode *target, IDispatch *script_this)
1064 IHTMLEventObj *prev_event;
1065 nsIDOMNode *parent, *nsnode;
1066 BOOL prevent_default = FALSE;
1067 HTMLInnerWindow *window;
1068 HTMLDOMNode *node;
1069 UINT16 node_type;
1070 nsresult nsres;
1071 HRESULT hres;
1073 TRACE("(%p) %s\n", doc, debugstr_w(event_info[eid].name));
1075 window = doc->window;
1076 if(!window) {
1077 WARN("NULL window\n");
1078 return;
1081 htmldoc_addref(&doc->basedoc);
1083 prev_event = window->event;
1084 window->event = event_obj ? &event_obj->IHTMLEventObj_iface : NULL;
1086 nsIDOMNode_GetNodeType(target->nsnode, &node_type);
1087 nsnode = target->nsnode;
1088 nsIDOMNode_AddRef(nsnode);
1090 switch(node_type) {
1091 case ELEMENT_NODE:
1092 do {
1093 hres = get_node(doc, nsnode, FALSE, &node);
1094 if(SUCCEEDED(hres) && node) {
1095 call_event_handlers(doc, event_obj, &node->event_target, node->cp_container, eid,
1096 script_this ? script_this : (IDispatch*)&node->IHTMLDOMNode_iface);
1097 node_release(node);
1100 if(!(event_info[eid].flags & EVENT_BUBBLE) || (event_obj && event_obj->cancel_bubble))
1101 break;
1103 nsIDOMNode_GetParentNode(nsnode, &parent);
1104 nsIDOMNode_Release(nsnode);
1105 nsnode = parent;
1106 if(!nsnode)
1107 break;
1109 nsIDOMNode_GetNodeType(nsnode, &node_type);
1110 }while(node_type == ELEMENT_NODE);
1112 if(!(event_info[eid].flags & EVENT_BUBBLE) || (event_obj && event_obj->cancel_bubble))
1113 break;
1115 case DOCUMENT_NODE:
1116 if(event_info[eid].flags & EVENT_FORWARDBODY) {
1117 nsIDOMHTMLElement *nsbody;
1118 nsresult nsres;
1120 nsres = nsIDOMHTMLDocument_GetBody(doc->nsdoc, &nsbody);
1121 if(NS_SUCCEEDED(nsres) && nsbody) {
1122 hres = get_node(doc, (nsIDOMNode*)nsbody, FALSE, &node);
1123 if(SUCCEEDED(hres) && node) {
1124 call_event_handlers(doc, event_obj, &node->event_target, node->cp_container, eid,
1125 script_this ? script_this : (IDispatch*)&node->IHTMLDOMNode_iface);
1126 node_release(node);
1128 nsIDOMHTMLElement_Release(nsbody);
1129 }else {
1130 ERR("Could not get body: %08x\n", nsres);
1134 call_event_handlers(doc, event_obj, &doc->node.event_target, &doc->basedoc.cp_container, eid,
1135 script_this ? script_this : (IDispatch*)&doc->basedoc.IHTMLDocument2_iface);
1136 break;
1138 default:
1139 FIXME("unimplemented node type %d\n", node_type);
1142 if(nsnode)
1143 nsIDOMNode_Release(nsnode);
1145 if(event_obj && event_obj->prevent_default)
1146 prevent_default = TRUE;
1147 window->event = prev_event;
1149 if(!prevent_default && (event_info[eid].flags & EVENT_HASDEFAULTHANDLERS)) {
1150 nsnode = target->nsnode;
1151 nsIDOMNode_AddRef(nsnode);
1153 do {
1154 hres = get_node(doc, nsnode, TRUE, &node);
1155 if(FAILED(hres))
1156 break;
1158 if(node) {
1159 if(node->vtbl->handle_event)
1160 hres = node->vtbl->handle_event(node, eid, event_obj ? event_obj->nsevent : NULL, &prevent_default);
1161 node_release(node);
1162 if(FAILED(hres) || prevent_default || (event_obj && event_obj->cancel_bubble))
1163 break;
1166 nsres = nsIDOMNode_GetParentNode(nsnode, &parent);
1167 if(NS_FAILED(nsres))
1168 break;
1170 nsIDOMNode_Release(nsnode);
1171 nsnode = parent;
1172 } while(nsnode);
1174 if(nsnode)
1175 nsIDOMNode_Release(nsnode);
1178 if(prevent_default && event_obj && event_obj->nsevent) {
1179 TRACE("calling PreventDefault\n");
1180 nsIDOMEvent_PreventDefault(event_obj->nsevent);
1183 htmldoc_release(&doc->basedoc);
1186 void fire_event(HTMLDocumentNode *doc, eventid_t eid, BOOL set_event, HTMLDOMNode *target, nsIDOMEvent *nsevent,
1187 IDispatch *script_this)
1189 HTMLEventObj *event_obj = NULL;
1190 HRESULT hres;
1192 if(set_event) {
1193 event_obj = create_event();
1194 if(!event_obj)
1195 return;
1197 hres = set_event_info(event_obj, target, eid, nsevent);
1198 if(FAILED(hres)) {
1199 IHTMLEventObj_Release(&event_obj->IHTMLEventObj_iface);
1200 return;
1204 fire_event_obj(doc, eid, event_obj, target, script_this);
1206 if(event_obj)
1207 IHTMLEventObj_Release(&event_obj->IHTMLEventObj_iface);
1210 HRESULT dispatch_event(HTMLDOMNode *node, const WCHAR *event_name, VARIANT *event_var, VARIANT_BOOL *cancelled)
1212 HTMLEventObj *event_obj = NULL;
1213 eventid_t eid;
1214 HRESULT hres;
1216 eid = attr_to_eid(event_name);
1217 if(eid == EVENTID_LAST) {
1218 WARN("unknown event %s\n", debugstr_w(event_name));
1219 return E_INVALIDARG;
1222 if(event_var && V_VT(event_var) != VT_EMPTY && V_VT(event_var) != VT_ERROR) {
1223 if(V_VT(event_var) != VT_DISPATCH) {
1224 FIXME("event_var %s not supported\n", debugstr_variant(event_var));
1225 return E_NOTIMPL;
1228 if(V_DISPATCH(event_var)) {
1229 IHTMLEventObj *event_iface;
1231 hres = IDispatch_QueryInterface(V_DISPATCH(event_var), &IID_IHTMLEventObj, (void**)&event_iface);
1232 if(FAILED(hres)) {
1233 FIXME("No IHTMLEventObj iface\n");
1234 return hres;
1237 event_obj = unsafe_impl_from_IHTMLEventObj(event_iface);
1238 if(!event_obj) {
1239 ERR("Not our IHTMLEventObj?\n");
1240 IHTMLEventObj_Release(event_iface);
1241 return E_FAIL;
1246 if(event_obj) {
1247 hres = set_event_info(event_obj, node, eid, NULL);
1248 if(SUCCEEDED(hres))
1249 fire_event_obj(node->doc, eid, event_obj, node, NULL);
1251 IHTMLEventObj_Release(&event_obj->IHTMLEventObj_iface);
1252 if(FAILED(hres))
1253 return hres;
1254 }else {
1255 fire_event(node->doc, eid, TRUE, node, NULL, NULL);
1258 *cancelled = VARIANT_TRUE; /* FIXME */
1259 return S_OK;
1262 HRESULT call_fire_event(HTMLDOMNode *node, eventid_t eid)
1264 HRESULT hres;
1266 if(node->vtbl->fire_event) {
1267 BOOL handled = FALSE;
1269 hres = node->vtbl->fire_event(node, eid, &handled);
1270 if(handled)
1271 return hres;
1274 fire_event(node->doc, eid, TRUE, node, NULL, NULL);
1275 return S_OK;
1278 HRESULT ensure_doc_nsevent_handler(HTMLDocumentNode *doc, eventid_t eid)
1280 nsIDOMNode *nsnode = NULL;
1282 TRACE("%s\n", debugstr_w(event_info[eid].name));
1284 if(!doc->nsdoc)
1285 return S_OK;
1287 switch(eid) {
1288 case EVENTID_FOCUSIN:
1289 doc->event_vector[eid] = TRUE;
1290 eid = EVENTID_FOCUS;
1291 break;
1292 case EVENTID_FOCUSOUT:
1293 doc->event_vector[eid] = TRUE;
1294 eid = EVENTID_BLUR;
1295 break;
1296 default:
1297 break;
1300 if(doc->event_vector[eid] || !(event_info[eid].flags & (EVENT_DEFAULTLISTENER|EVENT_BIND_TO_BODY)))
1301 return S_OK;
1303 if(event_info[eid].flags & EVENT_BIND_TO_BODY) {
1304 nsnode = doc->node.nsnode;
1305 nsIDOMNode_AddRef(nsnode);
1308 doc->event_vector[eid] = TRUE;
1309 add_nsevent_listener(doc, nsnode, event_info[eid].name);
1311 if(nsnode)
1312 nsIDOMNode_Release(nsnode);
1313 return S_OK;
1316 void detach_events(HTMLDocumentNode *doc)
1318 if(doc->event_vector) {
1319 int i;
1321 for(i=0; i < EVENTID_LAST; i++) {
1322 if(doc->event_vector[i]) {
1323 detach_nsevent(doc, event_info[i].name);
1324 doc->event_vector[i] = FALSE;
1329 release_nsevents(doc);
1332 static HRESULT get_event_dispex_ref(EventTarget *event_target, eventid_t eid, BOOL alloc, VARIANT **ret)
1334 WCHAR buf[64];
1335 buf[0] = 'o';
1336 buf[1] = 'n';
1337 strcpyW(buf+2, event_info[eid].name);
1338 return dispex_get_dprop_ref(&event_target->dispex, buf, alloc, ret);
1341 static void remove_event_handler(EventTarget *event_target, eventid_t eid)
1343 handler_vector_t *handler_vector;
1344 VARIANT *store;
1345 HRESULT hres;
1347 hres = get_event_dispex_ref(event_target, eid, FALSE, &store);
1348 if(SUCCEEDED(hres))
1349 VariantClear(store);
1351 handler_vector = get_handler_vector(event_target, eid, FALSE);
1352 if(handler_vector && handler_vector->handler_prop) {
1353 IDispatch_Release(handler_vector->handler_prop);
1354 handler_vector->handler_prop = NULL;
1358 static HRESULT set_event_handler_disp(EventTarget *event_target, eventid_t eid, IDispatch *disp)
1360 handler_vector_t *handler_vector;
1362 if(event_info[eid].flags & EVENT_FIXME)
1363 FIXME("unimplemented event %s\n", debugstr_w(event_info[eid].name));
1365 remove_event_handler(event_target, eid);
1366 if(!disp)
1367 return S_OK;
1369 handler_vector = get_handler_vector(event_target, eid, TRUE);
1370 if(!handler_vector)
1371 return E_OUTOFMEMORY;
1373 if(handler_vector->handler_prop)
1374 IDispatch_Release(handler_vector->handler_prop);
1376 handler_vector->handler_prop = disp;
1377 IDispatch_AddRef(disp);
1378 return S_OK;
1381 HRESULT set_event_handler(EventTarget *event_target, eventid_t eid, VARIANT *var)
1383 switch(V_VT(var)) {
1384 case VT_NULL:
1385 remove_event_handler(event_target, eid);
1386 return S_OK;
1388 case VT_DISPATCH:
1389 return set_event_handler_disp(event_target, eid, V_DISPATCH(var));
1391 case VT_BSTR: {
1392 VARIANT *v;
1393 HRESULT hres;
1396 * Setting event handler to string is a rare case and we don't want to
1397 * complicate nor increase memory of handler_vector_t for that. Instead,
1398 * we store the value in DispatchEx, which can already handle custom
1399 * properties.
1401 remove_event_handler(event_target, eid);
1403 hres = get_event_dispex_ref(event_target, eid, TRUE, &v);
1404 if(FAILED(hres))
1405 return hres;
1407 V_BSTR(v) = SysAllocString(V_BSTR(var));
1408 if(!V_BSTR(v))
1409 return E_OUTOFMEMORY;
1410 V_VT(v) = VT_BSTR;
1411 return S_OK;
1414 default:
1415 FIXME("not handler %s\n", debugstr_variant(var));
1416 /* fall through */
1417 case VT_EMPTY:
1418 return E_NOTIMPL;
1421 return S_OK;
1424 HRESULT get_event_handler(EventTarget *event_target, eventid_t eid, VARIANT *var)
1426 handler_vector_t *handler_vector;
1427 VARIANT *v;
1428 HRESULT hres;
1430 hres = get_event_dispex_ref(event_target, eid, FALSE, &v);
1431 if(SUCCEEDED(hres) && V_VT(v) != VT_EMPTY) {
1432 V_VT(var) = VT_EMPTY;
1433 return VariantCopy(var, v);
1436 handler_vector = get_handler_vector(event_target, eid, FALSE);
1437 if(handler_vector && handler_vector->handler_prop) {
1438 V_VT(var) = VT_DISPATCH;
1439 V_DISPATCH(var) = handler_vector->handler_prop;
1440 IDispatch_AddRef(V_DISPATCH(var));
1441 }else {
1442 V_VT(var) = VT_NULL;
1445 return S_OK;
1448 HRESULT attach_event(EventTarget *event_target, BSTR name, IDispatch *disp, VARIANT_BOOL *res)
1450 handler_vector_t *handler_vector;
1451 eventid_t eid;
1452 DWORD i = 0;
1454 eid = attr_to_eid(name);
1455 if(eid == EVENTID_LAST) {
1456 WARN("Unknown event\n");
1457 *res = VARIANT_TRUE;
1458 return S_OK;
1461 if(event_info[eid].flags & EVENT_FIXME)
1462 FIXME("unimplemented event %s\n", debugstr_w(event_info[eid].name));
1464 handler_vector = get_handler_vector(event_target, eid, TRUE);
1465 if(!handler_vector)
1466 return E_OUTOFMEMORY;
1468 while(i < handler_vector->handler_cnt && handler_vector->handlers[i])
1469 i++;
1470 if(i == handler_vector->handler_cnt) {
1471 if(i)
1472 handler_vector->handlers = heap_realloc_zero(handler_vector->handlers,
1473 (i + 1) * sizeof(*handler_vector->handlers));
1474 else
1475 handler_vector->handlers = heap_alloc_zero(sizeof(*handler_vector->handlers));
1476 if(!handler_vector->handlers)
1477 return E_OUTOFMEMORY;
1478 handler_vector->handler_cnt++;
1481 IDispatch_AddRef(disp);
1482 handler_vector->handlers[i] = disp;
1484 *res = VARIANT_TRUE;
1485 return S_OK;
1488 HRESULT detach_event(EventTarget *event_target, BSTR name, IDispatch *disp)
1490 handler_vector_t *handler_vector;
1491 eventid_t eid;
1492 unsigned i;
1494 eid = attr_to_eid(name);
1495 if(eid == EVENTID_LAST) {
1496 WARN("Unknown event\n");
1497 return S_OK;
1500 handler_vector = get_handler_vector(event_target, eid, FALSE);
1501 if(!handler_vector)
1502 return S_OK;
1504 for(i = 0; i < handler_vector->handler_cnt; i++) {
1505 if(handler_vector->handlers[i] == disp) {
1506 IDispatch_Release(handler_vector->handlers[i]);
1507 handler_vector->handlers[i] = NULL;
1511 return S_OK;
1514 void bind_target_event(HTMLDocumentNode *doc, EventTarget *event_target, const WCHAR *event, IDispatch *disp)
1516 eventid_t eid;
1518 TRACE("(%p %p %s %p)\n", doc, event_target, debugstr_w(event), disp);
1520 eid = attr_to_eid(event);
1521 if(eid == EVENTID_LAST) {
1522 WARN("Unsupported event %s\n", debugstr_w(event));
1523 return;
1526 set_event_handler_disp(event_target, eid, disp);
1529 void update_doc_cp_events(HTMLDocumentNode *doc, cp_static_data_t *cp)
1531 int i;
1533 for(i=0; i < EVENTID_LAST; i++) {
1534 if((event_info[i].flags & EVENT_DEFAULTLISTENER) && is_cp_event(cp, event_info[i].dispid))
1535 ensure_doc_nsevent_handler(doc, i);
1539 void check_event_attr(HTMLDocumentNode *doc, nsIDOMHTMLElement *nselem)
1541 nsIDOMMozNamedAttrMap *attr_map;
1542 const PRUnichar *name, *value;
1543 nsAString name_str, value_str;
1544 HTMLDOMNode *node = NULL;
1545 cpp_bool has_attrs;
1546 nsIDOMAttr *attr;
1547 IDispatch *disp;
1548 UINT32 length, i;
1549 eventid_t eid;
1550 nsresult nsres;
1551 HRESULT hres;
1553 nsres = nsIDOMHTMLElement_HasAttributes(nselem, &has_attrs);
1554 if(NS_FAILED(nsres) || !has_attrs)
1555 return;
1557 nsres = nsIDOMHTMLElement_GetAttributes(nselem, &attr_map);
1558 if(NS_FAILED(nsres))
1559 return;
1561 nsres = nsIDOMMozNamedAttrMap_GetLength(attr_map, &length);
1562 assert(nsres == NS_OK);
1564 nsAString_Init(&name_str, NULL);
1565 nsAString_Init(&value_str, NULL);
1567 for(i = 0; i < length; i++) {
1568 nsres = nsIDOMMozNamedAttrMap_Item(attr_map, i, &attr);
1569 if(NS_FAILED(nsres))
1570 continue;
1572 nsres = nsIDOMAttr_GetName(attr, &name_str);
1573 if(NS_FAILED(nsres)) {
1574 nsIDOMAttr_Release(attr);
1575 continue;
1578 nsAString_GetData(&name_str, &name);
1579 eid = attr_to_eid(name);
1580 if(eid == EVENTID_LAST) {
1581 nsIDOMAttr_Release(attr);
1582 continue;
1585 nsres = nsIDOMAttr_GetValue(attr, &value_str);
1586 nsIDOMAttr_Release(attr);
1587 if(NS_FAILED(nsres))
1588 continue;
1590 nsAString_GetData(&value_str, &value);
1591 if(!*value)
1592 continue;
1594 TRACE("%p.%s = %s\n", nselem, debugstr_w(name), debugstr_w(value));
1596 disp = script_parse_event(doc->window, value);
1597 if(!disp)
1598 continue;
1600 if(!node) {
1601 hres = get_node(doc, (nsIDOMNode*)nselem, TRUE, &node);
1602 if(FAILED(hres)) {
1603 IDispatch_Release(disp);
1604 break;
1608 set_event_handler_disp(&node->event_target, eid, disp);
1609 IDispatch_Release(disp);
1612 if(node)
1613 node_release(node);
1614 nsAString_Finish(&name_str);
1615 nsAString_Finish(&value_str);
1616 nsIDOMMozNamedAttrMap_Release(attr_map);
1619 HRESULT doc_init_events(HTMLDocumentNode *doc)
1621 unsigned i;
1622 HRESULT hres;
1624 doc->event_vector = heap_alloc_zero(EVENTID_LAST*sizeof(BOOL));
1625 if(!doc->event_vector)
1626 return E_OUTOFMEMORY;
1628 init_nsevents(doc);
1630 for(i=0; i < EVENTID_LAST; i++) {
1631 if(event_info[i].flags & EVENT_HASDEFAULTHANDLERS) {
1632 hres = ensure_doc_nsevent_handler(doc, i);
1633 if(FAILED(hres))
1634 return hres;
1638 return S_OK;
1641 static int event_id_cmp(const void *key, const struct wine_rb_entry *entry)
1643 return (INT_PTR)key - WINE_RB_ENTRY_VALUE(entry, handler_vector_t, entry)->event_id;
1646 void init_event_target(EventTarget *event_target)
1648 wine_rb_init(&event_target->handler_map, event_id_cmp);
1651 void release_event_target(EventTarget *event_target)
1653 handler_vector_t *iter, *iter2;
1654 unsigned i;
1656 WINE_RB_FOR_EACH_ENTRY_DESTRUCTOR(iter, iter2, &event_target->handler_map, handler_vector_t, entry) {
1657 if(iter->handler_prop)
1658 IDispatch_Release(iter->handler_prop);
1659 for(i = 0; i < iter->handler_cnt; i++)
1660 if(iter->handlers[i])
1661 IDispatch_Release(iter->handlers[i]);
1662 heap_free(iter->handlers);
1663 heap_free(iter);