rsaenh/tests: Fix memory leaks.
[wine] / dlls / mshtml / htmlevent.c
1 /*
2  * Copyright 2008-2009 Jacek Caban for CodeWeavers
3  *
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.
8  *
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.
13  *
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
17  */
18
19 #include <stdarg.h>
20
21 #define COBJMACROS
22
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winuser.h"
26 #include "ole2.h"
27 #include "mshtmdid.h"
28
29 #include "mshtml_private.h"
30 #include "htmlevent.h"
31
32 #include "wine/debug.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(mshtml);
35
36 typedef struct {
37     IDispatch *handler_prop;
38     DWORD handler_cnt;
39     IDispatch *handlers[0];
40 } handler_vector_t;
41
42 struct event_target_t {
43     handler_vector_t *event_table[EVENTID_LAST];
44 };
45
46 static const WCHAR beforeunloadW[] = {'b','e','f','o','r','e','u','n','l','o','a','d',0};
47 static const WCHAR onbeforeunloadW[] = {'o','n','b','e','f','o','r','e','u','n','l','o','a','d',0};
48
49 static const WCHAR blurW[] = {'b','l','u','r',0};
50 static const WCHAR onblurW[] = {'o','n','b','l','u','r',0};
51
52 static const WCHAR changeW[] = {'c','h','a','n','g','e',0};
53 static const WCHAR onchangeW[] = {'o','n','c','h','a','n','g','e',0};
54
55 static const WCHAR clickW[] = {'c','l','i','c','k',0};
56 static const WCHAR onclickW[] = {'o','n','c','l','i','c','k',0};
57
58 static const WCHAR dblclickW[] = {'d','b','l','c','l','i','c','k',0};
59 static const WCHAR ondblclickW[] = {'o','n','d','b','l','c','l','i','c','k',0};
60
61 static const WCHAR dragW[] = {'d','r','a','g',0};
62 static const WCHAR ondragW[] = {'o','n','d','r','a','g',0};
63
64 static const WCHAR dragstartW[] = {'d','r','a','g','s','t','a','r','t',0};
65 static const WCHAR ondragstartW[] = {'o','n','d','r','a','g','s','t','a','r','t',0};
66
67 static const WCHAR focusW[] = {'f','o','c','u','s',0};
68 static const WCHAR onfocusW[] = {'o','n','f','o','c','u','s',0};
69
70 static const WCHAR keydownW[] = {'k','e','y','d','o','w','n',0};
71 static const WCHAR onkeydownW[] = {'o','n','k','e','y','d','o','w','n',0};
72
73 static const WCHAR keyupW[] = {'k','e','y','u','p',0};
74 static const WCHAR onkeyupW[] = {'o','n','k','e','y','u','p',0};
75
76 static const WCHAR loadW[] = {'l','o','a','d',0};
77 static const WCHAR onloadW[] = {'o','n','l','o','a','d',0};
78
79 static const WCHAR mousedownW[] = {'m','o','u','s','e','d','o','w','n',0};
80 static const WCHAR onmousedownW[] = {'o','n','m','o','u','s','e','d','o','w','n',0};
81
82 static const WCHAR mouseoutW[] = {'m','o','u','s','e','o','u','t',0};
83 static const WCHAR onmouseoutW[] = {'o','n','m','o','u','s','e','o','u','t',0};
84
85 static const WCHAR mouseoverW[] = {'m','o','u','s','e','o','v','e','r',0};
86 static const WCHAR onmouseoverW[] = {'o','n','m','o','u','s','e','o','v','e','r',0};
87
88 static const WCHAR mouseupW[] = {'m','o','u','s','e','u','p',0};
89 static const WCHAR onmouseupW[] = {'o','n','m','o','u','s','e','u','p',0};
90
91 static const WCHAR pasteW[] = {'p','a','s','t','e',0};
92 static const WCHAR onpasteW[] = {'o','n','p','a','s','t','e',0};
93
94 static const WCHAR readystatechangeW[] = {'r','e','a','d','y','s','t','a','t','e','c','h','a','n','g','e',0};
95 static const WCHAR onreadystatechangeW[] = {'o','n','r','e','a','d','y','s','t','a','t','e','c','h','a','n','g','e',0};
96
97 static const WCHAR selectstartW[] = {'s','e','l','e','c','t','s','t','a','r','t',0};
98 static const WCHAR onselectstartW[] = {'o','n','s','e','l','e','c','t','s','t','a','r','t',0};
99
100 static const WCHAR HTMLEventsW[] = {'H','T','M','L','E','v','e','n','t','s',0};
101 static const WCHAR KeyboardEventW[] = {'K','e','y','b','o','a','r','d','E','v','e','n','t',0};
102 static const WCHAR MouseEventW[] = {'M','o','u','s','e','E','v','e','n','t',0};
103
104 enum {
105     EVENTT_NONE,
106     EVENTT_HTML,
107     EVENTT_KEY,
108     EVENTT_MOUSE
109 };
110
111 static const WCHAR *event_types[] = {
112     NULL,
113     HTMLEventsW,
114     KeyboardEventW,
115     MouseEventW
116 };
117
118 typedef struct {
119     LPCWSTR name;
120     LPCWSTR attr_name;
121     DWORD type;
122     DISPID dispid;
123     DWORD flags;
124 } event_info_t;
125
126 #define EVENT_DEFAULTLISTENER    0x0001
127 #define EVENT_BUBBLE             0x0002
128 #define EVENT_FORWARDBODY        0x0004
129
130 static const event_info_t event_info[] = {
131     {beforeunloadW,      onbeforeunloadW,      EVENTT_NONE,   DISPID_EVMETH_ONBEFOREUNLOAD,
132         EVENT_DEFAULTLISTENER|EVENT_FORWARDBODY},
133     {blurW,              onblurW,              EVENTT_HTML,   DISPID_EVMETH_ONBLUR,
134         EVENT_DEFAULTLISTENER},
135     {changeW,            onchangeW,            EVENTT_HTML,   DISPID_EVMETH_ONCHANGE,
136         EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
137     {clickW,             onclickW,             EVENTT_MOUSE,  DISPID_EVMETH_ONCLICK,
138         EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
139     {dblclickW,          ondblclickW,          EVENTT_MOUSE,  DISPID_EVMETH_ONDBLCLICK,
140         EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
141     {dragW,              ondragW,              EVENTT_MOUSE,  DISPID_EVMETH_ONDRAG,
142         0},
143     {dragstartW,         ondragstartW,         EVENTT_MOUSE,  DISPID_EVMETH_ONDRAGSTART,
144         0},
145     {focusW,             onfocusW,             EVENTT_HTML,   DISPID_EVMETH_ONFOCUS,
146         EVENT_DEFAULTLISTENER},
147     {keydownW,           onkeydownW,           EVENTT_KEY,    DISPID_EVMETH_ONKEYDOWN,
148         EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
149     {keyupW,             onkeyupW,             EVENTT_KEY,    DISPID_EVMETH_ONKEYUP,
150         EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
151     {loadW,              onloadW,              EVENTT_HTML,   DISPID_EVMETH_ONLOAD,
152         0},
153     {mousedownW,         onmousedownW,         EVENTT_MOUSE,  DISPID_EVMETH_ONMOUSEDOWN,
154         EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
155     {mouseoutW,          onmouseoutW,          EVENTT_MOUSE,  DISPID_EVMETH_ONMOUSEOUT,
156         EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
157     {mouseoverW,         onmouseoverW,         EVENTT_MOUSE,  DISPID_EVMETH_ONMOUSEOVER,
158         EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
159     {mouseupW,           onmouseupW,           EVENTT_MOUSE,  DISPID_EVMETH_ONMOUSEUP,
160         EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
161     {pasteW,             onpasteW,             EVENTT_NONE,   DISPID_EVMETH_ONPASTE,
162         0},
163     {readystatechangeW,  onreadystatechangeW,  EVENTT_NONE,   DISPID_EVMETH_ONREADYSTATECHANGE,
164         0},
165     {selectstartW,       onselectstartW,       EVENTT_MOUSE,  DISPID_EVMETH_ONSELECTSTART,
166         0}
167 };
168
169 eventid_t str_to_eid(LPCWSTR str)
170 {
171     int i;
172
173     for(i=0; i < sizeof(event_info)/sizeof(event_info[0]); i++) {
174         if(!strcmpW(event_info[i].name, str))
175             return i;
176     }
177
178     ERR("unknown type %s\n", debugstr_w(str));
179     return EVENTID_LAST;
180 }
181
182 static eventid_t attr_to_eid(LPCWSTR str)
183 {
184     int i;
185
186     for(i=0; i < sizeof(event_info)/sizeof(event_info[0]); i++) {
187         if(!strcmpW(event_info[i].attr_name, str))
188             return i;
189     }
190
191     return EVENTID_LAST;
192 }
193
194 typedef struct {
195     DispatchEx dispex;
196     const IHTMLEventObjVtbl  *lpIHTMLEventObjVtbl;
197
198     LONG ref;
199
200     HTMLDOMNode *target;
201     const event_info_t *type;
202     nsIDOMEvent *nsevent;
203 } HTMLEventObj;
204
205 #define HTMLEVENTOBJ(x) ((IHTMLEventObj*) &(x)->lpIHTMLEventObjVtbl)
206
207 #define HTMLEVENTOBJ_THIS(iface) DEFINE_THIS(HTMLEventObj, IHTMLEventObj, iface)
208
209 static HRESULT WINAPI HTMLEventObj_QueryInterface(IHTMLEventObj *iface, REFIID riid, void **ppv)
210 {
211     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
212
213     *ppv = NULL;
214
215     if(IsEqualGUID(&IID_IUnknown, riid)) {
216         TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv);
217         *ppv = HTMLEVENTOBJ(This);
218     }else if(IsEqualGUID(&IID_IHTMLEventObj, riid)) {
219         TRACE("(%p)->(IID_IHTMLEventObj %p)\n", This, ppv);
220         *ppv = HTMLEVENTOBJ(This);
221     }else if(dispex_query_interface(&This->dispex, riid, ppv)) {
222         return *ppv ? S_OK : E_NOINTERFACE;
223     }
224
225     if(*ppv) {
226         IUnknown_AddRef((IUnknown*)*ppv);
227         return S_OK;
228     }
229
230     WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv);
231     return E_NOINTERFACE;
232 }
233
234 static ULONG WINAPI HTMLEventObj_AddRef(IHTMLEventObj *iface)
235 {
236     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
237     LONG ref = InterlockedIncrement(&This->ref);
238
239     TRACE("(%p) ref=%d\n", This, ref);
240
241     return ref;
242 }
243
244 static ULONG WINAPI HTMLEventObj_Release(IHTMLEventObj *iface)
245 {
246     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
247     LONG ref = InterlockedDecrement(&This->ref);
248
249     TRACE("(%p) ref=%d\n", This, ref);
250
251     if(!ref) {
252         if(This->nsevent)
253             nsIDOMEvent_Release(This->nsevent);
254         release_dispex(&This->dispex);
255         heap_free(This);
256     }
257
258     return ref;
259 }
260
261 static HRESULT WINAPI HTMLEventObj_GetTypeInfoCount(IHTMLEventObj *iface, UINT *pctinfo)
262 {
263     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
264     FIXME("(%p)->(%p)\n", This, pctinfo);
265     return E_NOTIMPL;
266 }
267
268 static HRESULT WINAPI HTMLEventObj_GetTypeInfo(IHTMLEventObj *iface, UINT iTInfo,
269                                               LCID lcid, ITypeInfo **ppTInfo)
270 {
271     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
272     FIXME("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
273     return E_NOTIMPL;
274 }
275
276 static HRESULT WINAPI HTMLEventObj_GetIDsOfNames(IHTMLEventObj *iface, REFIID riid,
277                                                 LPOLESTR *rgszNames, UINT cNames,
278                                                 LCID lcid, DISPID *rgDispId)
279 {
280     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
281     FIXME("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
282           lcid, rgDispId);
283     return E_NOTIMPL;
284 }
285
286 static HRESULT WINAPI HTMLEventObj_Invoke(IHTMLEventObj *iface, DISPID dispIdMember,
287                             REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
288                             VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
289 {
290     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
291     FIXME("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
292           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
293     return E_NOTIMPL;
294 }
295
296 static HRESULT WINAPI HTMLEventObj_get_srcElement(IHTMLEventObj *iface, IHTMLElement **p)
297 {
298     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
299
300     TRACE("(%p)->(%p)\n", This, p);
301
302     return IHTMLDOMNode_QueryInterface(HTMLDOMNODE(This->target), &IID_IHTMLElement, (void**)p);
303 }
304
305 static HRESULT WINAPI HTMLEventObj_get_altKey(IHTMLEventObj *iface, VARIANT_BOOL *p)
306 {
307     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
308     PRBool ret = FALSE;
309
310     TRACE("(%p)->(%p)\n", This, p);
311
312     if(This->nsevent) {
313         nsIDOMKeyEvent *key_event;
314         nsresult nsres;
315
316         nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
317         if(NS_SUCCEEDED(nsres)) {
318             nsIDOMKeyEvent_GetAltKey(key_event, &ret);
319             nsIDOMKeyEvent_Release(key_event);
320         }else {
321             nsIDOMMouseEvent *mouse_event;
322
323             nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
324             if(NS_SUCCEEDED(nsres)) {
325                 nsIDOMMouseEvent_GetAltKey(mouse_event, &ret);
326                 nsIDOMMouseEvent_Release(mouse_event);
327             }
328         }
329     }
330
331     *p = ret ? VARIANT_TRUE : VARIANT_FALSE;
332     return S_OK;
333 }
334
335 static HRESULT WINAPI HTMLEventObj_get_ctrlKey(IHTMLEventObj *iface, VARIANT_BOOL *p)
336 {
337     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
338     PRBool ret = FALSE;
339
340     TRACE("(%p)->(%p)\n", This, p);
341
342     if(This->nsevent) {
343         nsIDOMKeyEvent *key_event;
344         nsresult nsres;
345
346         nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
347         if(NS_SUCCEEDED(nsres)) {
348             nsIDOMKeyEvent_GetCtrlKey(key_event, &ret);
349             nsIDOMKeyEvent_Release(key_event);
350         }else {
351             nsIDOMMouseEvent *mouse_event;
352
353             nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
354             if(NS_SUCCEEDED(nsres)) {
355                 nsIDOMMouseEvent_GetCtrlKey(mouse_event, &ret);
356                 nsIDOMMouseEvent_Release(mouse_event);
357             }
358         }
359     }
360
361     *p = ret ? VARIANT_TRUE : VARIANT_FALSE;
362     return S_OK;
363 }
364
365 static HRESULT WINAPI HTMLEventObj_get_shiftKey(IHTMLEventObj *iface, VARIANT_BOOL *p)
366 {
367     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
368     PRBool ret = FALSE;
369
370     TRACE("(%p)->(%p)\n", This, p);
371
372     if(This->nsevent) {
373         nsIDOMKeyEvent *key_event;
374         nsresult nsres;
375
376         nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
377         if(NS_SUCCEEDED(nsres)) {
378             nsIDOMKeyEvent_GetShiftKey(key_event, &ret);
379             nsIDOMKeyEvent_Release(key_event);
380         }else {
381             nsIDOMMouseEvent *mouse_event;
382
383             nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
384             if(NS_SUCCEEDED(nsres)) {
385                 nsIDOMMouseEvent_GetShiftKey(mouse_event, &ret);
386                 nsIDOMMouseEvent_Release(mouse_event);
387             }
388         }
389     }
390
391     *p = ret ? VARIANT_TRUE : VARIANT_FALSE;
392     return S_OK;
393 }
394
395 static HRESULT WINAPI HTMLEventObj_put_returnValue(IHTMLEventObj *iface, VARIANT v)
396 {
397     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
398     FIXME("(%p)->()\n", This);
399     return E_NOTIMPL;
400 }
401
402 static HRESULT WINAPI HTMLEventObj_get_returnValue(IHTMLEventObj *iface, VARIANT *p)
403 {
404     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
405
406     FIXME("(%p)->(%p)\n", This, p);
407
408     V_VT(p) = VT_EMPTY;
409     return S_OK;
410 }
411
412 static HRESULT WINAPI HTMLEventObj_put_cancelBubble(IHTMLEventObj *iface, VARIANT_BOOL v)
413 {
414     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
415     FIXME("(%p)->(%x)\n", This, v);
416     return E_NOTIMPL;
417 }
418
419 static HRESULT WINAPI HTMLEventObj_get_cancelBubble(IHTMLEventObj *iface, VARIANT_BOOL *p)
420 {
421     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
422
423     FIXME("(%p)->(%p)\n", This, p);
424
425     *p = VARIANT_FALSE;
426     return S_OK;
427 }
428
429 static HRESULT WINAPI HTMLEventObj_get_fromElement(IHTMLEventObj *iface, IHTMLElement **p)
430 {
431     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
432
433     FIXME("(%p)->(%p)\n", This, p);
434
435     *p = NULL;
436     return S_OK;
437 }
438
439 static HRESULT WINAPI HTMLEventObj_get_toElement(IHTMLEventObj *iface, IHTMLElement **p)
440 {
441     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
442
443     FIXME("(%p)->(%p)\n", This, p);
444
445     *p = NULL;
446     return S_OK;
447 }
448
449 static HRESULT WINAPI HTMLEventObj_put_keyCode(IHTMLEventObj *iface, LONG v)
450 {
451     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
452     FIXME("(%p)->(%d)\n", This, v);
453     return E_NOTIMPL;
454 }
455
456 static HRESULT WINAPI HTMLEventObj_get_keyCode(IHTMLEventObj *iface, LONG *p)
457 {
458     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
459     PRUint32 key_code = 0;
460
461     TRACE("(%p)->(%p)\n", This, p);
462
463     if(This->nsevent) {
464         nsIDOMKeyEvent *key_event;
465         nsresult nsres;
466
467         nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
468         if(NS_SUCCEEDED(nsres)) {
469             nsIDOMKeyEvent_GetKeyCode(key_event, &key_code);
470             nsIDOMKeyEvent_Release(key_event);
471         }
472     }
473
474     *p = key_code;
475     return S_OK;
476 }
477
478 static HRESULT WINAPI HTMLEventObj_get_button(IHTMLEventObj *iface, LONG *p)
479 {
480     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
481     PRUint16 button = 0;
482
483     TRACE("(%p)->(%p)\n", This, p);
484
485     if(This->nsevent) {
486         nsIDOMMouseEvent *mouse_event;
487         nsresult nsres;
488
489         nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
490         if(NS_SUCCEEDED(nsres)) {
491             nsIDOMMouseEvent_GetButton(mouse_event, &button);
492             nsIDOMMouseEvent_Release(mouse_event);
493         }
494     }
495
496     *p = button;
497     return S_OK;
498 }
499
500 static HRESULT WINAPI HTMLEventObj_get_type(IHTMLEventObj *iface, BSTR *p)
501 {
502     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
503
504     TRACE("(%p)->(%p)\n", This, p);
505
506     *p = SysAllocString(This->type->name);
507     return *p ? S_OK : E_OUTOFMEMORY;
508 }
509
510 static HRESULT WINAPI HTMLEventObj_get_qualifier(IHTMLEventObj *iface, BSTR *p)
511 {
512     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
513
514     FIXME("(%p)->(%p)\n", This, p);
515
516     *p = NULL;
517     return S_OK;
518 }
519
520 static HRESULT WINAPI HTMLEventObj_get_reason(IHTMLEventObj *iface, LONG *p)
521 {
522     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
523
524     FIXME("(%p)->(%p)\n", This, p);
525
526     *p = 0;
527     return S_OK;
528 }
529
530 static HRESULT WINAPI HTMLEventObj_get_x(IHTMLEventObj *iface, LONG *p)
531 {
532     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
533
534     FIXME("(%p)->(%p)\n", This, p);
535
536     *p = -1;
537     return S_OK;
538 }
539
540 static HRESULT WINAPI HTMLEventObj_get_y(IHTMLEventObj *iface, LONG *p)
541 {
542     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
543
544     FIXME("(%p)->(%p)\n", This, p);
545
546     *p = -1;
547     return S_OK;
548 }
549
550 static HRESULT WINAPI HTMLEventObj_get_clientX(IHTMLEventObj *iface, LONG *p)
551 {
552     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
553     PRInt32 x = 0;
554
555     TRACE("(%p)->(%p)\n", This, p);
556
557     if(This->nsevent) {
558         nsIDOMMouseEvent *mouse_event;
559         nsresult nsres;
560
561         nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
562         if(NS_SUCCEEDED(nsres)) {
563             nsIDOMMouseEvent_GetClientX(mouse_event, &x);
564             nsIDOMMouseEvent_Release(mouse_event);
565         }
566     }
567
568     *p = x;
569     return S_OK;
570 }
571
572 static HRESULT WINAPI HTMLEventObj_get_clientY(IHTMLEventObj *iface, LONG *p)
573 {
574     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
575     PRInt32 y = 0;
576
577     TRACE("(%p)->(%p)\n", This, p);
578
579     if(This->nsevent) {
580         nsIDOMMouseEvent *mouse_event;
581         nsresult nsres;
582
583         nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
584         if(NS_SUCCEEDED(nsres)) {
585             nsIDOMMouseEvent_GetClientY(mouse_event, &y);
586             nsIDOMMouseEvent_Release(mouse_event);
587         }
588     }
589
590     *p = y;
591     return S_OK;
592 }
593
594 static HRESULT WINAPI HTMLEventObj_get_offsetX(IHTMLEventObj *iface, LONG *p)
595 {
596     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
597
598     FIXME("(%p)->(%p)\n", This, p);
599
600     *p = 0;
601     return S_OK;
602 }
603
604 static HRESULT WINAPI HTMLEventObj_get_offsetY(IHTMLEventObj *iface, LONG *p)
605 {
606     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
607
608     FIXME("(%p)->(%p)\n", This, p);
609
610     *p = 0;
611     return S_OK;
612 }
613
614 static HRESULT WINAPI HTMLEventObj_get_screenX(IHTMLEventObj *iface, LONG *p)
615 {
616     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
617     PRInt32 x = 0;
618
619     TRACE("(%p)->(%p)\n", This, p);
620
621     if(This->nsevent) {
622         nsIDOMMouseEvent *mouse_event;
623         nsresult nsres;
624
625         nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
626         if(NS_SUCCEEDED(nsres)) {
627             nsIDOMMouseEvent_GetScreenX(mouse_event, &x);
628             nsIDOMMouseEvent_Release(mouse_event);
629         }
630     }
631
632     *p = x;
633     return S_OK;
634 }
635
636 static HRESULT WINAPI HTMLEventObj_get_screenY(IHTMLEventObj *iface, LONG *p)
637 {
638     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
639     PRInt32 y = 0;
640
641     TRACE("(%p)->(%p)\n", This, p);
642
643     if(This->nsevent) {
644         nsIDOMMouseEvent *mouse_event;
645         nsresult nsres;
646
647         nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
648         if(NS_SUCCEEDED(nsres)) {
649             nsIDOMMouseEvent_GetScreenY(mouse_event, &y);
650             nsIDOMMouseEvent_Release(mouse_event);
651         }
652     }
653
654     *p = y;
655     return S_OK;
656 }
657
658 static HRESULT WINAPI HTMLEventObj_get_srcFilter(IHTMLEventObj *iface, IDispatch **p)
659 {
660     HTMLEventObj *This = HTMLEVENTOBJ_THIS(iface);
661
662     FIXME("(%p)->(%p)\n", This, p);
663
664     *p = NULL;
665     return S_OK;
666 }
667
668 #undef HTMLEVENTOBJ_THIS
669
670 static const IHTMLEventObjVtbl HTMLEventObjVtbl = {
671     HTMLEventObj_QueryInterface,
672     HTMLEventObj_AddRef,
673     HTMLEventObj_Release,
674     HTMLEventObj_GetTypeInfoCount,
675     HTMLEventObj_GetTypeInfo,
676     HTMLEventObj_GetIDsOfNames,
677     HTMLEventObj_Invoke,
678     HTMLEventObj_get_srcElement,
679     HTMLEventObj_get_altKey,
680     HTMLEventObj_get_ctrlKey,
681     HTMLEventObj_get_shiftKey,
682     HTMLEventObj_put_returnValue,
683     HTMLEventObj_get_returnValue,
684     HTMLEventObj_put_cancelBubble,
685     HTMLEventObj_get_cancelBubble,
686     HTMLEventObj_get_fromElement,
687     HTMLEventObj_get_toElement,
688     HTMLEventObj_put_keyCode,
689     HTMLEventObj_get_keyCode,
690     HTMLEventObj_get_button,
691     HTMLEventObj_get_type,
692     HTMLEventObj_get_qualifier,
693     HTMLEventObj_get_reason,
694     HTMLEventObj_get_x,
695     HTMLEventObj_get_y,
696     HTMLEventObj_get_clientX,
697     HTMLEventObj_get_clientY,
698     HTMLEventObj_get_offsetX,
699     HTMLEventObj_get_offsetY,
700     HTMLEventObj_get_screenX,
701     HTMLEventObj_get_screenY,
702     HTMLEventObj_get_srcFilter
703 };
704
705 static const tid_t HTMLEventObj_iface_tids[] = {
706     IHTMLEventObj_tid,
707     0
708 };
709
710 static dispex_static_data_t HTMLEventObj_dispex = {
711     NULL,
712     DispCEventObj_tid,
713     NULL,
714     HTMLEventObj_iface_tids
715 };
716
717 static IHTMLEventObj *create_event(HTMLDOMNode *target, eventid_t eid, nsIDOMEvent *nsevent)
718 {
719     HTMLEventObj *ret;
720
721     ret = heap_alloc(sizeof(*ret));
722     if(!ret)
723         return NULL;
724
725     ret->lpIHTMLEventObjVtbl = &HTMLEventObjVtbl;
726     ret->ref = 1;
727     ret->type = event_info+eid;
728
729     ret->nsevent = nsevent;
730     if(nsevent) {
731         nsIDOMEvent_AddRef(nsevent);
732     }else if(event_types[event_info[eid].type]) {
733         nsIDOMDocumentEvent *doc_event;
734         nsresult nsres;
735
736         nsres = nsIDOMHTMLDocument_QueryInterface(target->doc->nsdoc, &IID_nsIDOMDocumentEvent,
737                  (void**)&doc_event);
738         if(NS_SUCCEEDED(nsres)) {
739             nsAString type_str;
740
741             nsAString_Init(&type_str, event_types[event_info[eid].type]);
742             nsres = nsIDOMDocumentEvent_CreateEvent(doc_event, &type_str, &ret->nsevent);
743             nsAString_Finish(&type_str);
744             nsIDOMDocumentEvent_Release(doc_event);
745         }
746         if(NS_FAILED(nsres)) {
747             ERR("Could not create event: %08x\n", nsres);
748             IHTMLEventObj_Release(HTMLEVENTOBJ(ret));
749             return NULL;
750         }
751     }
752
753     ret->target = target;
754     IHTMLDOMNode_AddRef(HTMLDOMNODE(target));
755
756     init_dispex(&ret->dispex, (IUnknown*)HTMLEVENTOBJ(ret), &HTMLEventObj_dispex);
757
758     return HTMLEVENTOBJ(ret);
759 }
760
761 static HRESULT call_cp_func(IDispatch *disp, DISPID dispid)
762 {
763     DISPPARAMS dp = {NULL,NULL,0,0};
764     ULONG argerr;
765     EXCEPINFO ei;
766     VARIANT vres;
767     HRESULT hres;
768
769     V_VT(&vres) = VT_EMPTY;
770     memset(&ei, 0, sizeof(ei));
771     hres = IDispatch_Invoke(disp, dispid, &IID_NULL, 0, DISPATCH_METHOD, &dp, &vres, &ei, &argerr);
772     if(SUCCEEDED(hres) && V_VT(&vres) != VT_EMPTY) {
773         FIXME("handle result %s\n", debugstr_variant(&vres));
774         VariantClear(&vres);
775     }
776
777     return hres;
778 }
779
780 static BOOL is_cp_event(ConnectionPoint *cp, DISPID dispid)
781 {
782     cp_static_data_t *data;
783     int min, max, i;
784     HRESULT hres;
785
786     data = cp->data;
787     if(!data)
788         return FALSE;
789
790     if(!data->ids) {
791         hres = get_dispids(data->tid, &data->id_cnt, &data->ids);
792         if(FAILED(hres))
793             return FALSE;
794     }
795
796     min = 0;
797     max = data->id_cnt-1;
798     while(min <= max) {
799         i = (min+max)/2;
800         if(data->ids[i] == dispid)
801             return TRUE;
802
803         if(data->ids[i] < dispid)
804             min = i+1;
805         else
806             max = i-1;
807     }
808
809     return FALSE;
810 }
811
812 static void call_event_handlers(HTMLDocumentNode *doc, IHTMLEventObj *event_obj, event_target_t *event_target,
813         ConnectionPointContainer *cp_container, eventid_t eid, IDispatch *this_obj)
814 {
815     handler_vector_t *handler_vector = NULL;
816     DWORD i;
817     HRESULT hres;
818
819     if(event_target)
820         handler_vector = event_target->event_table[eid];
821
822     if(handler_vector && handler_vector->handler_prop) {
823         DISPID named_arg = DISPID_THIS;
824         VARIANTARG arg;
825         DISPPARAMS dp = {&arg, &named_arg, 1, 1};
826
827         V_VT(&arg) = VT_DISPATCH;
828         V_DISPATCH(&arg) = this_obj;
829
830         TRACE("%s >>>\n", debugstr_w(event_info[eid].name));
831         hres = call_disp_func(handler_vector->handler_prop, &dp);
832         if(hres == S_OK)
833             TRACE("%s <<<\n", debugstr_w(event_info[eid].name));
834         else
835             WARN("%s <<< %08x\n", debugstr_w(event_info[eid].name), hres);
836     }
837
838     if(handler_vector && handler_vector->handler_cnt) {
839         VARIANTARG arg;
840         DISPPARAMS dp = {&arg, NULL, 1, 0};
841
842         V_VT(&arg) = VT_DISPATCH;
843         V_DISPATCH(&arg) = (IDispatch*)event_obj;
844
845         for(i=0; i < handler_vector->handler_cnt; i++) {
846             if(handler_vector->handlers[i]) {
847                 TRACE("%s [%d] >>>\n", debugstr_w(event_info[eid].name), i);
848                 hres = call_disp_func(handler_vector->handlers[i], &dp);
849                 if(hres == S_OK)
850                     TRACE("%s [%d] <<<\n", debugstr_w(event_info[eid].name), i);
851                 else
852                     WARN("%s [%d] <<< %08x\n", debugstr_w(event_info[eid].name), i, hres);
853             }
854         }
855     }
856
857     if(cp_container) {
858         ConnectionPoint *cp;
859
860         if(cp_container->forward_container)
861             cp_container = cp_container->forward_container;
862
863         for(cp = cp_container->cp_list; cp; cp = cp->next) {
864             if(cp->sinks_size && is_cp_event(cp, event_info[eid].dispid)) {
865                 for(i=0; i < cp->sinks_size; i++) {
866                     TRACE("cp %s [%d] >>>\n", debugstr_w(event_info[eid].name), i);
867                     hres = call_cp_func(cp->sinks[i].disp, event_info[eid].dispid);
868                     if(hres == S_OK)
869                         TRACE("cp %s [%d] <<<\n", debugstr_w(event_info[eid].name), i);
870                     else
871                         WARN("cp %s [%d] <<< %08x\n", debugstr_w(event_info[eid].name), i, hres);
872                 }
873             }
874         }
875     }
876 }
877
878 void fire_event(HTMLDocumentNode *doc, eventid_t eid, nsIDOMNode *target, nsIDOMEvent *nsevent)
879 {
880     IHTMLEventObj *prev_event, *event_obj = NULL;
881     nsIDOMNode *parent, *nsnode;
882     HTMLDOMNode *node;
883     PRUint16 node_type;
884
885     TRACE("(%p) %s\n", doc, debugstr_w(event_info[eid].name));
886
887     prev_event = doc->basedoc.window->event;
888     event_obj = doc->basedoc.window->event = create_event(get_node(doc, target, TRUE), eid, nsevent);
889
890     nsIDOMNode_GetNodeType(target, &node_type);
891     nsnode = target;
892     nsIDOMNode_AddRef(nsnode);
893
894     switch(node_type) {
895     case ELEMENT_NODE:
896         do {
897             node = get_node(doc, nsnode, FALSE);
898             if(node)
899                 call_event_handlers(doc, event_obj, *get_node_event_target(node), node->cp_container, eid,
900                         (IDispatch*)HTMLDOMNODE(node));
901
902             if(!(event_info[eid].flags & EVENT_BUBBLE))
903                 break;
904
905             nsIDOMNode_GetParentNode(nsnode, &parent);
906             nsIDOMNode_Release(nsnode);
907             nsnode = parent;
908             if(!nsnode)
909                 break;
910
911             nsIDOMNode_GetNodeType(nsnode, &node_type);
912         }while(node_type == ELEMENT_NODE);
913
914         if(!(event_info[eid].flags & EVENT_BUBBLE))
915             break;
916
917     case DOCUMENT_NODE:
918         if(event_info[eid].flags & EVENT_FORWARDBODY) {
919             nsIDOMHTMLElement *nsbody;
920             nsresult nsres;
921
922             nsres = nsIDOMHTMLDocument_GetBody(doc->nsdoc, &nsbody);
923             if(NS_SUCCEEDED(nsres) && nsbody) {
924                 node = get_node(doc, (nsIDOMNode*)nsbody, FALSE);
925                 if(node)
926                     call_event_handlers(doc, event_obj, *get_node_event_target(node), node->cp_container,
927                             eid, (IDispatch*)HTMLDOMNODE(node));
928                 nsIDOMHTMLElement_Release(nsbody);
929             }else {
930                 ERR("Could not get body: %08x\n", nsres);
931             }
932         }
933
934         call_event_handlers(doc, event_obj, doc->basedoc.doc_node->node.event_target, &doc->basedoc.cp_container, eid,
935                 (IDispatch*)HTMLDOC(&doc->basedoc));
936         break;
937
938     default:
939         FIXME("unimplemented node type %d\n", node_type);
940     }
941
942     if(nsnode)
943         nsIDOMNode_Release(nsnode);
944
945     IHTMLEventObj_Release(event_obj);
946     doc->basedoc.window->event = prev_event;
947 }
948
949 HRESULT dispatch_event(HTMLDOMNode *node, const WCHAR *event_name, VARIANT *event_obj, VARIANT_BOOL *cancelled)
950 {
951     eventid_t eid;
952
953     eid = attr_to_eid(event_name);
954     if(eid == EVENTID_LAST) {
955         WARN("unknown event %s\n", debugstr_w(event_name));
956         return E_INVALIDARG;
957     }
958
959     if(event_obj && V_VT(event_obj) != VT_EMPTY && V_VT(event_obj) != VT_ERROR)
960         FIXME("event_obj not implemented\n");
961
962     if(!(event_info[eid].flags & EVENT_DEFAULTLISTENER)) {
963         FIXME("not EVENT_DEFAULTEVENTHANDLER\n");
964         return E_NOTIMPL;
965     }
966
967     fire_event(node->doc, eid, node->nsnode, NULL);
968
969     *cancelled = VARIANT_TRUE; /* FIXME */
970     return S_OK;
971 }
972
973 HRESULT call_event(HTMLDOMNode *node, eventid_t eid)
974 {
975     HRESULT hres;
976
977     if(node->vtbl->call_event) {
978         BOOL handled = FALSE;
979
980         hres = node->vtbl->call_event(node, eid, &handled);
981         if(handled)
982             return hres;
983     }
984
985     fire_event(node->doc, eid, node->nsnode, NULL);
986     return S_OK;
987 }
988
989 static inline event_target_t *get_event_target(event_target_t **event_target_ptr)
990 {
991     if(!*event_target_ptr)
992         *event_target_ptr = heap_alloc_zero(sizeof(event_target_t));
993     return *event_target_ptr;
994 }
995
996 static BOOL alloc_handler_vector(event_target_t *event_target, eventid_t eid, int cnt)
997 {
998     handler_vector_t *new_vector, *handler_vector = event_target->event_table[eid];
999
1000     if(handler_vector) {
1001         if(cnt <= handler_vector->handler_cnt)
1002             return TRUE;
1003
1004         new_vector = heap_realloc_zero(handler_vector, sizeof(handler_vector_t) + sizeof(IDispatch*)*cnt);
1005     }else {
1006         new_vector = heap_alloc_zero(sizeof(handler_vector_t) + sizeof(IDispatch*)*cnt);
1007     }
1008
1009     if(!new_vector)
1010         return FALSE;
1011
1012     new_vector->handler_cnt = cnt;
1013     event_target->event_table[eid] = new_vector;
1014     return TRUE;
1015 }
1016
1017 static HRESULT set_event_handler_disp(event_target_t **event_target_ptr, HTMLDocumentNode *doc,
1018         eventid_t eid, IDispatch *disp)
1019 {
1020     event_target_t *event_target;
1021
1022     event_target = get_event_target(event_target_ptr);
1023     if(!event_target)
1024         return E_OUTOFMEMORY;
1025
1026     if(!alloc_handler_vector(event_target, eid, 0))
1027         return E_OUTOFMEMORY;
1028
1029     if(event_target->event_table[eid]->handler_prop)
1030         IDispatch_Release(event_target->event_table[eid]->handler_prop);
1031
1032     event_target->event_table[eid]->handler_prop = disp;
1033     if(!disp)
1034         return S_OK;
1035     IDispatch_AddRef(disp);
1036
1037     if(doc->nsdoc && (event_info[eid].flags & EVENT_DEFAULTLISTENER)) {
1038         if(!doc->event_vector) {
1039             doc->event_vector = heap_alloc_zero(EVENTID_LAST*sizeof(BOOL));
1040             if(!doc->event_vector)
1041                 return E_OUTOFMEMORY;
1042         }
1043
1044         if(!doc->event_vector[eid]) {
1045             doc->event_vector[eid] = TRUE;
1046             add_nsevent_listener(doc, event_info[eid].name);
1047         }
1048     }
1049
1050     return S_OK;
1051 }
1052
1053 HRESULT set_event_handler(event_target_t **event_target, HTMLDocumentNode *doc, eventid_t eid, VARIANT *var)
1054 {
1055     switch(V_VT(var)) {
1056     case VT_NULL:
1057         if(*event_target && (*event_target)->event_table[eid] && (*event_target)->event_table[eid]->handler_prop) {
1058             IDispatch_Release((*event_target)->event_table[eid]->handler_prop);
1059             (*event_target)->event_table[eid]->handler_prop = NULL;
1060         }
1061         break;
1062
1063     case VT_DISPATCH:
1064         return set_event_handler_disp(event_target, doc, eid, V_DISPATCH(var));
1065
1066     default:
1067         FIXME("not supported vt=%d\n", V_VT(var));
1068         return E_NOTIMPL;
1069     }
1070
1071     return S_OK;
1072 }
1073
1074 HRESULT get_event_handler(event_target_t **event_target, eventid_t eid, VARIANT *var)
1075 {
1076     if(*event_target && (*event_target)->event_table[eid] && (*event_target)->event_table[eid]->handler_prop) {
1077         V_VT(var) = VT_DISPATCH;
1078         V_DISPATCH(var) = (*event_target)->event_table[eid]->handler_prop;
1079         IDispatch_AddRef(V_DISPATCH(var));
1080     }else {
1081         V_VT(var) = VT_NULL;
1082     }
1083
1084     return S_OK;
1085 }
1086
1087 HRESULT attach_event(event_target_t **event_target_ptr, HTMLDocument *doc, BSTR name, IDispatch *disp, VARIANT_BOOL *res)
1088 {
1089     event_target_t *event_target;
1090     eventid_t eid;
1091     DWORD i = 0;
1092
1093     eid = attr_to_eid(name);
1094     if(eid == EVENTID_LAST) {
1095         WARN("Unknown event\n");
1096         *res = VARIANT_TRUE;
1097         return S_OK;
1098     }
1099
1100     event_target = get_event_target(event_target_ptr);
1101     if(!event_target)
1102         return E_OUTOFMEMORY;
1103
1104     if(event_target->event_table[eid]) {
1105         while(i < event_target->event_table[eid]->handler_cnt && event_target->event_table[eid]->handlers[i])
1106             i++;
1107         if(i == event_target->event_table[eid]->handler_cnt && !alloc_handler_vector(event_target, eid, i+1))
1108             return E_OUTOFMEMORY;
1109     }else if(!alloc_handler_vector(event_target, eid, i+1)) {
1110         return E_OUTOFMEMORY;
1111     }
1112
1113     IDispatch_AddRef(disp);
1114     event_target->event_table[eid]->handlers[i] = disp;
1115
1116     *res = VARIANT_TRUE;
1117     return S_OK;
1118 }
1119
1120 void check_event_attr(HTMLDocumentNode *doc, nsIDOMElement *nselem)
1121 {
1122     const PRUnichar *attr_value;
1123     nsAString attr_name_str, attr_value_str;
1124     IDispatch *disp;
1125     HTMLDOMNode *node;
1126     int i;
1127     nsresult nsres;
1128
1129     nsAString_Init(&attr_value_str, NULL);
1130     nsAString_Init(&attr_name_str, NULL);
1131
1132     for(i=0; i < EVENTID_LAST; i++) {
1133         nsAString_SetData(&attr_name_str, event_info[i].attr_name);
1134         nsres = nsIDOMElement_GetAttribute(nselem, &attr_name_str, &attr_value_str);
1135         if(NS_SUCCEEDED(nsres)) {
1136             nsAString_GetData(&attr_value_str, &attr_value);
1137             if(!*attr_value)
1138                 continue;
1139
1140             TRACE("%p.%s = %s\n", nselem, debugstr_w(event_info[i].attr_name), debugstr_w(attr_value));
1141
1142             disp = script_parse_event(doc->basedoc.window, attr_value);
1143             if(disp) {
1144                 node = get_node(doc, (nsIDOMNode*)nselem, TRUE);
1145                 set_event_handler_disp(get_node_event_target(node), node->doc, i, disp);
1146                 IDispatch_Release(disp);
1147             }
1148         }
1149     }
1150
1151     nsAString_Finish(&attr_value_str);
1152     nsAString_Finish(&attr_name_str);
1153 }
1154
1155 void release_event_target(event_target_t *event_target)
1156 {
1157     int i, j;
1158
1159     for(i=0; i < EVENTID_LAST; i++) {
1160         if(event_target->event_table[i]) {
1161             if(event_target->event_table[i]->handler_prop)
1162                 IDispatch_Release(event_target->event_table[i]->handler_prop);
1163             for(j=0; j < event_target->event_table[i]->handler_cnt; j++)
1164                 IDispatch_Release(event_target->event_table[i]->handlers[j]);
1165         }
1166     }
1167
1168     heap_free(event_target);
1169 }