mshtml: Call default event handles through event target vtbl.
[wine.git] / dlls / mshtml / htmlevent.c
blobdedd3119e4a7e84cca35c9253618d8528a1b239f
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_BIND_TO_BODY 0x0008
110 #define EVENT_CANCELABLE 0x0010
111 #define EVENT_HASDEFAULTHANDLERS 0x0020
112 #define EVENT_FIXME 0x0040
114 static const event_info_t event_info[] = {
115 {abortW, EVENTT_NONE, DISPID_EVMETH_ONABORT,
116 EVENT_BIND_TO_BODY},
117 {beforeactivateW, EVENTT_NONE, DISPID_EVMETH_ONBEFOREACTIVATE,
118 EVENT_FIXME},
119 {beforeunloadW, EVENTT_NONE, DISPID_EVMETH_ONBEFOREUNLOAD,
120 EVENT_DEFAULTLISTENER},
121 {blurW, EVENTT_HTML, DISPID_EVMETH_ONBLUR,
122 EVENT_DEFAULTLISTENER},
123 {changeW, EVENTT_HTML, DISPID_EVMETH_ONCHANGE,
124 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
125 {clickW, EVENTT_MOUSE, DISPID_EVMETH_ONCLICK,
126 EVENT_DEFAULTLISTENER|EVENT_BUBBLE|EVENT_CANCELABLE|EVENT_HASDEFAULTHANDLERS},
127 {contextmenuW, EVENTT_MOUSE, DISPID_EVMETH_ONCONTEXTMENU,
128 EVENT_BUBBLE|EVENT_CANCELABLE},
129 {dataavailableW, EVENTT_NONE, DISPID_EVMETH_ONDATAAVAILABLE,
130 EVENT_BUBBLE},
131 {dblclickW, EVENTT_MOUSE, DISPID_EVMETH_ONDBLCLICK,
132 EVENT_DEFAULTLISTENER|EVENT_BUBBLE|EVENT_CANCELABLE},
133 {dragW, EVENTT_MOUSE, DISPID_EVMETH_ONDRAG,
134 EVENT_FIXME|EVENT_CANCELABLE},
135 {dragstartW, EVENTT_MOUSE, DISPID_EVMETH_ONDRAGSTART,
136 EVENT_FIXME|EVENT_CANCELABLE},
137 {errorW, EVENTT_NONE, DISPID_EVMETH_ONERROR,
138 EVENT_BIND_TO_BODY},
139 {focusW, EVENTT_HTML, DISPID_EVMETH_ONFOCUS,
140 EVENT_DEFAULTLISTENER},
141 {focusinW, EVENTT_HTML, DISPID_EVMETH_ONFOCUSIN,
142 EVENT_BUBBLE},
143 {focusoutW, EVENTT_HTML, DISPID_EVMETH_ONFOCUSOUT,
144 EVENT_BUBBLE},
145 {helpW, EVENTT_KEY, DISPID_EVMETH_ONHELP,
146 EVENT_BUBBLE|EVENT_CANCELABLE},
147 {keydownW, EVENTT_KEY, DISPID_EVMETH_ONKEYDOWN,
148 EVENT_DEFAULTLISTENER|EVENT_BUBBLE|EVENT_HASDEFAULTHANDLERS},
149 {keypressW, EVENTT_KEY, DISPID_EVMETH_ONKEYPRESS,
150 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
151 {keyupW, EVENTT_KEY, DISPID_EVMETH_ONKEYUP,
152 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
153 {loadW, EVENTT_HTML, DISPID_EVMETH_ONLOAD,
154 EVENT_BIND_TO_BODY},
155 {messageW, EVENTT_NONE, DISPID_EVMETH_ONMESSAGE,
157 {mousedownW, EVENTT_MOUSE, DISPID_EVMETH_ONMOUSEDOWN,
158 EVENT_DEFAULTLISTENER|EVENT_BUBBLE|EVENT_CANCELABLE},
159 {mousemoveW, EVENTT_MOUSE, DISPID_EVMETH_ONMOUSEMOVE,
160 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
161 {mouseoutW, EVENTT_MOUSE, DISPID_EVMETH_ONMOUSEOUT,
162 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
163 {mouseoverW, EVENTT_MOUSE, DISPID_EVMETH_ONMOUSEOVER,
164 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
165 {mouseupW, EVENTT_MOUSE, DISPID_EVMETH_ONMOUSEUP,
166 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
167 {mousewheelW, EVENTT_MOUSE, DISPID_EVMETH_ONMOUSEWHEEL,
168 EVENT_FIXME},
169 {pasteW, EVENTT_NONE, DISPID_EVMETH_ONPASTE,
170 EVENT_CANCELABLE},
171 {readystatechangeW, EVENTT_NONE, DISPID_EVMETH_ONREADYSTATECHANGE,
173 {resizeW, EVENTT_NONE, DISPID_EVMETH_ONRESIZE,
174 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
175 {scrollW, EVENTT_HTML, DISPID_EVMETH_ONSCROLL,
176 EVENT_DEFAULTLISTENER|EVENT_BUBBLE},
177 {selectionchangeW, EVENTT_NONE, DISPID_EVMETH_ONSELECTIONCHANGE,
178 EVENT_FIXME},
179 {selectstartW, EVENTT_MOUSE, DISPID_EVMETH_ONSELECTSTART,
180 EVENT_CANCELABLE},
181 {submitW, EVENTT_HTML, DISPID_EVMETH_ONSUBMIT,
182 EVENT_DEFAULTLISTENER|EVENT_BUBBLE|EVENT_CANCELABLE|EVENT_HASDEFAULTHANDLERS},
183 {unloadW, EVENTT_HTML, DISPID_EVMETH_ONUNLOAD,
184 EVENT_FIXME}
187 static BOOL use_event_quirks(EventTarget*);
189 eventid_t str_to_eid(LPCWSTR str)
191 int i;
193 for(i=0; i < sizeof(event_info)/sizeof(event_info[0]); i++) {
194 if(!strcmpW(event_info[i].name, str))
195 return i;
198 ERR("unknown type %s\n", debugstr_w(str));
199 return EVENTID_LAST;
202 static eventid_t attr_to_eid(const WCHAR *str)
204 int i;
206 if((str[0] != 'o' && str[0] != 'O') || (str[1] != 'n' && str[1] != 'N'))
207 return EVENTID_LAST;
209 for(i=0; i < sizeof(event_info)/sizeof(event_info[0]); i++) {
210 if(!strcmpW(event_info[i].name, str+2))
211 return i;
214 return EVENTID_LAST;
217 struct HTMLEventObj {
218 DispatchEx dispex;
219 IHTMLEventObj IHTMLEventObj_iface;
221 LONG ref;
223 HTMLDOMNode *target;
224 const event_info_t *type;
225 nsIDOMEvent *nsevent;
226 VARIANT return_value;
227 BOOL prevent_default;
228 BOOL cancel_bubble;
231 static inline HTMLEventObj *impl_from_IHTMLEventObj(IHTMLEventObj *iface)
233 return CONTAINING_RECORD(iface, HTMLEventObj, IHTMLEventObj_iface);
236 static HRESULT WINAPI HTMLEventObj_QueryInterface(IHTMLEventObj *iface, REFIID riid, void **ppv)
238 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
240 TRACE("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv);
242 if(IsEqualGUID(&IID_IUnknown, riid)) {
243 *ppv = &This->IHTMLEventObj_iface;
244 }else if(IsEqualGUID(&IID_IHTMLEventObj, riid)) {
245 *ppv = &This->IHTMLEventObj_iface;
246 }else if(dispex_query_interface(&This->dispex, riid, ppv)) {
247 return *ppv ? S_OK : E_NOINTERFACE;
248 }else {
249 *ppv = NULL;
250 WARN("(%p)->(%s %p)\n", This, debugstr_mshtml_guid(riid), ppv);
251 return E_NOINTERFACE;
254 IUnknown_AddRef((IUnknown*)*ppv);
255 return S_OK;
258 static ULONG WINAPI HTMLEventObj_AddRef(IHTMLEventObj *iface)
260 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
261 LONG ref = InterlockedIncrement(&This->ref);
263 TRACE("(%p) ref=%d\n", This, ref);
265 return ref;
268 static ULONG WINAPI HTMLEventObj_Release(IHTMLEventObj *iface)
270 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
271 LONG ref = InterlockedDecrement(&This->ref);
273 TRACE("(%p) ref=%d\n", This, ref);
275 if(!ref) {
276 if(This->target)
277 IHTMLDOMNode_Release(&This->target->IHTMLDOMNode_iface);
278 if(This->nsevent)
279 nsIDOMEvent_Release(This->nsevent);
280 release_dispex(&This->dispex);
281 heap_free(This);
284 return ref;
287 static HRESULT WINAPI HTMLEventObj_GetTypeInfoCount(IHTMLEventObj *iface, UINT *pctinfo)
289 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
290 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
293 static HRESULT WINAPI HTMLEventObj_GetTypeInfo(IHTMLEventObj *iface, UINT iTInfo,
294 LCID lcid, ITypeInfo **ppTInfo)
296 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
297 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo);
300 static HRESULT WINAPI HTMLEventObj_GetIDsOfNames(IHTMLEventObj *iface, REFIID riid,
301 LPOLESTR *rgszNames, UINT cNames,
302 LCID lcid, DISPID *rgDispId)
304 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
305 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface, riid, rgszNames, cNames,
306 lcid, rgDispId);
309 static HRESULT WINAPI HTMLEventObj_Invoke(IHTMLEventObj *iface, DISPID dispIdMember,
310 REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
311 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
313 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
314 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface, dispIdMember, riid, lcid,
315 wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
318 static HRESULT WINAPI HTMLEventObj_get_srcElement(IHTMLEventObj *iface, IHTMLElement **p)
320 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
322 TRACE("(%p)->(%p)\n", This, p);
324 *p = NULL;
325 if(This->target)
326 IHTMLDOMNode_QueryInterface(&This->target->IHTMLDOMNode_iface, &IID_IHTMLElement, (void**)p);
327 return S_OK;
330 static HRESULT WINAPI HTMLEventObj_get_altKey(IHTMLEventObj *iface, VARIANT_BOOL *p)
332 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
333 cpp_bool ret = FALSE;
335 TRACE("(%p)->(%p)\n", This, p);
337 if(This->nsevent) {
338 nsIDOMKeyEvent *key_event;
339 nsresult nsres;
341 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
342 if(NS_SUCCEEDED(nsres)) {
343 nsIDOMKeyEvent_GetAltKey(key_event, &ret);
344 nsIDOMKeyEvent_Release(key_event);
345 }else {
346 nsIDOMMouseEvent *mouse_event;
348 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
349 if(NS_SUCCEEDED(nsres)) {
350 nsIDOMMouseEvent_GetAltKey(mouse_event, &ret);
351 nsIDOMMouseEvent_Release(mouse_event);
356 *p = ret ? VARIANT_TRUE : VARIANT_FALSE;
357 return S_OK;
360 static HRESULT WINAPI HTMLEventObj_get_ctrlKey(IHTMLEventObj *iface, VARIANT_BOOL *p)
362 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
363 cpp_bool ret = FALSE;
365 TRACE("(%p)->(%p)\n", This, p);
367 if(This->nsevent) {
368 nsIDOMKeyEvent *key_event;
369 nsresult nsres;
371 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
372 if(NS_SUCCEEDED(nsres)) {
373 nsIDOMKeyEvent_GetCtrlKey(key_event, &ret);
374 nsIDOMKeyEvent_Release(key_event);
375 }else {
376 nsIDOMMouseEvent *mouse_event;
378 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
379 if(NS_SUCCEEDED(nsres)) {
380 nsIDOMMouseEvent_GetCtrlKey(mouse_event, &ret);
381 nsIDOMMouseEvent_Release(mouse_event);
386 *p = ret ? VARIANT_TRUE : VARIANT_FALSE;
387 return S_OK;
390 static HRESULT WINAPI HTMLEventObj_get_shiftKey(IHTMLEventObj *iface, VARIANT_BOOL *p)
392 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
393 cpp_bool ret = FALSE;
395 TRACE("(%p)->(%p)\n", This, p);
397 if(This->nsevent) {
398 nsIDOMKeyEvent *key_event;
399 nsresult nsres;
401 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
402 if(NS_SUCCEEDED(nsres)) {
403 nsIDOMKeyEvent_GetShiftKey(key_event, &ret);
404 nsIDOMKeyEvent_Release(key_event);
405 }else {
406 nsIDOMMouseEvent *mouse_event;
408 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
409 if(NS_SUCCEEDED(nsres)) {
410 nsIDOMMouseEvent_GetShiftKey(mouse_event, &ret);
411 nsIDOMMouseEvent_Release(mouse_event);
416 *p = ret ? VARIANT_TRUE : VARIANT_FALSE;
417 return S_OK;
420 static HRESULT WINAPI HTMLEventObj_put_returnValue(IHTMLEventObj *iface, VARIANT v)
422 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
424 TRACE("(%p)->(%s)\n", This, debugstr_variant(&v));
426 if(V_VT(&v) != VT_BOOL) {
427 FIXME("unsupported value %s\n", debugstr_variant(&v));
428 return DISP_E_BADVARTYPE;
431 This->return_value = v;
432 if(!V_BOOL(&v))
433 This->prevent_default = TRUE;
434 return S_OK;
437 static HRESULT WINAPI HTMLEventObj_get_returnValue(IHTMLEventObj *iface, VARIANT *p)
439 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
441 TRACE("(%p)->(%p)\n", This, p);
443 V_VT(p) = VT_EMPTY;
444 return VariantCopy(p, &This->return_value);
447 static HRESULT WINAPI HTMLEventObj_put_cancelBubble(IHTMLEventObj *iface, VARIANT_BOOL v)
449 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
451 TRACE("(%p)->(%x)\n", This, v);
453 This->cancel_bubble = !!v;
454 return S_OK;
457 static HRESULT WINAPI HTMLEventObj_get_cancelBubble(IHTMLEventObj *iface, VARIANT_BOOL *p)
459 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
461 TRACE("(%p)->(%p)\n", This, p);
463 *p = This->cancel_bubble ? VARIANT_TRUE : VARIANT_FALSE;
464 return S_OK;
467 static HRESULT WINAPI HTMLEventObj_get_fromElement(IHTMLEventObj *iface, IHTMLElement **p)
469 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
471 FIXME("(%p)->(%p)\n", This, p);
473 *p = NULL;
474 return S_OK;
477 static HRESULT WINAPI HTMLEventObj_get_toElement(IHTMLEventObj *iface, IHTMLElement **p)
479 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
481 FIXME("(%p)->(%p)\n", This, p);
483 *p = NULL;
484 return S_OK;
487 static HRESULT WINAPI HTMLEventObj_put_keyCode(IHTMLEventObj *iface, LONG v)
489 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
490 FIXME("(%p)->(%d)\n", This, v);
491 return E_NOTIMPL;
494 static HRESULT WINAPI HTMLEventObj_get_keyCode(IHTMLEventObj *iface, LONG *p)
496 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
497 UINT32 key_code = 0;
499 TRACE("(%p)->(%p)\n", This, p);
501 if(This->nsevent) {
502 nsIDOMKeyEvent *key_event;
503 nsresult nsres;
505 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMKeyEvent, (void**)&key_event);
506 if(NS_SUCCEEDED(nsres)) {
507 nsIDOMKeyEvent_GetKeyCode(key_event, &key_code);
508 nsIDOMKeyEvent_Release(key_event);
512 *p = key_code;
513 return S_OK;
516 static HRESULT WINAPI HTMLEventObj_get_button(IHTMLEventObj *iface, LONG *p)
518 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
519 INT16 button = 0;
521 TRACE("(%p)->(%p)\n", This, p);
523 if(This->nsevent) {
524 nsIDOMMouseEvent *mouse_event;
525 nsresult nsres;
527 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
528 if(NS_SUCCEEDED(nsres)) {
529 nsIDOMMouseEvent_GetButton(mouse_event, &button);
530 nsIDOMMouseEvent_Release(mouse_event);
534 *p = button;
535 return S_OK;
538 static HRESULT WINAPI HTMLEventObj_get_type(IHTMLEventObj *iface, BSTR *p)
540 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
542 TRACE("(%p)->(%p)\n", This, p);
544 if(!This->type) {
545 *p = NULL;
546 return S_OK;
549 *p = SysAllocString(This->type->name);
550 return *p ? S_OK : E_OUTOFMEMORY;
553 static HRESULT WINAPI HTMLEventObj_get_qualifier(IHTMLEventObj *iface, BSTR *p)
555 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
557 FIXME("(%p)->(%p)\n", This, p);
559 *p = NULL;
560 return S_OK;
563 static HRESULT WINAPI HTMLEventObj_get_reason(IHTMLEventObj *iface, LONG *p)
565 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
567 FIXME("(%p)->(%p)\n", This, p);
569 *p = 0;
570 return S_OK;
573 static HRESULT WINAPI HTMLEventObj_get_x(IHTMLEventObj *iface, LONG *p)
575 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
576 LONG x = 0;
578 TRACE("(%p)->(%p)\n", This, p);
580 if(This->nsevent) {
581 nsIDOMUIEvent *ui_event;
582 nsresult nsres;
584 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMUIEvent, (void**)&ui_event);
585 if(NS_SUCCEEDED(nsres)) {
586 /* NOTE: pageX is not exactly right here. */
587 nsres = nsIDOMUIEvent_GetPageX(ui_event, &x);
588 assert(nsres == NS_OK);
589 nsIDOMUIEvent_Release(ui_event);
593 *p = x;
594 return S_OK;
597 static HRESULT WINAPI HTMLEventObj_get_y(IHTMLEventObj *iface, LONG *p)
599 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
600 LONG y = 0;
602 TRACE("(%p)->(%p)\n", This, p);
604 if(This->nsevent) {
605 nsIDOMUIEvent *ui_event;
606 nsresult nsres;
608 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMUIEvent, (void**)&ui_event);
609 if(NS_SUCCEEDED(nsres)) {
610 /* NOTE: pageY is not exactly right here. */
611 nsres = nsIDOMUIEvent_GetPageY(ui_event, &y);
612 assert(nsres == NS_OK);
613 nsIDOMUIEvent_Release(ui_event);
617 *p = y;
618 return S_OK;
621 static HRESULT WINAPI HTMLEventObj_get_clientX(IHTMLEventObj *iface, LONG *p)
623 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
624 LONG x = 0;
626 TRACE("(%p)->(%p)\n", This, p);
628 if(This->nsevent) {
629 nsIDOMMouseEvent *mouse_event;
630 nsresult nsres;
632 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
633 if(NS_SUCCEEDED(nsres)) {
634 nsIDOMMouseEvent_GetClientX(mouse_event, &x);
635 nsIDOMMouseEvent_Release(mouse_event);
639 *p = x;
640 return S_OK;
643 static HRESULT WINAPI HTMLEventObj_get_clientY(IHTMLEventObj *iface, LONG *p)
645 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
646 LONG y = 0;
648 TRACE("(%p)->(%p)\n", This, p);
650 if(This->nsevent) {
651 nsIDOMMouseEvent *mouse_event;
652 nsresult nsres;
654 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
655 if(NS_SUCCEEDED(nsres)) {
656 nsIDOMMouseEvent_GetClientY(mouse_event, &y);
657 nsIDOMMouseEvent_Release(mouse_event);
661 *p = y;
662 return S_OK;
665 static HRESULT WINAPI HTMLEventObj_get_offsetX(IHTMLEventObj *iface, LONG *p)
667 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
669 FIXME("(%p)->(%p)\n", This, p);
671 *p = 0;
672 return S_OK;
675 static HRESULT WINAPI HTMLEventObj_get_offsetY(IHTMLEventObj *iface, LONG *p)
677 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
679 FIXME("(%p)->(%p)\n", This, p);
681 *p = 0;
682 return S_OK;
685 static HRESULT WINAPI HTMLEventObj_get_screenX(IHTMLEventObj *iface, LONG *p)
687 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
688 LONG x = 0;
690 TRACE("(%p)->(%p)\n", This, p);
692 if(This->nsevent) {
693 nsIDOMMouseEvent *mouse_event;
694 nsresult nsres;
696 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
697 if(NS_SUCCEEDED(nsres)) {
698 nsIDOMMouseEvent_GetScreenX(mouse_event, &x);
699 nsIDOMMouseEvent_Release(mouse_event);
703 *p = x;
704 return S_OK;
707 static HRESULT WINAPI HTMLEventObj_get_screenY(IHTMLEventObj *iface, LONG *p)
709 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
710 LONG y = 0;
712 TRACE("(%p)->(%p)\n", This, p);
714 if(This->nsevent) {
715 nsIDOMMouseEvent *mouse_event;
716 nsresult nsres;
718 nsres = nsIDOMEvent_QueryInterface(This->nsevent, &IID_nsIDOMMouseEvent, (void**)&mouse_event);
719 if(NS_SUCCEEDED(nsres)) {
720 nsIDOMMouseEvent_GetScreenY(mouse_event, &y);
721 nsIDOMMouseEvent_Release(mouse_event);
725 *p = y;
726 return S_OK;
729 static HRESULT WINAPI HTMLEventObj_get_srcFilter(IHTMLEventObj *iface, IDispatch **p)
731 HTMLEventObj *This = impl_from_IHTMLEventObj(iface);
733 FIXME("(%p)->(%p)\n", This, p);
735 *p = NULL;
736 return S_OK;
739 static const IHTMLEventObjVtbl HTMLEventObjVtbl = {
740 HTMLEventObj_QueryInterface,
741 HTMLEventObj_AddRef,
742 HTMLEventObj_Release,
743 HTMLEventObj_GetTypeInfoCount,
744 HTMLEventObj_GetTypeInfo,
745 HTMLEventObj_GetIDsOfNames,
746 HTMLEventObj_Invoke,
747 HTMLEventObj_get_srcElement,
748 HTMLEventObj_get_altKey,
749 HTMLEventObj_get_ctrlKey,
750 HTMLEventObj_get_shiftKey,
751 HTMLEventObj_put_returnValue,
752 HTMLEventObj_get_returnValue,
753 HTMLEventObj_put_cancelBubble,
754 HTMLEventObj_get_cancelBubble,
755 HTMLEventObj_get_fromElement,
756 HTMLEventObj_get_toElement,
757 HTMLEventObj_put_keyCode,
758 HTMLEventObj_get_keyCode,
759 HTMLEventObj_get_button,
760 HTMLEventObj_get_type,
761 HTMLEventObj_get_qualifier,
762 HTMLEventObj_get_reason,
763 HTMLEventObj_get_x,
764 HTMLEventObj_get_y,
765 HTMLEventObj_get_clientX,
766 HTMLEventObj_get_clientY,
767 HTMLEventObj_get_offsetX,
768 HTMLEventObj_get_offsetY,
769 HTMLEventObj_get_screenX,
770 HTMLEventObj_get_screenY,
771 HTMLEventObj_get_srcFilter
774 static inline HTMLEventObj *unsafe_impl_from_IHTMLEventObj(IHTMLEventObj *iface)
776 return iface->lpVtbl == &HTMLEventObjVtbl ? impl_from_IHTMLEventObj(iface) : NULL;
779 static const tid_t HTMLEventObj_iface_tids[] = {
780 IHTMLEventObj_tid,
784 static dispex_static_data_t HTMLEventObj_dispex = {
785 NULL,
786 DispCEventObj_tid,
787 HTMLEventObj_iface_tids
790 static HTMLEventObj *create_event(void)
792 HTMLEventObj *ret;
794 ret = heap_alloc_zero(sizeof(*ret));
795 if(!ret)
796 return NULL;
798 ret->IHTMLEventObj_iface.lpVtbl = &HTMLEventObjVtbl;
799 ret->ref = 1;
801 init_dispex(&ret->dispex, (IUnknown*)&ret->IHTMLEventObj_iface, &HTMLEventObj_dispex);
803 return ret;
806 static HRESULT set_event_info(HTMLEventObj *event, HTMLDOMNode *target, eventid_t eid, nsIDOMEvent *nsevent)
808 event->type = event_info+eid;
809 event->nsevent = nsevent;
811 if(nsevent) {
812 nsIDOMEvent_AddRef(nsevent);
813 }else if(event_types[event_info[eid].type]) {
814 nsAString type_str;
815 nsresult nsres;
817 nsAString_InitDepend(&type_str, event_types[event_info[eid].type]);
818 nsres = nsIDOMHTMLDocument_CreateEvent(target->doc->nsdoc, &type_str, &event->nsevent);
819 nsAString_Finish(&type_str);
820 if(NS_FAILED(nsres)) {
821 ERR("Could not create event: %08x\n", nsres);
822 return E_FAIL;
826 event->target = target;
827 if(target)
828 IHTMLDOMNode_AddRef(&target->IHTMLDOMNode_iface);
829 return S_OK;
832 HRESULT create_event_obj(IHTMLEventObj **ret)
834 HTMLEventObj *event;
836 event = create_event();
837 if(!event)
838 return E_OUTOFMEMORY;
840 *ret = &event->IHTMLEventObj_iface;
841 return S_OK;
844 static handler_vector_t *get_handler_vector(EventTarget *event_target, eventid_t eid, BOOL alloc)
846 const event_target_vtbl_t *vtbl;
847 handler_vector_t *handler_vector;
848 struct wine_rb_entry *entry;
850 entry = wine_rb_get(&event_target->handler_map, (const void*)eid);
851 if(entry)
852 return WINE_RB_ENTRY_VALUE(entry, handler_vector_t, entry);
853 if(!alloc)
854 return NULL;
856 handler_vector = heap_alloc_zero(sizeof(*handler_vector));
857 if(!handler_vector)
858 return NULL;
860 handler_vector->event_id = eid;
861 vtbl = dispex_get_vtbl(&event_target->dispex);
862 if(vtbl->bind_event)
863 vtbl->bind_event(&event_target->dispex, eid);
864 else
865 FIXME("Unsupported event binding on target %p\n", event_target);
867 wine_rb_put(&event_target->handler_map, (const void*)eid, &handler_vector->entry);
868 return handler_vector;
871 static HRESULT call_disp_func(IDispatch *disp, DISPPARAMS *dp, VARIANT *retv)
873 IDispatchEx *dispex;
874 EXCEPINFO ei;
875 HRESULT hres;
877 memset(&ei, 0, sizeof(ei));
879 hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex);
880 if(SUCCEEDED(hres)) {
881 hres = IDispatchEx_InvokeEx(dispex, 0, GetUserDefaultLCID(), DISPATCH_METHOD, dp, retv, &ei, NULL);
882 IDispatchEx_Release(dispex);
883 }else {
884 TRACE("Could not get IDispatchEx interface: %08x\n", hres);
885 hres = IDispatch_Invoke(disp, 0, &IID_NULL, GetUserDefaultLCID(), DISPATCH_METHOD,
886 dp, retv, &ei, NULL);
889 return hres;
892 static HRESULT call_cp_func(IDispatch *disp, DISPID dispid, HTMLEventObj *event_obj, VARIANT *retv)
894 DISPPARAMS dp = {NULL,NULL,0,0};
895 VARIANT event_arg;
896 ULONG argerr;
897 EXCEPINFO ei;
899 if(event_obj) {
900 V_VT(&event_arg) = VT_DISPATCH;
901 V_DISPATCH(&event_arg) = (IDispatch*)&event_obj->IHTMLEventObj_iface;
902 dp.rgvarg = &event_arg;
903 dp.cArgs = 1;
906 memset(&ei, 0, sizeof(ei));
907 return IDispatch_Invoke(disp, dispid, &IID_NULL, 0, DISPATCH_METHOD, &dp, retv, &ei, &argerr);
910 static BOOL is_cp_event(cp_static_data_t *data, DISPID dispid)
912 int min, max, i;
913 HRESULT hres;
915 if(!data)
916 return FALSE;
918 if(!data->ids) {
919 hres = get_dispids(data->tid, &data->id_cnt, &data->ids);
920 if(FAILED(hres))
921 return FALSE;
924 min = 0;
925 max = data->id_cnt-1;
926 while(min <= max) {
927 i = (min+max)/2;
928 if(data->ids[i] == dispid)
929 return TRUE;
931 if(data->ids[i] < dispid)
932 min = i+1;
933 else
934 max = i-1;
937 return FALSE;
940 void call_event_handlers(HTMLEventObj *event_obj, EventTarget *event_target, eventid_t eid)
942 handler_vector_t *handler_vector = get_handler_vector(event_target, eid, FALSE);
943 const BOOL cancelable = event_info[eid].flags & EVENT_CANCELABLE;
944 ConnectionPointContainer *cp_container = NULL;
945 const event_target_vtbl_t *vtbl;
946 VARIANT v;
947 HRESULT hres;
949 if(handler_vector && handler_vector->handler_prop) {
950 DISPID named_arg = DISPID_THIS;
951 VARIANTARG arg;
952 DISPPARAMS dp = {&arg, &named_arg, 1, 1};
954 if(!use_event_quirks(event_target))
955 FIXME("Event argument not supported\n");
957 V_VT(&arg) = VT_DISPATCH;
958 V_DISPATCH(&arg) = (IDispatch*)&event_target->dispex.IDispatchEx_iface;
959 V_VT(&v) = VT_EMPTY;
961 TRACE("%s >>>\n", debugstr_w(event_info[eid].name));
962 hres = call_disp_func(handler_vector->handler_prop, &dp, &v);
963 if(hres == S_OK) {
964 TRACE("%s <<< %s\n", debugstr_w(event_info[eid].name), debugstr_variant(&v));
966 if(cancelable) {
967 if(V_VT(&v) == VT_BOOL) {
968 if(!V_BOOL(&v))
969 event_obj->prevent_default = TRUE;
970 }else if(V_VT(&v) != VT_EMPTY) {
971 FIXME("unhandled result %s\n", debugstr_variant(&v));
974 VariantClear(&v);
975 }else {
976 WARN("%s <<< %08x\n", debugstr_w(event_info[eid].name), hres);
980 if(handler_vector && handler_vector->handler_cnt) {
981 VARIANTARG arg;
982 DISPPARAMS dp = {&arg, NULL, 1, 0};
983 int i;
985 V_VT(&arg) = VT_DISPATCH;
986 V_DISPATCH(&arg) = (IDispatch*)&event_obj->dispex.IDispatchEx_iface;
988 i = handler_vector->handler_cnt;
989 while(i--) {
990 if(handler_vector->handlers[i]) {
991 V_VT(&v) = VT_EMPTY;
993 TRACE("%s [%d] >>>\n", debugstr_w(event_info[eid].name), i);
994 hres = call_disp_func(handler_vector->handlers[i], &dp, &v);
995 if(hres == S_OK) {
996 TRACE("%s [%d] <<<\n", debugstr_w(event_info[eid].name), i);
998 if(cancelable) {
999 if(V_VT(&v) == VT_BOOL) {
1000 if(!V_BOOL(&v))
1001 event_obj->prevent_default = TRUE;
1002 }else if(V_VT(&v) != VT_EMPTY) {
1003 FIXME("unhandled result %s\n", debugstr_variant(&v));
1006 VariantClear(&v);
1007 }else {
1008 WARN("%s [%d] <<< %08x\n", debugstr_w(event_info[eid].name), i, hres);
1014 if((vtbl = dispex_get_vtbl(&event_target->dispex)) && vtbl->get_cp_container)
1015 cp_container = vtbl->get_cp_container(&event_target->dispex);
1016 if(cp_container) {
1017 if(cp_container->cps) {
1018 ConnectionPoint *cp;
1019 unsigned i, j;
1021 for(j=0; cp_container->cp_entries[j].riid; j++) {
1022 cp = cp_container->cps + j;
1023 if(!cp->sinks_size || !is_cp_event(cp->data, event_info[eid].dispid))
1024 continue;
1026 for(i=0; i < cp->sinks_size; i++) {
1027 if(!cp->sinks[i].disp)
1028 continue;
1030 V_VT(&v) = VT_EMPTY;
1032 TRACE("cp %s [%u] >>>\n", debugstr_w(event_info[eid].name), i);
1033 hres = call_cp_func(cp->sinks[i].disp, event_info[eid].dispid,
1034 cp->data->pass_event_arg ? event_obj : NULL, &v);
1035 if(hres == S_OK) {
1036 TRACE("cp %s [%u] <<<\n", debugstr_w(event_info[eid].name), i);
1038 if(cancelable) {
1039 if(V_VT(&v) == VT_BOOL) {
1040 if(!V_BOOL(&v))
1041 event_obj->prevent_default = TRUE;
1042 }else if(V_VT(&v) != VT_EMPTY) {
1043 FIXME("unhandled result %s\n", debugstr_variant(&v));
1046 VariantClear(&v);
1047 }else {
1048 WARN("cp %s [%u] <<< %08x\n", debugstr_w(event_info[eid].name), i, hres);
1053 IConnectionPointContainer_Release(&cp_container->IConnectionPointContainer_iface);
1057 static void fire_event_obj(HTMLDocumentNode *doc, eventid_t eid, HTMLEventObj *event_obj,
1058 HTMLDOMNode *target)
1060 IHTMLEventObj *prev_event;
1061 nsIDOMNode *parent, *nsnode = NULL;
1062 BOOL prevent_default = FALSE;
1063 HTMLInnerWindow *window;
1064 HTMLDOMNode *node;
1065 UINT16 node_type = 0;
1066 nsresult nsres;
1067 HRESULT hres;
1069 TRACE("(%p) %s\n", doc, debugstr_w(event_info[eid].name));
1071 window = doc->window;
1072 if(!window) {
1073 WARN("NULL window\n");
1074 return;
1077 htmldoc_addref(&doc->basedoc);
1079 prev_event = window->event;
1080 window->event = event_obj ? &event_obj->IHTMLEventObj_iface : NULL;
1082 if(target) {
1083 nsIDOMNode_GetNodeType(target->nsnode, &node_type);
1084 nsnode = target->nsnode;
1085 nsIDOMNode_AddRef(nsnode);
1088 switch(node_type) {
1089 case ELEMENT_NODE:
1090 do {
1091 hres = get_node(doc, nsnode, FALSE, &node);
1092 if(SUCCEEDED(hres) && node) {
1093 call_event_handlers(event_obj, &node->event_target, eid);
1094 node_release(node);
1097 if(!(event_info[eid].flags & EVENT_BUBBLE) || (event_obj && event_obj->cancel_bubble))
1098 break;
1100 nsIDOMNode_GetParentNode(nsnode, &parent);
1101 nsIDOMNode_Release(nsnode);
1102 nsnode = parent;
1103 if(!nsnode)
1104 break;
1106 nsIDOMNode_GetNodeType(nsnode, &node_type);
1107 }while(node_type == ELEMENT_NODE);
1109 if(!(event_info[eid].flags & EVENT_BUBBLE) || (event_obj && event_obj->cancel_bubble))
1110 break;
1111 /* fallthrough */
1113 case DOCUMENT_NODE:
1114 call_event_handlers(event_obj, &doc->node.event_target, eid);
1115 if(!(event_info[eid].flags & EVENT_BUBBLE) || (event_obj && event_obj->cancel_bubble))
1116 break;
1117 /* fallthrough */
1119 default: /* window object */
1120 call_event_handlers(event_obj, &doc->window->event_target, eid);
1123 if(nsnode)
1124 nsIDOMNode_Release(nsnode);
1126 if(event_obj && event_obj->prevent_default)
1127 prevent_default = TRUE;
1128 window->event = prev_event;
1130 if(target && !prevent_default && (event_info[eid].flags & EVENT_HASDEFAULTHANDLERS)) {
1131 nsnode = target->nsnode;
1132 nsIDOMNode_AddRef(nsnode);
1134 do {
1135 hres = get_node(doc, nsnode, TRUE, &node);
1136 if(FAILED(hres))
1137 break;
1139 if(node) {
1140 const event_target_vtbl_t *vtbl = dispex_get_vtbl(&node->event_target.dispex);
1141 if(vtbl && vtbl->handle_event_default)
1142 hres = vtbl->handle_event_default(&node->event_target.dispex, eid, event_obj ? event_obj->nsevent : NULL, &prevent_default);
1143 node_release(node);
1144 if(FAILED(hres) || prevent_default || (event_obj && event_obj->cancel_bubble))
1145 break;
1148 nsres = nsIDOMNode_GetParentNode(nsnode, &parent);
1149 if(NS_FAILED(nsres))
1150 break;
1152 nsIDOMNode_Release(nsnode);
1153 nsnode = parent;
1154 } while(nsnode);
1156 if(nsnode)
1157 nsIDOMNode_Release(nsnode);
1160 if(prevent_default && event_obj && event_obj->nsevent) {
1161 TRACE("calling PreventDefault\n");
1162 nsIDOMEvent_PreventDefault(event_obj->nsevent);
1165 htmldoc_release(&doc->basedoc);
1168 void fire_event(HTMLDocumentNode *doc, eventid_t eid, BOOL set_event, HTMLDOMNode *target, nsIDOMEvent *nsevent)
1170 HTMLEventObj *event_obj = NULL;
1171 HRESULT hres;
1173 if(set_event) {
1174 event_obj = create_event();
1175 if(!event_obj)
1176 return;
1178 hres = set_event_info(event_obj, target, eid, nsevent);
1179 if(FAILED(hres)) {
1180 IHTMLEventObj_Release(&event_obj->IHTMLEventObj_iface);
1181 return;
1185 fire_event_obj(doc, eid, event_obj, target);
1187 if(event_obj)
1188 IHTMLEventObj_Release(&event_obj->IHTMLEventObj_iface);
1191 HRESULT dispatch_event(HTMLDOMNode *node, const WCHAR *event_name, VARIANT *event_var, VARIANT_BOOL *cancelled)
1193 HTMLEventObj *event_obj = NULL;
1194 eventid_t eid;
1195 HRESULT hres;
1197 eid = attr_to_eid(event_name);
1198 if(eid == EVENTID_LAST) {
1199 WARN("unknown event %s\n", debugstr_w(event_name));
1200 return E_INVALIDARG;
1203 if(event_var && V_VT(event_var) != VT_EMPTY && V_VT(event_var) != VT_ERROR) {
1204 if(V_VT(event_var) != VT_DISPATCH) {
1205 FIXME("event_var %s not supported\n", debugstr_variant(event_var));
1206 return E_NOTIMPL;
1209 if(V_DISPATCH(event_var)) {
1210 IHTMLEventObj *event_iface;
1212 hres = IDispatch_QueryInterface(V_DISPATCH(event_var), &IID_IHTMLEventObj, (void**)&event_iface);
1213 if(FAILED(hres)) {
1214 FIXME("No IHTMLEventObj iface\n");
1215 return hres;
1218 event_obj = unsafe_impl_from_IHTMLEventObj(event_iface);
1219 if(!event_obj) {
1220 ERR("Not our IHTMLEventObj?\n");
1221 IHTMLEventObj_Release(event_iface);
1222 return E_FAIL;
1227 if(event_obj) {
1228 hres = set_event_info(event_obj, node, eid, NULL);
1229 if(SUCCEEDED(hres))
1230 fire_event_obj(node->doc, eid, event_obj, node);
1232 IHTMLEventObj_Release(&event_obj->IHTMLEventObj_iface);
1233 if(FAILED(hres))
1234 return hres;
1235 }else {
1236 fire_event(node->doc, eid, TRUE, node, NULL);
1239 *cancelled = VARIANT_TRUE; /* FIXME */
1240 return S_OK;
1243 HRESULT ensure_doc_nsevent_handler(HTMLDocumentNode *doc, eventid_t eid)
1245 nsIDOMNode *nsnode = NULL;
1247 TRACE("%s\n", debugstr_w(event_info[eid].name));
1249 if(!doc->nsdoc)
1250 return S_OK;
1252 switch(eid) {
1253 case EVENTID_FOCUSIN:
1254 doc->event_vector[eid] = TRUE;
1255 eid = EVENTID_FOCUS;
1256 break;
1257 case EVENTID_FOCUSOUT:
1258 doc->event_vector[eid] = TRUE;
1259 eid = EVENTID_BLUR;
1260 break;
1261 default:
1262 break;
1265 if(doc->event_vector[eid] || !(event_info[eid].flags & (EVENT_DEFAULTLISTENER|EVENT_BIND_TO_BODY)))
1266 return S_OK;
1268 if(event_info[eid].flags & EVENT_BIND_TO_BODY) {
1269 nsnode = doc->node.nsnode;
1270 nsIDOMNode_AddRef(nsnode);
1273 doc->event_vector[eid] = TRUE;
1274 add_nsevent_listener(doc, nsnode, event_info[eid].name);
1276 if(nsnode)
1277 nsIDOMNode_Release(nsnode);
1278 return S_OK;
1281 void detach_events(HTMLDocumentNode *doc)
1283 if(doc->event_vector) {
1284 int i;
1286 for(i=0; i < EVENTID_LAST; i++) {
1287 if(doc->event_vector[i]) {
1288 detach_nsevent(doc, event_info[i].name);
1289 doc->event_vector[i] = FALSE;
1294 release_nsevents(doc);
1297 static HRESULT get_event_dispex_ref(EventTarget *event_target, eventid_t eid, BOOL alloc, VARIANT **ret)
1299 WCHAR buf[64];
1300 buf[0] = 'o';
1301 buf[1] = 'n';
1302 strcpyW(buf+2, event_info[eid].name);
1303 return dispex_get_dprop_ref(&event_target->dispex, buf, alloc, ret);
1306 static void remove_event_handler(EventTarget *event_target, eventid_t eid)
1308 handler_vector_t *handler_vector;
1309 VARIANT *store;
1310 HRESULT hres;
1312 hres = get_event_dispex_ref(event_target, eid, FALSE, &store);
1313 if(SUCCEEDED(hres))
1314 VariantClear(store);
1316 handler_vector = get_handler_vector(event_target, eid, FALSE);
1317 if(handler_vector && handler_vector->handler_prop) {
1318 IDispatch_Release(handler_vector->handler_prop);
1319 handler_vector->handler_prop = NULL;
1323 static HRESULT set_event_handler_disp(EventTarget *event_target, eventid_t eid, IDispatch *disp)
1325 handler_vector_t *handler_vector;
1327 if(event_info[eid].flags & EVENT_FIXME)
1328 FIXME("unimplemented event %s\n", debugstr_w(event_info[eid].name));
1330 remove_event_handler(event_target, eid);
1331 if(!disp)
1332 return S_OK;
1334 handler_vector = get_handler_vector(event_target, eid, TRUE);
1335 if(!handler_vector)
1336 return E_OUTOFMEMORY;
1338 if(handler_vector->handler_prop)
1339 IDispatch_Release(handler_vector->handler_prop);
1341 handler_vector->handler_prop = disp;
1342 IDispatch_AddRef(disp);
1343 return S_OK;
1346 HRESULT set_event_handler(EventTarget *event_target, eventid_t eid, VARIANT *var)
1348 switch(V_VT(var)) {
1349 case VT_EMPTY:
1350 if(use_event_quirks(event_target)) {
1351 WARN("attempt to set to VT_EMPTY in quirks mode\n");
1352 return E_NOTIMPL;
1354 /* fall through */
1355 case VT_NULL:
1356 remove_event_handler(event_target, eid);
1357 return S_OK;
1359 case VT_DISPATCH:
1360 return set_event_handler_disp(event_target, eid, V_DISPATCH(var));
1362 case VT_BSTR: {
1363 VARIANT *v;
1364 HRESULT hres;
1366 if(!use_event_quirks(event_target))
1367 FIXME("Setting to string %s not supported\n", debugstr_w(V_BSTR(var)));
1370 * Setting event handler to string is a rare case and we don't want to
1371 * complicate nor increase memory of handler_vector_t for that. Instead,
1372 * we store the value in DispatchEx, which can already handle custom
1373 * properties.
1375 remove_event_handler(event_target, eid);
1377 hres = get_event_dispex_ref(event_target, eid, TRUE, &v);
1378 if(FAILED(hres))
1379 return hres;
1381 V_BSTR(v) = SysAllocString(V_BSTR(var));
1382 if(!V_BSTR(v))
1383 return E_OUTOFMEMORY;
1384 V_VT(v) = VT_BSTR;
1385 return S_OK;
1388 default:
1389 FIXME("not handler %s\n", debugstr_variant(var));
1390 return E_NOTIMPL;
1393 return S_OK;
1396 HRESULT get_event_handler(EventTarget *event_target, eventid_t eid, VARIANT *var)
1398 handler_vector_t *handler_vector;
1399 VARIANT *v;
1400 HRESULT hres;
1402 hres = get_event_dispex_ref(event_target, eid, FALSE, &v);
1403 if(SUCCEEDED(hres) && V_VT(v) != VT_EMPTY) {
1404 V_VT(var) = VT_EMPTY;
1405 return VariantCopy(var, v);
1408 handler_vector = get_handler_vector(event_target, eid, FALSE);
1409 if(handler_vector && handler_vector->handler_prop) {
1410 V_VT(var) = VT_DISPATCH;
1411 V_DISPATCH(var) = handler_vector->handler_prop;
1412 IDispatch_AddRef(V_DISPATCH(var));
1413 }else {
1414 V_VT(var) = VT_NULL;
1417 return S_OK;
1420 HRESULT attach_event(EventTarget *event_target, BSTR name, IDispatch *disp, VARIANT_BOOL *res)
1422 handler_vector_t *handler_vector;
1423 eventid_t eid;
1424 DWORD i = 0;
1426 eid = attr_to_eid(name);
1427 if(eid == EVENTID_LAST) {
1428 WARN("Unknown event\n");
1429 *res = VARIANT_TRUE;
1430 return S_OK;
1433 if(event_info[eid].flags & EVENT_FIXME)
1434 FIXME("unimplemented event %s\n", debugstr_w(event_info[eid].name));
1436 handler_vector = get_handler_vector(event_target, eid, TRUE);
1437 if(!handler_vector)
1438 return E_OUTOFMEMORY;
1440 while(i < handler_vector->handler_cnt && handler_vector->handlers[i])
1441 i++;
1442 if(i == handler_vector->handler_cnt) {
1443 if(i)
1444 handler_vector->handlers = heap_realloc_zero(handler_vector->handlers,
1445 (i + 1) * sizeof(*handler_vector->handlers));
1446 else
1447 handler_vector->handlers = heap_alloc_zero(sizeof(*handler_vector->handlers));
1448 if(!handler_vector->handlers)
1449 return E_OUTOFMEMORY;
1450 handler_vector->handler_cnt++;
1453 IDispatch_AddRef(disp);
1454 handler_vector->handlers[i] = disp;
1456 *res = VARIANT_TRUE;
1457 return S_OK;
1460 HRESULT detach_event(EventTarget *event_target, BSTR name, IDispatch *disp)
1462 handler_vector_t *handler_vector;
1463 eventid_t eid;
1464 unsigned i;
1466 eid = attr_to_eid(name);
1467 if(eid == EVENTID_LAST) {
1468 WARN("Unknown event\n");
1469 return S_OK;
1472 handler_vector = get_handler_vector(event_target, eid, FALSE);
1473 if(!handler_vector)
1474 return S_OK;
1476 for(i = 0; i < handler_vector->handler_cnt; i++) {
1477 if(handler_vector->handlers[i] == disp) {
1478 IDispatch_Release(handler_vector->handlers[i]);
1479 handler_vector->handlers[i] = NULL;
1483 return S_OK;
1486 void bind_target_event(HTMLDocumentNode *doc, EventTarget *event_target, const WCHAR *event, IDispatch *disp)
1488 eventid_t eid;
1490 TRACE("(%p %p %s %p)\n", doc, event_target, debugstr_w(event), disp);
1492 eid = attr_to_eid(event);
1493 if(eid == EVENTID_LAST) {
1494 WARN("Unsupported event %s\n", debugstr_w(event));
1495 return;
1498 set_event_handler_disp(event_target, eid, disp);
1501 void update_doc_cp_events(HTMLDocumentNode *doc, cp_static_data_t *cp)
1503 int i;
1505 for(i=0; i < EVENTID_LAST; i++) {
1506 if((event_info[i].flags & EVENT_DEFAULTLISTENER) && is_cp_event(cp, event_info[i].dispid))
1507 ensure_doc_nsevent_handler(doc, i);
1511 void check_event_attr(HTMLDocumentNode *doc, nsIDOMHTMLElement *nselem)
1513 nsIDOMMozNamedAttrMap *attr_map;
1514 const PRUnichar *name, *value;
1515 nsAString name_str, value_str;
1516 HTMLDOMNode *node = NULL;
1517 cpp_bool has_attrs;
1518 nsIDOMAttr *attr;
1519 IDispatch *disp;
1520 UINT32 length, i;
1521 eventid_t eid;
1522 nsresult nsres;
1523 HRESULT hres;
1525 nsres = nsIDOMHTMLElement_HasAttributes(nselem, &has_attrs);
1526 if(NS_FAILED(nsres) || !has_attrs)
1527 return;
1529 nsres = nsIDOMHTMLElement_GetAttributes(nselem, &attr_map);
1530 if(NS_FAILED(nsres))
1531 return;
1533 nsres = nsIDOMMozNamedAttrMap_GetLength(attr_map, &length);
1534 assert(nsres == NS_OK);
1536 nsAString_Init(&name_str, NULL);
1537 nsAString_Init(&value_str, NULL);
1539 for(i = 0; i < length; i++) {
1540 nsres = nsIDOMMozNamedAttrMap_Item(attr_map, i, &attr);
1541 if(NS_FAILED(nsres))
1542 continue;
1544 nsres = nsIDOMAttr_GetName(attr, &name_str);
1545 if(NS_FAILED(nsres)) {
1546 nsIDOMAttr_Release(attr);
1547 continue;
1550 nsAString_GetData(&name_str, &name);
1551 eid = attr_to_eid(name);
1552 if(eid == EVENTID_LAST) {
1553 nsIDOMAttr_Release(attr);
1554 continue;
1557 nsres = nsIDOMAttr_GetValue(attr, &value_str);
1558 nsIDOMAttr_Release(attr);
1559 if(NS_FAILED(nsres))
1560 continue;
1562 nsAString_GetData(&value_str, &value);
1563 if(!*value)
1564 continue;
1566 TRACE("%p.%s = %s\n", nselem, debugstr_w(name), debugstr_w(value));
1568 disp = script_parse_event(doc->window, value);
1569 if(!disp)
1570 continue;
1572 if(!node) {
1573 hres = get_node(doc, (nsIDOMNode*)nselem, TRUE, &node);
1574 if(FAILED(hres)) {
1575 IDispatch_Release(disp);
1576 break;
1580 set_event_handler_disp(get_node_event_prop_target(node, eid), eid, disp);
1581 IDispatch_Release(disp);
1584 if(node)
1585 node_release(node);
1586 nsAString_Finish(&name_str);
1587 nsAString_Finish(&value_str);
1588 nsIDOMMozNamedAttrMap_Release(attr_map);
1591 HRESULT doc_init_events(HTMLDocumentNode *doc)
1593 unsigned i;
1594 HRESULT hres;
1596 doc->event_vector = heap_alloc_zero(EVENTID_LAST*sizeof(BOOL));
1597 if(!doc->event_vector)
1598 return E_OUTOFMEMORY;
1600 init_nsevents(doc);
1602 for(i=0; i < EVENTID_LAST; i++) {
1603 if(event_info[i].flags & EVENT_HASDEFAULTHANDLERS) {
1604 hres = ensure_doc_nsevent_handler(doc, i);
1605 if(FAILED(hres))
1606 return hres;
1610 return S_OK;
1613 static inline EventTarget *impl_from_IEventTarget(IEventTarget *iface)
1615 return CONTAINING_RECORD(iface, EventTarget, IEventTarget_iface);
1618 static HRESULT WINAPI EventTarget_QueryInterface(IEventTarget *iface, REFIID riid, void **ppv)
1620 EventTarget *This = impl_from_IEventTarget(iface);
1621 return IDispatchEx_QueryInterface(&This->dispex.IDispatchEx_iface, riid, ppv);
1624 static ULONG WINAPI EventTarget_AddRef(IEventTarget *iface)
1626 EventTarget *This = impl_from_IEventTarget(iface);
1627 return IDispatchEx_AddRef(&This->dispex.IDispatchEx_iface);
1630 static ULONG WINAPI EventTarget_Release(IEventTarget *iface)
1632 EventTarget *This = impl_from_IEventTarget(iface);
1633 return IDispatchEx_Release(&This->dispex.IDispatchEx_iface);
1636 static HRESULT WINAPI EventTarget_GetTypeInfoCount(IEventTarget *iface, UINT *pctinfo)
1638 EventTarget *This = impl_from_IEventTarget(iface);
1639 return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
1642 static HRESULT WINAPI EventTarget_GetTypeInfo(IEventTarget *iface, UINT iTInfo,
1643 LCID lcid, ITypeInfo **ppTInfo)
1645 EventTarget *This = impl_from_IEventTarget(iface);
1646 return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface, iTInfo, lcid, ppTInfo);
1649 static HRESULT WINAPI EventTarget_GetIDsOfNames(IEventTarget *iface, REFIID riid, LPOLESTR *rgszNames,
1650 UINT cNames, LCID lcid, DISPID *rgDispId)
1652 EventTarget *This = impl_from_IEventTarget(iface);
1653 return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface, riid,
1654 rgszNames, cNames, lcid, rgDispId);
1657 static HRESULT WINAPI EventTarget_Invoke(IEventTarget *iface, DISPID dispIdMember,
1658 REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams,
1659 VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr)
1661 EventTarget *This = impl_from_IEventTarget(iface);
1662 return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface, dispIdMember,
1663 riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1666 static HRESULT WINAPI EventTarget_addEventListener(IEventTarget *iface, BSTR type,
1667 IDispatch *listener, VARIANT_BOOL capture)
1669 EventTarget *This = impl_from_IEventTarget(iface);
1670 FIXME("(%p)->(%s %p %x)\n", This, debugstr_w(type), listener, capture);
1671 return E_NOTIMPL;
1674 static HRESULT WINAPI EventTarget_removeEventListener(IEventTarget *iface, BSTR type,
1675 IDispatch *listener, VARIANT_BOOL capture)
1677 EventTarget *This = impl_from_IEventTarget(iface);
1678 FIXME("(%p)->(%s %p %x)\n", This, debugstr_w(type), listener, capture);
1679 return E_NOTIMPL;
1682 static HRESULT WINAPI EventTarget_dispatchEvent(IEventTarget *iface, IDOMEvent *event, VARIANT_BOOL *result)
1684 EventTarget *This = impl_from_IEventTarget(iface);
1685 FIXME("(%p)->(%p %p)\n", This, event, result);
1686 return E_NOTIMPL;
1689 static const IEventTargetVtbl EventTargetVtbl = {
1690 EventTarget_QueryInterface,
1691 EventTarget_AddRef,
1692 EventTarget_Release,
1693 EventTarget_GetTypeInfoCount,
1694 EventTarget_GetTypeInfo,
1695 EventTarget_GetIDsOfNames,
1696 EventTarget_Invoke,
1697 EventTarget_addEventListener,
1698 EventTarget_removeEventListener,
1699 EventTarget_dispatchEvent
1702 #define DELAY_INIT_VTBL ((const IEventTargetVtbl*)1)
1704 static BOOL use_event_quirks(EventTarget *event_target)
1706 if(event_target->IEventTarget_iface.lpVtbl == DELAY_INIT_VTBL) {
1707 event_target->IEventTarget_iface.lpVtbl =
1708 dispex_compat_mode(&event_target->dispex) >= COMPAT_MODE_IE9
1709 ? &EventTargetVtbl : NULL;
1711 return !event_target->IEventTarget_iface.lpVtbl;
1714 HRESULT EventTarget_QI(EventTarget *event_target, REFIID riid, void **ppv)
1716 if(IsEqualGUID(riid, &IID_IEventTarget)) {
1717 if(use_event_quirks(event_target)) {
1718 WARN("IEventTarget queried, but not supported by in document mode\n");
1719 *ppv = NULL;
1720 return E_NOINTERFACE;
1722 IEventTarget_AddRef(&event_target->IEventTarget_iface);
1723 *ppv = &event_target->IEventTarget_iface;
1724 return S_OK;
1727 if(dispex_query_interface(&event_target->dispex, riid, ppv))
1728 return *ppv ? S_OK : E_NOINTERFACE;
1730 WARN("(%p)->(%s %p)\n", event_target, debugstr_mshtml_guid(riid), ppv);
1731 *ppv = NULL;
1732 return E_NOINTERFACE;
1735 static int event_id_cmp(const void *key, const struct wine_rb_entry *entry)
1737 return (INT_PTR)key - WINE_RB_ENTRY_VALUE(entry, handler_vector_t, entry)->event_id;
1740 void EventTarget_Init(EventTarget *event_target, IUnknown *outer, dispex_static_data_t *dispex_data,
1741 compat_mode_t compat_mode)
1743 init_dispex_with_compat_mode(&event_target->dispex, outer, dispex_data, compat_mode);
1744 wine_rb_init(&event_target->handler_map, event_id_cmp);
1747 * IEventTarget is supported by the object or not depending on compatibility mode.
1748 * We use NULL vtbl for objects in compatibility mode not supporting the interface.
1749 * For targets that don't know compatibility mode at creation time, we set vtbl
1750 * to special DELAY_INIT_VTBL value so that vtbl will be set to proper value
1751 * when it's needed.
1753 if(compat_mode == COMPAT_MODE_QUIRKS && dispex_data->vtbl && dispex_data->vtbl->get_compat_mode)
1754 event_target->IEventTarget_iface.lpVtbl = DELAY_INIT_VTBL;
1755 else if(compat_mode < COMPAT_MODE_IE9)
1756 event_target->IEventTarget_iface.lpVtbl = NULL;
1757 else
1758 event_target->IEventTarget_iface.lpVtbl = &EventTargetVtbl;
1761 void release_event_target(EventTarget *event_target)
1763 handler_vector_t *iter, *iter2;
1764 unsigned i;
1766 WINE_RB_FOR_EACH_ENTRY_DESTRUCTOR(iter, iter2, &event_target->handler_map, handler_vector_t, entry) {
1767 if(iter->handler_prop)
1768 IDispatch_Release(iter->handler_prop);
1769 for(i = 0; i < iter->handler_cnt; i++)
1770 if(iter->handlers[i])
1771 IDispatch_Release(iter->handlers[i]);
1772 heap_free(iter->handlers);
1773 heap_free(iter);